Compare commits

...
2 Commits
Author SHA1 Message Date
Kevin LewiandGitHub a30d5b7fa8 Publishing v0.4.1 (#136) 2024-09-24 01:55:54 -07:00
CynthiaGitHubdaxpeddaKevin Lewidependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8f20115075 v0.4.1 - draft-11 compatible, builds on latest Rust (#135)
* Fix Clippy (#85)

* Update `curve25519-dalek` to 4.0.0-pre.5 (#86)

* Update `curve25519-dalek`

* Improve documentation

* Adding all-features CI test (#87)

* Upgrade `p256` to v0.12 (#90)

* Upgrade `p256` to v0.12

* Upgrade MSRV to 1.60

(cherry picked from commit daa8dc048f)

* Replace `json` with `serde_json` (#92)

(cherry picked from commit c8de51672b)

* Fix Clippy (#96)

(cherry picked from commit 74eaebe446)

* Depend on `ProjectivePoint: ToEncodedPoint` (#95)

(cherry picked from commit 0409db6f40)

* Update `curve25519-dalek` (#94)

(cherry picked from commit 2787151e1d)

* Use explicit crate features (#100)

(cherry picked from commit 5bce3e3206)

* Bump `curve25519-dalek` to v4.0.0-rc.1 (#102)

(cherry picked from commit 8363d26f6f)

* Test P-384 (#84)

(cherry picked from commit 83eb78b232)

* Update RustCrypto dependencies to v0.13 (#106)

(cherry picked from commit 8b895cc631)

* Update curve25519-dalek requirement from =4.0.0-rc.1 to =4.0.0-rc.2 (#108)

Updates the requirements on [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) to permit the latest version.
- [Release notes](https://github.com/dalek-cryptography/curve25519-dalek/releases)
- [Changelog](https://github.com/dalek-cryptography/curve25519-dalek/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dalek-cryptography/curve25519-dalek/compare/4.0.0-rc.1...4.0.0-rc.2)

---
updated-dependencies:
- dependency-name: curve25519-dalek
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
(cherry picked from commit 4bd2cf466e)

* Updating dual-license language (#110)

(cherry picked from commit f79ebf9844)

* Bump `curve25519-dalek` to v4.0.0-rc.3 (#113)

(cherry picked from commit 209b957ae4)

* Bump `curve25519-dalek` to v4 (#116)

(cherry picked from commit 0fdfdfdaee)

* Fixing clippy IntoIterator warnings (#123)

(cherry picked from commit c0162ec8d9)

* Bump actions/checkout from 3 to 4 (#120)

Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
(cherry picked from commit 8da56845b8)

* Updating setup-rust-action (#125)

(cherry picked from commit 59e3fedb21)

* Test P-521 (#127)

(cherry picked from commit 68cc7d3709)

* Fix ambiguous lifetime elision (#131)

(cherry picked from commit 40769f7eca)

* Bump actions/cache from 3 to 4 (#132)

Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
(cherry picked from commit c93884aca3)

* Fixups + cherry-picking changes from #101

* Fix Clippy warnings

* More fixups, tests passing

* Ensure CI runs on branch v0.4

---------

Co-authored-by: daxpedda <[email protected]>
Co-authored-by: Kevin Lewi <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 23:43:55 -07:00
26 changed files with 458 additions and 277 deletions
+5 -4
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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
+4
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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 = []
-12
View File
@@ -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.
+6 -4
View File
@@ -1,4 +1,4 @@
# voprf ![Build Status](https://github.com/novifinancial/voprf/workflows/Rust%20CI/badge.svg) # voprf ![Build Status](https://github.com/facebook/voprf/workflows/Rust%20CI/badge.svg)
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
View File
@@ -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;
+97 -37
View File
@@ -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],
}
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:
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 where
<CS::Hash as OutputSizeUser>::OutputSize: T: Digest,
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{ {
GenericArray::from(STR_VOPRF) fn chain_update_multi(mut self, datas: &[&[u8]]) -> Self {
.concat([mode.to_u8()].into()) for data in datas {
.concat(CS::ID.to_be_bytes().into()) self.update(data)
}
self
}
} }
/////////////////////// ///////////////////////
+5 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
} }
} }
+9 -6
View File
@@ -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[..]);
}; };
} }
+6 -5
View File
@@ -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)
+6 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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('#') {
+73 -14
View File
@@ -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
View File
@@ -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(())
} }
} }