Compare commits
2
Commits
v0.5.0-pre.2
...
v0.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a30d5b7fa8 | ||
|
|
8f20115075 |
+5
-4
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|||||||
+22
-17
@@ -3,8 +3,9 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- v0.4
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, repoened, synchronize]
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
cargo-audit:
|
cargo-audit:
|
||||||
@@ -12,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Cache cargo-audit
|
- name: Cache cargo-audit
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/.crates.toml
|
~/.cargo/.crates.toml
|
||||||
@@ -24,7 +25,7 @@ jobs:
|
|||||||
run: cargo install cargo-audit
|
run: cargo install cargo-audit
|
||||||
|
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run cargo audit
|
- name: Run cargo audit
|
||||||
run: cargo audit -D warnings
|
run: cargo audit -D warnings
|
||||||
@@ -35,8 +36,7 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
backend_feature:
|
backend_feature:
|
||||||
- --features ristretto255-ciphersuite,ristretto255-u64
|
- --features ristretto255-ciphersuite
|
||||||
- --features ristretto255-ciphersuite,ristretto255-u32
|
|
||||||
-
|
-
|
||||||
frontend_feature:
|
frontend_feature:
|
||||||
-
|
-
|
||||||
@@ -44,11 +44,11 @@ jobs:
|
|||||||
- --features serde
|
- --features serde
|
||||||
toolchain:
|
toolchain:
|
||||||
- stable
|
- stable
|
||||||
- 1.57.0
|
- 1.65.0
|
||||||
name: test
|
name: test
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install ${{ matrix.toolchain }} toolchain
|
- name: Install ${{ matrix.toolchain }} toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@@ -75,6 +75,12 @@ jobs:
|
|||||||
command: test
|
command: test
|
||||||
args: --no-default-features ${{ matrix.frontend_feature }},std ${{ 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:
|
build-no-std:
|
||||||
name: Build with no-std on ${{ matrix.target }}
|
name: Build with no-std on ${{ matrix.target }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -88,15 +94,14 @@ jobs:
|
|||||||
- thumbv6m-none-eabi
|
- thumbv6m-none-eabi
|
||||||
backend_feature:
|
backend_feature:
|
||||||
-
|
-
|
||||||
- --features ristretto255-ciphersuite,ristretto255-u64
|
- --features ristretto255-ciphersuite
|
||||||
- --features ristretto255-ciphersuite,ristretto255-u32
|
|
||||||
frontend_feature:
|
frontend_feature:
|
||||||
-
|
-
|
||||||
- --features danger
|
- --features danger
|
||||||
- --features serde
|
- --features serde
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v2
|
||||||
- run: rustup target add ${{ matrix.target }}
|
- run: rustup target add ${{ matrix.target }}
|
||||||
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
|
||||||
|
|
||||||
@@ -106,7 +111,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install stable toolchain
|
- name: Install stable toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@@ -120,7 +125,7 @@ jobs:
|
|||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --all-targets -- -D warnings
|
args: --all-features --all-targets -- -D warnings
|
||||||
|
|
||||||
- name: Run cargo doc
|
- name: Run cargo doc
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
@@ -136,7 +141,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install nightly toolchain
|
- name: Install nightly toolchain
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
@@ -157,7 +162,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Cache
|
- name: Cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/.crates.toml
|
~/.cargo/.crates.toml
|
||||||
@@ -166,10 +171,10 @@ jobs:
|
|||||||
key: taplo
|
key: taplo
|
||||||
|
|
||||||
- name: Install Taplo
|
- name: Install Taplo
|
||||||
run: cargo install taplo-cli
|
run: cargo install taplo-cli --locked
|
||||||
|
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run Taplo
|
- name: Run Taplo
|
||||||
run: taplo fmt --check
|
run: taplo fmt --check
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
|
rust: [stable]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: hecrj/setup-rust-action@v1
|
- uses: hecrj/setup-rust-action@v2
|
||||||
with:
|
with:
|
||||||
rust-version: ${{ matrix.rust }}
|
rust-version: ${{ matrix.rust }}
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|||||||
@@ -8,3 +8,7 @@ Cargo.lock
|
|||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# Editors
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.4.1 (September 24, 2024)
|
||||||
|
* Backport all non-protocol-breaking changes from versions 0.5+
|
||||||
|
* Fixes Rust 1.81+ compatibility, compatible with 0.4.0 (draft 11), incompatible with 0.5+ (final RFC)
|
||||||
|
* Updated dependencies
|
||||||
|
|
||||||
## 0.4.0 (September 15, 2022)
|
## 0.4.0 (September 15, 2022)
|
||||||
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
|
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
|
||||||
the addition of the POPRF mode
|
the addition of the POPRF mode
|
||||||
|
|||||||
+2
-1
@@ -27,4 +27,5 @@ outlined on that page and do not file a public issue.
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
By contributing to voprf, you agree that your contributions will be
|
By contributing to voprf, 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.
|
||||||
|
|||||||
+27
-20
@@ -7,56 +7,63 @@ keywords = ["oprf"]
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "voprf"
|
name = "voprf"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/novifinancial/voprf/"
|
repository = "https://github.com/facebook/voprf/"
|
||||||
rust-version = "1.57"
|
rust-version = "1.65"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
alloc = []
|
alloc = []
|
||||||
danger = []
|
danger = []
|
||||||
default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde"]
|
default = ["ristretto255-ciphersuite", "dep:serde"]
|
||||||
ristretto255 = ["curve25519-dalek", "generic-array/more_lengths"]
|
ristretto255 = ["dep:curve25519-dalek", "generic-array/more_lengths"]
|
||||||
ristretto255-ciphersuite = ["ristretto255", "sha2"]
|
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
|
||||||
ristretto255-fiat-u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"]
|
serde = ["generic-array/serde", "dep:serde"]
|
||||||
ristretto255-fiat-u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"]
|
|
||||||
ristretto255-simd = ["curve25519-dalek/simd_backend", "ristretto255"]
|
|
||||||
ristretto255-u32 = ["curve25519-dalek/u32_backend", "ristretto255"]
|
|
||||||
ristretto255-u64 = ["curve25519-dalek/u64_backend", "ristretto255"]
|
|
||||||
serde = ["generic-array/serde", "serde_"]
|
|
||||||
std = ["alloc"]
|
std = ["alloc"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
curve25519-dalek = { version = "=4.0.0-pre.1", default-features = false, optional = true }
|
curve25519-dalek = { version = "4", default-features = false, features = [
|
||||||
|
"rand_core",
|
||||||
|
"zeroize",
|
||||||
|
], optional = true }
|
||||||
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
derive-where = { version = "1", features = ["zeroize-on-drop"] }
|
||||||
digest = "0.10"
|
digest = "0.10"
|
||||||
displaydoc = { version = "0.2", default-features = false }
|
displaydoc = { version = "0.2", default-features = false }
|
||||||
elliptic-curve = { version = "0.12", features = [
|
elliptic-curve = { version = "0.13", features = [
|
||||||
"hash2curve",
|
"hash2curve",
|
||||||
"sec1",
|
"sec1",
|
||||||
"voprf",
|
"voprf",
|
||||||
] }
|
] }
|
||||||
generic-array = "0.14"
|
generic-array = "0.14"
|
||||||
rand_core = { version = "0.6", default-features = false }
|
rand_core = { version = "0.6", default-features = false }
|
||||||
serde_ = { version = "1", package = "serde", default-features = false, features = [
|
serde = { version = "1", default-features = false, features = [
|
||||||
"derive",
|
"derive",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
sha2 = { version = "0.10", default-features = false, optional = true }
|
sha2 = { version = "0.10", default-features = false, optional = true }
|
||||||
subtle = { version = "2.3", default-features = false }
|
subtle = { version = "2.6", default-features = false }
|
||||||
zeroize = { version = "1.5", default-features = false }
|
zeroize = { version = "1.8", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
generic-array = { version = "0.14", features = ["more_lengths"] }
|
generic-array = { version = "0.14", features = ["more_lengths"] }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
json = "0.12"
|
p256 = { version = "0.13", default-features = false, features = [
|
||||||
p256 = { version = "0.11", 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",
|
"hash2curve",
|
||||||
"voprf",
|
"voprf",
|
||||||
] }
|
] }
|
||||||
proptest = "1"
|
proptest = "1"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
serde_json = "1"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["danger", "std"]
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
targets = []
|
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.
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# voprf 
|
# voprf 
|
||||||
An implementation of a (verifiable) oblivious pseudorandom function (VOPRF)
|
An implementation of a (verifiable) oblivious pseudorandom function (VOPRF)
|
||||||
|
|
||||||
A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation.
|
A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation.
|
||||||
@@ -16,12 +16,12 @@ Installation
|
|||||||
Add the following line to the dependencies of your `Cargo.toml`:
|
Add the following line to the dependencies of your `Cargo.toml`:
|
||||||
|
|
||||||
```
|
```
|
||||||
voprf = "0.4"
|
voprf = "0.4.1"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Minimum Supported Rust Version
|
### Minimum Supported Rust Version
|
||||||
|
|
||||||
Rust **1.57** or higher.
|
Rust **1.65** or higher.
|
||||||
|
|
||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
@@ -32,4 +32,6 @@ To learn more about contributing to this project, [see this document](./CONTRIBU
|
|||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
This project is [licensed](./LICENSE) under either Apache 2.0 or MIT, at your option.
|
This project is dual-licensed under either the [MIT license](./LICENSE-MIT)
|
||||||
|
or the [Apache License, Version 2.0](./LICENSE-APACHE).
|
||||||
|
You may select, at your option, one of the above-listed licenses.
|
||||||
|
|||||||
+10
-9
@@ -1,14 +1,15 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
|
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::{Digest, OutputSizeUser};
|
use digest::{FixedOutput, HashMarker, OutputSizeUser};
|
||||||
use elliptic_curve::VoprfParameters;
|
use elliptic_curve::VoprfParameters;
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ where
|
|||||||
{
|
{
|
||||||
/// The ciphersuite identifier as dictated by
|
/// The ciphersuite identifier as dictated by
|
||||||
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
||||||
const ID: u16;
|
const ID: &'static str;
|
||||||
|
|
||||||
/// A finite cyclic group along with a point representation that allows some
|
/// A finite cyclic group along with a point representation that allows some
|
||||||
/// customization on how to hash an input to a curve point. See [`Group`].
|
/// customization on how to hash an input to a curve point. See [`Group`].
|
||||||
@@ -30,17 +31,17 @@ where
|
|||||||
|
|
||||||
/// The main hash function to use (for HKDF computations and hashing
|
/// The main hash function to use (for HKDF computations and hashing
|
||||||
/// transcripts).
|
/// transcripts).
|
||||||
type Hash: BlockSizeUser + Digest;
|
type Hash: BlockSizeUser + Default + FixedOutput + HashMarker;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: VoprfParameters> CipherSuite for T
|
impl<T: VoprfParameters> CipherSuite for T
|
||||||
where
|
where
|
||||||
T: Group,
|
T: Group,
|
||||||
T::Hash: BlockSizeUser + Digest,
|
T::Hash: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
<T::Hash as OutputSizeUser>::OutputSize:
|
<T::Hash as OutputSizeUser>::OutputSize:
|
||||||
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
|
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
|
||||||
{
|
{
|
||||||
const ID: u16 = T::ID;
|
const ID: &'static str = T::ID;
|
||||||
|
|
||||||
type Group = T;
|
type Group = T;
|
||||||
|
|
||||||
|
|||||||
+96
-36
@@ -1,19 +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
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Common functionality between multiple OPRF modes.
|
//! Common functionality between multiple OPRF modes.
|
||||||
|
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
|
use core::ops::Add;
|
||||||
|
|
||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::{Digest, Output, OutputSizeUser};
|
use digest::{Digest, Output, OutputSizeUser};
|
||||||
use generic_array::sequence::Concat;
|
use generic_array::sequence::Concat;
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U11, U2, U256};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U2, U256, U9};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
use subtle::ConstantTimeEq;
|
use subtle::ConstantTimeEq;
|
||||||
@@ -72,7 +74,7 @@ impl Mode {
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct BlindedElement<CS: CipherSuite>(
|
pub struct BlindedElement<CS: CipherSuite>(
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
@@ -89,7 +91,7 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct EvaluationElement<CS: CipherSuite>(
|
pub struct EvaluationElement<CS: CipherSuite>(
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
|
||||||
@@ -106,7 +108,7 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>)
|
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>)
|
||||||
where
|
where
|
||||||
@@ -120,7 +122,7 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct Proof<CS: CipherSuite>
|
pub struct Proof<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -145,8 +147,8 @@ pub(crate) fn generate_proof<CS: CipherSuite, R: RngCore + CryptoRng>(
|
|||||||
k: <CS::Group as Group>::Scalar,
|
k: <CS::Group as Group>::Scalar,
|
||||||
a: <CS::Group as Group>::Elem,
|
a: <CS::Group as Group>::Elem,
|
||||||
b: <CS::Group as Group>::Elem,
|
b: <CS::Group as Group>::Elem,
|
||||||
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<Proof<CS>>
|
) -> Result<Proof<CS>>
|
||||||
where
|
where
|
||||||
@@ -194,9 +196,9 @@ where
|
|||||||
&STR_CHALLENGE,
|
&STR_CHALLENGE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
|
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||||
let s_scalar = r - &(c_scalar * &k);
|
let s_scalar = r - &(c_scalar * &k);
|
||||||
|
|
||||||
Ok(Proof { c_scalar, s_scalar })
|
Ok(Proof { c_scalar, s_scalar })
|
||||||
@@ -207,8 +209,8 @@ where
|
|||||||
pub(crate) fn verify_proof<CS: CipherSuite>(
|
pub(crate) fn verify_proof<CS: CipherSuite>(
|
||||||
a: <CS::Group as Group>::Elem,
|
a: <CS::Group as Group>::Elem,
|
||||||
b: <CS::Group as Group>::Elem,
|
b: <CS::Group as Group>::Elem,
|
||||||
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
|
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
|
||||||
proof: &Proof<CS>,
|
proof: &Proof<CS>,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
@@ -254,9 +256,9 @@ where
|
|||||||
&STR_CHALLENGE,
|
&STR_CHALLENGE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
|
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
match c.ct_eq(&proof.c_scalar).into() {
|
match c.ct_eq(&proof.c_scalar).into() {
|
||||||
true => Ok(()),
|
true => Ok(()),
|
||||||
@@ -296,16 +298,16 @@ where
|
|||||||
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
|
||||||
|
|
||||||
// seedDST = "Seed-" || contextString
|
// seedDST = "Seed-" || contextString
|
||||||
let seed_dst = GenericArray::from(STR_SEED).concat(create_context_string::<CS>(mode));
|
let seed_dst = Dst::new::<CS, _, _>(STR_SEED, mode);
|
||||||
|
|
||||||
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
// h1Input = I2OSP(len(Bm), 2) || Bm ||
|
||||||
// I2OSP(len(seedDST), 2) || seedDST
|
// I2OSP(len(seedDST), 2) || seedDST
|
||||||
// seed = Hash(h1Input)
|
// seed = Hash(h1Input)
|
||||||
let seed = CS::Hash::new()
|
let seed = CS::Hash::new()
|
||||||
.chain_update(&elem_len)
|
.chain_update(elem_len)
|
||||||
.chain_update(CS::Group::serialize_elem(b))
|
.chain_update(CS::Group::serialize_elem(b))
|
||||||
.chain_update(i2osp_2_array(&seed_dst))
|
.chain_update(seed_dst.i2osp_2())
|
||||||
.chain_update(seed_dst)
|
.chain_update_multi(&seed_dst.as_dst())
|
||||||
.finalize();
|
.finalize();
|
||||||
let seed_len = i2osp_2_array(&seed);
|
let seed_len = i2osp_2_array(&seed);
|
||||||
|
|
||||||
@@ -332,9 +334,9 @@ where
|
|||||||
&STR_COMPOSITE,
|
&STR_COMPOSITE,
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, mode);
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap();
|
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
|
||||||
m = c * &di + &m;
|
m = c * &di + &m;
|
||||||
z = match k_option {
|
z = match k_option {
|
||||||
Some(_) => z,
|
Some(_) => z,
|
||||||
@@ -365,8 +367,7 @@ where
|
|||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||||
{
|
{
|
||||||
let context_string = create_context_string::<CS>(mode);
|
let dst = Dst::new::<CS, _, _>(STR_DERIVE_KEYPAIR, mode);
|
||||||
let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
|
|
||||||
|
|
||||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
|
||||||
|
|
||||||
@@ -376,7 +377,7 @@ where
|
|||||||
// || contextString)
|
// || contextString)
|
||||||
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
|
let sk_s = CS::Group::hash_to_scalar::<CS::Hash>(
|
||||||
&[seed, &info_len, info, &counter.to_be_bytes()],
|
&[seed, &info_len, info, &counter.to_be_bytes()],
|
||||||
&dst,
|
&dst.as_dst(),
|
||||||
)
|
)
|
||||||
.map_err(|_| Error::DeriveKeyPair)?;
|
.map_err(|_| Error::DeriveKeyPair)?;
|
||||||
|
|
||||||
@@ -388,7 +389,12 @@ where
|
|||||||
Err(Error::Protocol)
|
Err(Error::Protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can only fail with [`Error::DeriveKeyPair`] and [`Error::Protocol`].
|
/// Corresponds to DeriveKeyPair() function from the VOPRF specification.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// - [`Error::DeriveKeyPair`] if the `input` and `seed` together are longer
|
||||||
|
/// then `u16::MAX - 3`.
|
||||||
|
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
|
||||||
#[cfg(feature = "danger")]
|
#[cfg(feature = "danger")]
|
||||||
pub fn derive_key<CS: CipherSuite>(
|
pub fn derive_key<CS: CipherSuite>(
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
@@ -450,8 +456,8 @@ where
|
|||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||||
{
|
{
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).map_err(|_| Error::Input)
|
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.
|
/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF.
|
||||||
@@ -492,16 +498,70 @@ where
|
|||||||
.finalize())
|
.finalize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the contextString parameter as defined in
|
pub(crate) struct Dst<L: ArrayLength<u8>> {
|
||||||
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
dst_1: GenericArray<u8, L>,
|
||||||
pub(crate) fn create_context_string<CS: CipherSuite>(mode: Mode) -> GenericArray<u8, U11>
|
dst_2: [u8; 2],
|
||||||
where
|
}
|
||||||
|
|
||||||
|
impl<L: ArrayLength<u8>> Dst<L> {
|
||||||
|
pub(crate) fn new<CS: CipherSuite, T, TL>(par_1: T, mode: Mode) -> Self
|
||||||
|
where
|
||||||
|
T: Into<GenericArray<u8, TL>>,
|
||||||
|
TL: ArrayLength<u8> + Add<U9, Output = L>,
|
||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||||
|
{
|
||||||
|
let par_1 = par_1.into();
|
||||||
|
// Generates the contextString parameter as defined in
|
||||||
|
// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
|
||||||
|
let par_2 = GenericArray::from(STR_VOPRF).concat([mode.to_u8()].into());
|
||||||
|
|
||||||
|
// See <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-4.1>
|
||||||
|
let cs_id_u16: u16 = match CS::ID {
|
||||||
|
"ristretto255-SHA512" => 0x0001,
|
||||||
|
"decaf448-SHAKE256" => 0x0002,
|
||||||
|
"P256-SHA256" => 0x0003,
|
||||||
|
"P384-SHA384" => 0x0004,
|
||||||
|
"P521-SHA512" => 0x0005,
|
||||||
|
_ => panic!("Incompatible ciphersuite: {}", CS::ID),
|
||||||
|
};
|
||||||
|
|
||||||
|
let dst_1 = par_1.concat(par_2);
|
||||||
|
let dst_2 = cs_id_u16.to_be_bytes();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
L::USIZE + 2 <= u16::MAX.into(),
|
||||||
|
"constructed DST longer then {}",
|
||||||
|
u16::MAX
|
||||||
|
);
|
||||||
|
|
||||||
|
Self { dst_1, dst_2 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
|
||||||
|
[&self.dst_1, &self.dst_2]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn i2osp_2(&self) -> [u8; 2] {
|
||||||
|
u16::try_from(L::USIZE + 2).unwrap().to_be_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait DigestExt {
|
||||||
|
fn chain_update_multi(self, data: &[&[u8]]) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DigestExt for T
|
||||||
|
where
|
||||||
|
T: Digest,
|
||||||
{
|
{
|
||||||
GenericArray::from(STR_VOPRF)
|
fn chain_update_multi(mut self, datas: &[&[u8]]) -> Self {
|
||||||
.concat([mode.to_u8()].into())
|
for data in datas {
|
||||||
.concat(CS::ID.to_be_bytes().into())
|
self.update(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
|||||||
+5
-4
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Errors which are produced during an execution of the protocol
|
//! Errors which are produced during an execution of the protocol
|
||||||
|
|
||||||
|
|||||||
+16
-15
@@ -1,17 +1,18 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::Digest;
|
use digest::{FixedOutput, HashMarker};
|
||||||
use elliptic_curve::group::cofactor::CofactorGroup;
|
use elliptic_curve::group::cofactor::CofactorGroup;
|
||||||
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
|
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
|
||||||
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
|
||||||
use elliptic_curve::{
|
use elliptic_curve::{
|
||||||
AffinePoint, Field, FieldSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey,
|
AffinePoint, Field, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey,
|
||||||
};
|
};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
@@ -24,32 +25,32 @@ impl<C> Group for C
|
|||||||
where
|
where
|
||||||
C: GroupDigest,
|
C: GroupDigest,
|
||||||
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
|
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
|
||||||
FieldSize<Self>: ModulusSize,
|
FieldBytesSize<Self>: ModulusSize,
|
||||||
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
|
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
|
||||||
Scalar<Self>: FromOkm,
|
Scalar<Self>: FromOkm,
|
||||||
{
|
{
|
||||||
type Elem = ProjectivePoint<Self>;
|
type Elem = ProjectivePoint<Self>;
|
||||||
|
|
||||||
type ElemLen = <FieldSize<Self> as ModulusSize>::CompressedPointSize;
|
type ElemLen = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize;
|
||||||
|
|
||||||
type Scalar = Scalar<Self>;
|
type Scalar = Scalar<Self>;
|
||||||
|
|
||||||
type ScalarLen = FieldSize<Self>;
|
type ScalarLen = FieldBytesSize<Self>;
|
||||||
|
|
||||||
// Implements the `hash_to_curve()` function from
|
// Implements the `hash_to_curve()` function from
|
||||||
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
|
||||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
|
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||||
{
|
{
|
||||||
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
|
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements the `HashToScalar()` function
|
// Implements the `HashToScalar()` function
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
|
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||||
{
|
{
|
||||||
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
|
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst)
|
||||||
@@ -92,7 +93,7 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn zero_scalar() -> Self::Scalar {
|
fn zero_scalar() -> Self::Scalar {
|
||||||
Scalar::<Self>::zero()
|
Scalar::<Self>::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||||
@@ -100,7 +101,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
fn deserialize_scalar(scalar_bits: &[u8]) -> Result<Self::Scalar> {
|
||||||
SecretKey::<Self>::from_be_bytes(scalar_bits)
|
SecretKey::<Self>::from_slice(scalar_bits)
|
||||||
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
.map(|secret_key| *secret_key.to_nonzero_scalar())
|
||||||
.map_err(|_| Error::Deserialization)
|
.map_err(|_| Error::Deserialization)
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-9
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Defines the Group trait to specify the underlying prime order group
|
//! Defines the Group trait to specify the underlying prime order group
|
||||||
|
|
||||||
@@ -14,7 +15,7 @@ mod ristretto;
|
|||||||
use core::ops::{Add, Mul, Sub};
|
use core::ops::{Add, Mul, Sub};
|
||||||
|
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::Digest;
|
use digest::{FixedOutput, HashMarker};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
|
||||||
use generic_array::{ArrayLength, GenericArray};
|
use generic_array::{ArrayLength, GenericArray};
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
@@ -54,9 +55,9 @@ pub trait Group {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||||
/// then [`u16::MAX`].
|
/// then [`u16::MAX`].
|
||||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
|
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
||||||
|
|
||||||
/// Hashes a slice of pseudo-random bytes to a scalar
|
/// Hashes a slice of pseudo-random bytes to a scalar
|
||||||
@@ -64,9 +65,9 @@ pub trait Group {
|
|||||||
/// # Errors
|
/// # Errors
|
||||||
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
|
||||||
/// then [`u16::MAX`].
|
/// then [`u16::MAX`].
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
|
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
|
||||||
|
|
||||||
/// Get the base point for the group
|
/// Get the base point for the group
|
||||||
|
|||||||
+17
-23
@@ -1,16 +1,17 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
|
||||||
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
|
||||||
use curve25519_dalek::scalar::Scalar;
|
use curve25519_dalek::scalar::Scalar;
|
||||||
use curve25519_dalek::traits::Identity;
|
use curve25519_dalek::traits::Identity;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::Digest;
|
use digest::{FixedOutput, HashMarker};
|
||||||
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
@@ -22,21 +23,17 @@ use crate::{Error, InternalError, Result};
|
|||||||
|
|
||||||
/// [`Group`] implementation for Ristretto255.
|
/// [`Group`] implementation for Ristretto255.
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
// `cfg` here is only needed because of a bug in Rust's crate feature documentation. See: https://github.com/rust-lang/rust/issues/83428
|
|
||||||
#[cfg(feature = "ristretto255")]
|
|
||||||
pub struct Ristretto255;
|
pub struct Ristretto255;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255-ciphersuite")]
|
#[cfg(feature = "ristretto255-ciphersuite")]
|
||||||
impl crate::CipherSuite for Ristretto255 {
|
impl crate::CipherSuite for Ristretto255 {
|
||||||
const ID: u16 = 0x0001;
|
const ID: &'static str = "ristretto255-SHA512";
|
||||||
|
|
||||||
type Group = Ristretto255;
|
type Group = Ristretto255;
|
||||||
|
|
||||||
type Hash = sha2::Sha512;
|
type Hash = sha2::Sha512;
|
||||||
}
|
}
|
||||||
|
|
||||||
// `cfg` here is only needed because of a bug in Rust's crate feature documentation. See: https://github.com/rust-lang/rust/issues/83428
|
|
||||||
#[cfg(feature = "ristretto255")]
|
|
||||||
impl Group for Ristretto255 {
|
impl Group for Ristretto255 {
|
||||||
type Elem = RistrettoPoint;
|
type Elem = RistrettoPoint;
|
||||||
|
|
||||||
@@ -48,9 +45,9 @@ impl Group for Ristretto255 {
|
|||||||
|
|
||||||
// Implements the `hash_to_ristretto255()` function from
|
// Implements the `hash_to_ristretto255()` function from
|
||||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
|
||||||
fn hash_to_curve<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Elem, InternalError>
|
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||||
{
|
{
|
||||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||||
@@ -63,9 +60,9 @@ impl Group for Ristretto255 {
|
|||||||
|
|
||||||
// Implements the `HashToScalar()` function from
|
// Implements the `HashToScalar()` function from
|
||||||
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
|
||||||
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[u8]) -> Result<Self::Scalar, InternalError>
|
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
|
||||||
where
|
where
|
||||||
H: Digest + BlockSizeUser,
|
H: BlockSizeUser + Default + FixedOutput + HashMarker,
|
||||||
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
|
||||||
{
|
{
|
||||||
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
let mut uniform_bytes = GenericArray::<_, U64>::default();
|
||||||
@@ -90,11 +87,8 @@ impl Group for Ristretto255 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem> {
|
||||||
if element_bits.len() != 32 {
|
|
||||||
return Err(Error::Deserialization);
|
|
||||||
}
|
|
||||||
|
|
||||||
CompressedRistretto::from_slice(element_bits)
|
CompressedRistretto::from_slice(element_bits)
|
||||||
|
.map_err(|_| Error::Deserialization)?
|
||||||
.decompress()
|
.decompress()
|
||||||
.filter(|point| point != &RistrettoPoint::identity())
|
.filter(|point| point != &RistrettoPoint::identity())
|
||||||
.ok_or(Error::Deserialization)
|
.ok_or(Error::Deserialization)
|
||||||
@@ -104,7 +98,7 @@ impl Group for Ristretto255 {
|
|||||||
loop {
|
loop {
|
||||||
let scalar = Scalar::random(rng);
|
let scalar = Scalar::random(rng);
|
||||||
|
|
||||||
if scalar != Scalar::zero() {
|
if scalar != Scalar::ZERO {
|
||||||
break scalar;
|
break scalar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,12 +109,12 @@ impl Group for Ristretto255 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
|
||||||
scalar.ct_eq(&Scalar::zero())
|
scalar.ct_eq(&Scalar::ZERO)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn zero_scalar() -> Self::Scalar {
|
fn zero_scalar() -> Self::Scalar {
|
||||||
Scalar::zero()
|
Scalar::ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
|
||||||
@@ -131,8 +125,8 @@ impl Group for Ristretto255 {
|
|||||||
scalar_bits
|
scalar_bits
|
||||||
.try_into()
|
.try_into()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(Scalar::from_canonical_bytes)
|
.and_then(|bytes| Scalar::from_canonical_bytes(bytes).into())
|
||||||
.filter(|scalar| scalar != &Scalar::zero())
|
.filter(|scalar| scalar != &Scalar::ZERO)
|
||||||
.ok_or(Error::Deserialization)
|
.ok_or(Error::Deserialization)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-4
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Includes a series of tests for the group implementations
|
//! Includes a series of tests for the group implementations
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ use crate::{Error, Group, Result};
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_group_properties() -> Result<()> {
|
fn test_group_properties() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
{
|
{
|
||||||
@@ -27,6 +30,12 @@ fn test_group_properties() -> Result<()> {
|
|||||||
test_identity_element_error::<NistP256>()?;
|
test_identity_element_error::<NistP256>()?;
|
||||||
test_zero_scalar_error::<NistP256>()?;
|
test_zero_scalar_error::<NistP256>()?;
|
||||||
|
|
||||||
|
test_identity_element_error::<NistP384>()?;
|
||||||
|
test_zero_scalar_error::<NistP384>()?;
|
||||||
|
|
||||||
|
test_identity_element_error::<NistP521>()?;
|
||||||
|
test_zero_scalar_error::<NistP521>()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-21
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
|
||||||
//!
|
//!
|
||||||
@@ -532,24 +533,15 @@
|
|||||||
//! a [`CipherSuite`].
|
//! a [`CipherSuite`].
|
||||||
//!
|
//!
|
||||||
//! - The `ristretto255` feature enables using [`Ristretto255`] as the
|
//! - The `ristretto255` feature enables using [`Ristretto255`] as the
|
||||||
//! underlying group for the [Group] choice. A backend feature, which are
|
//! underlying group for the [Group] choice. To select a specific backend see
|
||||||
//! re-exported from [curve25519-dalek] and allow for selecting the
|
//! the [curve25519-dalek] documentation.
|
||||||
//! corresponding backend for the curve arithmetic used, has to be selected,
|
|
||||||
//! otherwise compilation will fail. The `ristretto255-u64` feature is
|
|
||||||
//! included as the default. Other features are mapped as `ristretto255-u32`,
|
|
||||||
//! `ristretto255-fiat-u64` and `ristretto255-fiat-u32`. Any `ristretto255-*`
|
|
||||||
//! backend feature will enable the `ristretto255` feature.
|
|
||||||
//!
|
|
||||||
//! - The `ristretto255-simd` feature is re-exported from [curve25519-dalek] and
|
|
||||||
//! enables parallel formulas, using either AVX2 or AVX512-IFMA. This will
|
|
||||||
//! automatically enable the `ristretto255-u64` feature and requires Rust
|
|
||||||
//! nightly.
|
|
||||||
//!
|
//!
|
||||||
//! [curve25519-dalek]:
|
//! [curve25519-dalek]:
|
||||||
//! (https://doc.dalek.rs/curve25519_dalek/index.html#backends-and-features)
|
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
|
||||||
|
|
||||||
#![cfg_attr(not(test), deny(unsafe_code))]
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
#![cfg_attr(not(test), deny(unsafe_code))]
|
||||||
#![warn(
|
#![warn(
|
||||||
clippy::cargo,
|
clippy::cargo,
|
||||||
clippy::missing_errors_doc,
|
clippy::missing_errors_doc,
|
||||||
@@ -564,9 +556,6 @@ extern crate alloc;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
extern crate serde_ as serde;
|
|
||||||
|
|
||||||
mod ciphersuite;
|
mod ciphersuite;
|
||||||
mod common;
|
mod common;
|
||||||
mod error;
|
mod error;
|
||||||
|
|||||||
+29
-14
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Contains the main OPRF API
|
//! Contains the main OPRF API
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ use crate::{CipherSuite, Error, Group, Result};
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct OprfClient<CS: CipherSuite>
|
pub struct OprfClient<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -59,7 +60,7 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct OprfServer<CS: CipherSuite>
|
pub struct OprfServer<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -277,7 +278,7 @@ where
|
|||||||
.chain_update(input.as_ref())
|
.chain_update(input.as_ref())
|
||||||
.chain_update(elem_len)
|
.chain_update(elem_len)
|
||||||
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
.chain_update(CS::Group::serialize_elem(unblinded_element))
|
||||||
.chain_update(&STR_FINALIZE)
|
.chain_update(STR_FINALIZE)
|
||||||
.finalize())
|
.finalize())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -291,11 +292,10 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
|
|
||||||
use generic_array::sequence::Concat;
|
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
|
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
|
|
||||||
fn prf<CS: CipherSuite>(
|
fn prf<CS: CipherSuite>(
|
||||||
@@ -308,8 +308,8 @@ mod tests {
|
|||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||||
{
|
{
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
let res = point * &key;
|
let res = point * &key;
|
||||||
|
|
||||||
@@ -348,9 +348,8 @@ mod tests {
|
|||||||
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let dst =
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(Mode::Oprf));
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst.as_dst()).unwrap();
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[&input], &dst).unwrap();
|
|
||||||
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
|
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -427,6 +426,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_functionality() -> Result<()> {
|
fn test_functionality() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
{
|
{
|
||||||
@@ -447,6 +448,20 @@ mod tests {
|
|||||||
zeroize_oprf_client::<NistP256>();
|
zeroize_oprf_client::<NistP256>();
|
||||||
zeroize_oprf_server::<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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+36
-24
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Contains the main POPRF API
|
//! Contains the main POPRF API
|
||||||
|
|
||||||
@@ -14,16 +15,14 @@ use core::iter::{self, Map, Repeat, Zip};
|
|||||||
use derive_where::derive_where;
|
use derive_where::derive_where;
|
||||||
use digest::core_api::BlockSizeUser;
|
use digest::core_api::BlockSizeUser;
|
||||||
use digest::{Digest, Output, OutputSizeUser};
|
use digest::{Digest, Output, OutputSizeUser};
|
||||||
use generic_array::sequence::Concat;
|
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
|
||||||
use generic_array::GenericArray;
|
use generic_array::GenericArray;
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof,
|
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
|
||||||
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof, BlindedElement,
|
server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
|
||||||
EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR,
|
PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
|
||||||
STR_INFO,
|
|
||||||
};
|
};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use crate::serialization::serde::{Element, Scalar};
|
use crate::serialization::serde::{Element, Scalar};
|
||||||
@@ -41,7 +40,7 @@ use crate::{CipherSuite, Error, Group, Result};
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct PoprfClient<CS: CipherSuite>
|
pub struct PoprfClient<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -61,7 +60,7 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct PoprfServer<CS: CipherSuite>
|
pub struct PoprfServer<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -541,7 +540,7 @@ pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
pub struct PoprfPreparedTweak<CS: CipherSuite>(
|
||||||
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
|
||||||
@@ -616,10 +615,9 @@ where
|
|||||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||||
|
|
||||||
let dst =
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
|
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst).unwrap();
|
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
let t = CS::Group::base_elem() * &m;
|
let t = CS::Group::base_elem() * &m;
|
||||||
let tweaked_key = t + &pk;
|
let tweaked_key = t + &pk;
|
||||||
@@ -654,10 +652,9 @@ where
|
|||||||
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
|
||||||
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
let framed_info = [STR_INFO.as_slice(), &info_len, info];
|
||||||
|
|
||||||
let dst =
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf);
|
||||||
GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(Mode::Poprf));
|
|
||||||
// This can't fail, the size of the `input` is known.
|
// This can't fail, the size of the `input` is known.
|
||||||
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst).unwrap();
|
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
let t = sk + &m;
|
let t = sk + &m;
|
||||||
|
|
||||||
@@ -723,7 +720,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(blinds
|
Ok(blinds
|
||||||
.zip(messages.into_iter())
|
.zip(messages)
|
||||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -810,8 +807,8 @@ mod tests {
|
|||||||
{
|
{
|
||||||
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
let t = compute_tweak::<CS>(key, Some(info)).unwrap();
|
||||||
|
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
// evaluatedElement = G.ScalarInverse(t) * blindedElement
|
||||||
let res = point * &CS::Group::invert_scalar(t);
|
let res = point * &CS::Group::invert_scalar(t);
|
||||||
@@ -864,10 +861,9 @@ mod tests {
|
|||||||
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let wrong_pk = {
|
let wrong_pk = {
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
|
||||||
// Choose a group element that is unlikely to be the right public key
|
// Choose a group element that is unlikely to be the right public key
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
|
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||||
};
|
};
|
||||||
let client_finalize_result = client_blind_result.state.finalize(
|
let client_finalize_result = client_blind_result.state.finalize(
|
||||||
input,
|
input,
|
||||||
@@ -970,6 +966,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_functionality() -> Result<()> {
|
fn test_functionality() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
{
|
{
|
||||||
@@ -990,6 +988,20 @@ mod tests {
|
|||||||
zeroize_verifiable_client::<NistP256>();
|
zeroize_verifiable_client::<NistP256>();
|
||||||
zeroize_verifiable_server::<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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Handles the serialization of each of the components used in the VOPRF
|
//! Handles the serialization of each of the components used in the VOPRF
|
||||||
//! protocol
|
//! protocol
|
||||||
@@ -306,11 +307,11 @@ fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait SliceExt {
|
trait SliceExt {
|
||||||
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self>;
|
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> SliceExt for [T] {
|
impl<T> SliceExt for [T] {
|
||||||
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self> {
|
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
|
||||||
if take > self.len() {
|
if take > self.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@@ -389,6 +390,8 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
|
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
|
||||||
|
let _ = $item::<p384::NistP384>::deserialize(&$bytes[..]);
|
||||||
|
let _ = $item::<p521::NistP521>::deserialize(&$bytes[..]);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! The VOPRF test vectors taken from:
|
//! The VOPRF test vectors taken from:
|
||||||
//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/master/draft-irtf-cfrg-voprf.md
|
//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/dff20b461c0de23fcd521116f3d058cfa5b80b90/draft-irtf-cfrg-voprf.md
|
||||||
|
|
||||||
pub(crate) const VECTORS: &str = r#"
|
pub(crate) const VECTORS: &str = r#"
|
||||||
## OPRF(ristretto255, SHA-512)
|
## OPRF(ristretto255, SHA-512)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
@@ -50,7 +51,7 @@ impl RngCore for CycleRng {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
let len = min(self.v.len(), dest.len());
|
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);
|
rotate_left(&mut self.v, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
mod cfrg_vectors;
|
mod cfrg_vectors;
|
||||||
mod mock_rng;
|
mod mock_rng;
|
||||||
|
|||||||
+6
-5
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
use alloc::string::{String, ToString};
|
use alloc::string::{String, ToString};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -96,7 +97,7 @@ fn parse_params(input: &str) -> String {
|
|||||||
let key = iter.next().unwrap().split_whitespace().next().unwrap();
|
let key = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||||
let val = iter.next().unwrap().split_whitespace().next().unwrap();
|
let val = iter.next().unwrap().split_whitespace().next().unwrap();
|
||||||
|
|
||||||
param = format!(" \"{}\": \"{}", key, val);
|
param = format!(" \"{key}\": \"{val}");
|
||||||
} else {
|
} else {
|
||||||
let s = line.trim().to_string();
|
let s = line.trim().to_string();
|
||||||
if s.contains('~') || s.contains('#') {
|
if s.contains('~') || s.contains('#') {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
@@ -14,7 +15,7 @@ use digest::core_api::BlockSizeUser;
|
|||||||
use digest::OutputSizeUser;
|
use digest::OutputSizeUser;
|
||||||
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
|
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
|
||||||
use generic_array::ArrayLength;
|
use generic_array::ArrayLength;
|
||||||
use json::JsonValue;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::tests::mock_rng::CycleRng;
|
use crate::tests::mock_rng::CycleRng;
|
||||||
use crate::tests::parser::*;
|
use crate::tests::parser::*;
|
||||||
@@ -40,7 +41,7 @@ struct VOPRFTestVectorParameters {
|
|||||||
output: Vec<Vec<u8>>,
|
output: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
|
fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
|
||||||
VOPRFTestVectorParameters {
|
VOPRFTestVectorParameters {
|
||||||
seed: decode(values, "Seed"),
|
seed: decode(values, "Seed"),
|
||||||
sksm: decode(values, "skSm"),
|
sksm: decode(values, "skSm"),
|
||||||
@@ -57,18 +58,18 @@ fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode(values: &JsonValue, key: &str) -> Vec<u8> {
|
fn decode(values: &Value, key: &str) -> Vec<u8> {
|
||||||
values[key]
|
values[key]
|
||||||
.as_str()
|
.as_str()
|
||||||
.and_then(|s| hex::decode(&s).ok())
|
.and_then(|s| hex::decode(s).ok())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
fn decode_vec(values: &Value, key: &str) -> Vec<Vec<u8>> {
|
||||||
let s = values[key].as_str().unwrap();
|
let s = values[key].as_str().unwrap();
|
||||||
let res = match s.contains(',') {
|
let res = match s.contains(',') {
|
||||||
true => Some(s.split(',').map(|x| hex::decode(&x).unwrap()).collect()),
|
true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
|
||||||
false => Some(vec![hex::decode(&s).unwrap()]),
|
false => Some(vec![hex::decode(s).unwrap()]),
|
||||||
};
|
};
|
||||||
res.unwrap()
|
res.unwrap()
|
||||||
}
|
}
|
||||||
@@ -76,8 +77,10 @@ fn decode_vec(values: &JsonValue, key: &str) -> Vec<Vec<u8>> {
|
|||||||
macro_rules! json_to_test_vectors {
|
macro_rules! json_to_test_vectors {
|
||||||
( $v:ident, $cs:expr, $mode:expr ) => {
|
( $v:ident, $cs:expr, $mode:expr ) => {
|
||||||
$v[$cs][$mode]
|
$v[$cs][$mode]
|
||||||
.members()
|
.as_array()
|
||||||
.map(|x| populate_test_vectors(&x))
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.map(populate_test_vectors)
|
||||||
.collect::<Vec<VOPRFTestVectorParameters>>()
|
.collect::<Vec<VOPRFTestVectorParameters>>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -85,8 +88,10 @@ macro_rules! json_to_test_vectors {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_vectors() -> Result<()> {
|
fn test_vectors() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
let rfc = json::parse(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
|
let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
|
||||||
.expect("Could not parse json");
|
.expect("Could not parse json");
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
@@ -157,6 +162,60 @@ fn test_vectors() -> Result<()> {
|
|||||||
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
|
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
|
||||||
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
|
||||||
|
|
||||||
|
let p384_oprf_tvs =
|
||||||
|
json_to_test_vectors!(rfc, String::from("P-384, SHA-384"), String::from("OPRF"));
|
||||||
|
assert_ne!(p384_oprf_tvs.len(), 0);
|
||||||
|
test_oprf_seed_to_key::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
test_oprf_blind::<NistP384>(&p384_oprf_tvs)?;
|
||||||
|
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("P-384, SHA-384"), String::from("VOPRF"));
|
||||||
|
assert_ne!(p384_voprf_tvs.len(), 0);
|
||||||
|
test_voprf_seed_to_key::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
test_voprf_blind::<NistP384>(&p384_voprf_tvs)?;
|
||||||
|
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("P-384, SHA-384"), String::from("POPRF"));
|
||||||
|
assert_ne!(p384_poprf_tvs.len(), 0);
|
||||||
|
test_poprf_seed_to_key::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
test_poprf_blind::<NistP384>(&p384_poprf_tvs)?;
|
||||||
|
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("P-521, SHA-512"), String::from("OPRF"));
|
||||||
|
assert_ne!(p521_oprf_tvs.len(), 0);
|
||||||
|
test_oprf_seed_to_key::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
test_oprf_blind::<NistP521>(&p521_oprf_tvs)?;
|
||||||
|
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("P-521, SHA-512"), String::from("VOPRF"));
|
||||||
|
assert_ne!(p521_voprf_tvs.len(), 0);
|
||||||
|
test_voprf_seed_to_key::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
test_voprf_blind::<NistP521>(&p521_voprf_tvs)?;
|
||||||
|
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("P-521, SHA-512"), String::from("POPRF"));
|
||||||
|
assert_ne!(p521_poprf_tvs.len(), 0);
|
||||||
|
test_poprf_seed_to_key::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_blind::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_blind_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_finalize::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
test_poprf_evaluate::<NistP521>(&p521_poprf_tvs)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-19
@@ -1,9 +1,10 @@
|
|||||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
//
|
//
|
||||||
// This source code is licensed under both the MIT license found in the
|
// This source code is dual-licensed under either the MIT license found in the
|
||||||
// LICENSE-MIT file in the root directory of this source tree and the Apache
|
// LICENSE-MIT file in the root directory of this source tree or the Apache
|
||||||
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
|
||||||
// of this source tree.
|
// of this source tree. You may select, at your option, one of the above-listed
|
||||||
|
// licenses.
|
||||||
|
|
||||||
//! Contains the main VOPRF API
|
//! Contains the main VOPRF API
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ use crate::{CipherSuite, Error, Group, Result};
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct VoprfClient<CS: CipherSuite>
|
pub struct VoprfClient<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -59,7 +60,7 @@ where
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(crate = "serde", bound = "")
|
serde(bound = "")
|
||||||
)]
|
)]
|
||||||
pub struct VoprfServer<CS: CipherSuite>
|
pub struct VoprfServer<CS: CipherSuite>
|
||||||
where
|
where
|
||||||
@@ -160,7 +161,7 @@ where
|
|||||||
///
|
///
|
||||||
/// The resulting messages can each fail individually with [`Error::Input`]
|
/// The resulting messages can each fail individually with [`Error::Input`]
|
||||||
/// if the `input` is empty or longer then [`u16::MAX`].
|
/// if the `input` is empty or longer then [`u16::MAX`].
|
||||||
pub fn batch_finalize<'a, I: 'a, II, IC, IM>(
|
pub fn batch_finalize<'a, I, II, IC, IM>(
|
||||||
inputs: &'a II,
|
inputs: &'a II,
|
||||||
clients: &'a IC,
|
clients: &'a IC,
|
||||||
messages: &'a IM,
|
messages: &'a IM,
|
||||||
@@ -169,7 +170,7 @@ where
|
|||||||
) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>>
|
) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>>
|
||||||
where
|
where
|
||||||
CS: 'a,
|
CS: 'a,
|
||||||
I: AsRef<[u8]>,
|
I: 'a + AsRef<[u8]>,
|
||||||
&'a II: 'a + IntoIterator<Item = I>,
|
&'a II: 'a + IntoIterator<Item = I>,
|
||||||
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
|
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
|
||||||
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
|
||||||
@@ -536,7 +537,7 @@ where
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(blinds
|
Ok(blinds
|
||||||
.zip(messages.into_iter())
|
.zip(messages)
|
||||||
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,13 +588,12 @@ mod tests {
|
|||||||
|
|
||||||
use ::alloc::vec;
|
use ::alloc::vec;
|
||||||
use ::alloc::vec::Vec;
|
use ::alloc::vec::Vec;
|
||||||
use generic_array::sequence::Concat;
|
|
||||||
use generic_array::typenum::Sum;
|
use generic_array::typenum::Sum;
|
||||||
use generic_array::ArrayLength;
|
use generic_array::ArrayLength;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
|
use crate::common::{Dst, STR_HASH_TO_GROUP};
|
||||||
use crate::Group;
|
use crate::Group;
|
||||||
|
|
||||||
fn prf<CS: CipherSuite>(
|
fn prf<CS: CipherSuite>(
|
||||||
@@ -605,8 +605,8 @@ mod tests {
|
|||||||
<CS::Hash as OutputSizeUser>::OutputSize:
|
<CS::Hash as OutputSizeUser>::OutputSize:
|
||||||
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
|
||||||
{
|
{
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
|
||||||
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
|
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
|
||||||
|
|
||||||
let res = point * &key;
|
let res = point * &key;
|
||||||
|
|
||||||
@@ -718,10 +718,9 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let messages: Vec<_> = messages.collect();
|
let messages: Vec<_> = messages.collect();
|
||||||
let wrong_pk = {
|
let wrong_pk = {
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
|
||||||
// Choose a group element that is unlikely to be the right public key
|
// Choose a group element that is unlikely to be the right public key
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
|
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||||
};
|
};
|
||||||
let client_finalize_result =
|
let client_finalize_result =
|
||||||
VoprfClient::batch_finalize(&inputs, &client_states, &messages, &proof, wrong_pk);
|
VoprfClient::batch_finalize(&inputs, &client_states, &messages, &proof, wrong_pk);
|
||||||
@@ -739,10 +738,9 @@ mod tests {
|
|||||||
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
|
||||||
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
|
||||||
let wrong_pk = {
|
let wrong_pk = {
|
||||||
let dst = GenericArray::from(STR_HASH_TO_GROUP)
|
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf);
|
||||||
.concat(create_context_string::<CS>(Mode::Oprf));
|
|
||||||
// Choose a group element that is unlikely to be the right public key
|
// Choose a group element that is unlikely to be the right public key
|
||||||
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst).unwrap()
|
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
|
||||||
};
|
};
|
||||||
let client_finalize_result = client_blind_result.state.finalize(
|
let client_finalize_result = client_blind_result.state.finalize(
|
||||||
input,
|
input,
|
||||||
@@ -837,6 +835,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_functionality() -> Result<()> {
|
fn test_functionality() -> Result<()> {
|
||||||
use p256::NistP256;
|
use p256::NistP256;
|
||||||
|
use p384::NistP384;
|
||||||
|
use p521::NistP521;
|
||||||
|
|
||||||
#[cfg(feature = "ristretto255")]
|
#[cfg(feature = "ristretto255")]
|
||||||
{
|
{
|
||||||
@@ -861,6 +861,24 @@ mod tests {
|
|||||||
zeroize_voprf_client::<NistP256>();
|
zeroize_voprf_client::<NistP256>();
|
||||||
zeroize_voprf_server::<NistP256>();
|
zeroize_voprf_server::<NistP256>();
|
||||||
|
|
||||||
|
verifiable_retrieval::<NistP384>();
|
||||||
|
verifiable_batch_retrieval::<NistP384>();
|
||||||
|
verifiable_bad_public_key::<NistP384>();
|
||||||
|
verifiable_batch_bad_public_key::<NistP384>();
|
||||||
|
verifiable_server_evaluate::<NistP384>();
|
||||||
|
|
||||||
|
zeroize_voprf_client::<NistP384>();
|
||||||
|
zeroize_voprf_server::<NistP384>();
|
||||||
|
|
||||||
|
verifiable_retrieval::<NistP521>();
|
||||||
|
verifiable_batch_retrieval::<NistP521>();
|
||||||
|
verifiable_bad_public_key::<NistP521>();
|
||||||
|
verifiable_batch_bad_public_key::<NistP521>();
|
||||||
|
verifiable_server_evaluate::<NistP521>();
|
||||||
|
|
||||||
|
zeroize_voprf_client::<NistP521>();
|
||||||
|
zeroize_voprf_server::<NistP521>();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user