diff --git a/.cargo/license.rs b/.cargo/license.rs
index 3fa8d9f..664968b 100644
--- a/.cargo/license.rs
+++ b/.cargo/license.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 4345a70..b4eaf4b 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -3,8 +3,9 @@ on:
push:
branches:
- main
+ - v0.4
pull_request:
- types: [opened, repoened, synchronize]
+ types: [opened, reopened, synchronize]
jobs:
cargo-audit:
@@ -12,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache cargo-audit
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: |
~/.cargo/.crates.toml
@@ -24,7 +25,7 @@ jobs:
run: cargo install cargo-audit
- name: Checkout sources
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Run cargo audit
run: cargo audit -D warnings
@@ -35,8 +36,7 @@ jobs:
fail-fast: false
matrix:
backend_feature:
- - --features ristretto255-ciphersuite,ristretto255-u64
- - --features ristretto255-ciphersuite,ristretto255-u32
+ - --features ristretto255-ciphersuite
-
frontend_feature:
-
@@ -44,11 +44,11 @@ jobs:
- --features serde
toolchain:
- stable
- - 1.57.0
+ - 1.65.0
name: test
steps:
- name: Checkout sources
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install ${{ matrix.toolchain }} toolchain
uses: actions-rs/toolchain@v1
@@ -75,6 +75,12 @@ jobs:
command: test
args: --no-default-features ${{ matrix.frontend_feature }},std ${{ matrix.backend_feature }}
+ - name: Run cargo test with all features enabled
+ uses: actions-rs/cargo@v1
+ with:
+ command: test
+ args: --all-features
+
build-no-std:
name: Build with no-std on ${{ matrix.target }}
runs-on: ubuntu-latest
@@ -88,15 +94,14 @@ jobs:
- thumbv6m-none-eabi
backend_feature:
-
- - --features ristretto255-ciphersuite,ristretto255-u64
- - --features ristretto255-ciphersuite,ristretto255-u32
+ - --features ristretto255-ciphersuite
frontend_feature:
-
- --features danger
- --features serde
steps:
- - uses: actions/checkout@v3
- - uses: hecrj/setup-rust-action@v1
+ - uses: actions/checkout@v4
+ - uses: hecrj/setup-rust-action@v2
- run: rustup target add ${{ matrix.target }}
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
@@ -106,7 +111,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
@@ -120,7 +125,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: clippy
- args: --all-targets -- -D warnings
+ args: --all-features --all-targets -- -D warnings
- name: Run cargo doc
uses: actions-rs/cargo@v1
@@ -136,7 +141,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install nightly toolchain
uses: actions-rs/toolchain@v1
@@ -157,7 +162,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Cache
- uses: actions/cache@v3
+ uses: actions/cache@v4
with:
path: |
~/.cargo/.crates.toml
@@ -166,10 +171,10 @@ jobs:
key: taplo
- name: Install Taplo
- run: cargo install taplo-cli
+ run: cargo install taplo-cli --locked
- name: Checkout sources
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Run Taplo
run: taplo fmt --check
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 24fdd8c..5ae8001 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -10,9 +10,10 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
+ rust: [stable]
steps:
- - uses: hecrj/setup-rust-action@v1
+ - uses: hecrj/setup-rust-action@v2
with:
rust-version: ${{ matrix.rust }}
- uses: actions/checkout@master
diff --git a/.gitignore b/.gitignore
index 088ba6b..3507e83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,7 @@ Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
+
+# Editors
+.idea
+.vscode
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7e57b8e..8c8ddad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## 0.4.1 (TBD)
+* Backport all non-protocol-breaking changes from versions 0.5+
+ * Fixes Rust 1.81+ compatibility, compatible with 0.4.0 (draft 11), incompatible with 0.5+ (final RFC)
+* Updated dependencies
+
## 0.4.0 (September 15, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
the addition of the POPRF mode
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 906df5e..a2aeae6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,4 +27,5 @@ outlined on that page and do not file a public issue.
## License
By contributing to voprf, you agree that your contributions will be
-licensed under the LICENSE file in the root directory of this source tree.
+licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
+directory of this source tree.
diff --git a/Cargo.toml b/Cargo.toml
index f94a116..bba6601 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,56 +7,63 @@ keywords = ["oprf"]
license = "MIT"
name = "voprf"
readme = "README.md"
-repository = "https://github.com/novifinancial/voprf/"
-rust-version = "1.57"
-version = "0.4.0"
+repository = "https://github.com/facebook/voprf/"
+rust-version = "1.65"
+version = "0.4.1"
[features]
alloc = []
danger = []
-default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde"]
-ristretto255 = ["curve25519-dalek", "generic-array/more_lengths"]
-ristretto255-ciphersuite = ["ristretto255", "sha2"]
-ristretto255-fiat-u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"]
-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_"]
+default = ["ristretto255-ciphersuite", "dep:serde"]
+ristretto255 = ["dep:curve25519-dalek", "generic-array/more_lengths"]
+ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
+serde = ["generic-array/serde", "dep:serde"]
std = ["alloc"]
[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"] }
digest = "0.10"
displaydoc = { version = "0.2", default-features = false }
-elliptic-curve = { version = "0.12", features = [
+elliptic-curve = { version = "0.13", features = [
"hash2curve",
"sec1",
"voprf",
] }
generic-array = "0.14"
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",
], optional = true }
sha2 = { version = "0.10", default-features = false, optional = true }
-subtle = { version = "2.3", default-features = false }
-zeroize = { version = "1.5", default-features = false }
+subtle = { version = "2.6", default-features = false }
+zeroize = { version = "1.8", default-features = false }
[dev-dependencies]
generic-array = { version = "0.14", features = ["more_lengths"] }
hex = "0.4"
-json = "0.12"
-p256 = { version = "0.11", default-features = false, features = [
+p256 = { version = "0.13", default-features = false, features = [
+ "hash2curve",
+ "voprf",
+] }
+p384 = { version = "0.13", default-features = false, features = [
+ "hash2curve",
+ "voprf",
+] }
+p521 = { version = "0.13.3", default-features = false, features = [
"hash2curve",
"voprf",
] }
proptest = "1"
rand = "0.8"
regex = "1"
+serde_json = "1"
sha2 = "0.10"
[package.metadata.docs.rs]
-features = ["danger", "std"]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
targets = []
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 09250ca..0000000
--- a/LICENSE
+++ /dev/null
@@ -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.
diff --git a/README.md b/README.md
index 2751847..4b4ac98 100644
--- a/README.md
+++ b/README.md
@@ -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)
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`:
```
-voprf = "0.4"
+voprf = "0.4.1"
```
### Minimum Supported Rust Version
-Rust **1.57** or higher.
+Rust **1.65** or higher.
Contributors
------------
@@ -32,4 +32,6 @@ To learn more about contributing to this project, [see this document](./CONTRIBU
License
-------
-This project is [licensed](./LICENSE) under either Apache 2.0 or MIT, at your option.
+This project is dual-licensed under either the [MIT license](./LICENSE-MIT)
+or the [Apache License, Version 2.0](./LICENSE-APACHE).
+You may select, at your option, one of the above-listed licenses.
diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs
index 734c9ca..0f080d2 100644
--- a/src/ciphersuite.rs
+++ b/src/ciphersuite.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
use digest::core_api::BlockSizeUser;
-use digest::{Digest, OutputSizeUser};
+use digest::{FixedOutput, HashMarker, OutputSizeUser};
use elliptic_curve::VoprfParameters;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
@@ -22,7 +23,7 @@ where
{
/// The ciphersuite identifier as dictated by
///
- const ID: u16;
+ const ID: &'static str;
/// A finite cyclic group along with a point representation that allows some
/// customization on how to hash an input to a curve point. See [`Group`].
@@ -30,17 +31,17 @@ where
/// The main hash function to use (for HKDF computations and hashing
/// transcripts).
- type Hash: BlockSizeUser + Digest;
+ type Hash: BlockSizeUser + Default + FixedOutput + HashMarker;
}
impl CipherSuite for T
where
T: Group,
- T::Hash: BlockSizeUser + Digest,
+ T::Hash: BlockSizeUser + Default + FixedOutput + HashMarker,
::OutputSize:
IsLess + IsLessOrEqual<::BlockSize>,
{
- const ID: u16 = T::ID;
+ const ID: &'static str = T::ID;
type Group = T;
diff --git a/src/common.rs b/src/common.rs
index 6af1b95..a04056a 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Common functionality between multiple OPRF modes.
use core::convert::TryFrom;
+use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser};
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 rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq;
@@ -72,7 +74,7 @@ impl Mode {
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct BlindedElement(
#[cfg_attr(feature = "serde", serde(with = "Element::"))]
@@ -89,7 +91,7 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct EvaluationElement(
#[cfg_attr(feature = "serde", serde(with = "Element::"))]
@@ -106,7 +108,7 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct PreparedEvaluationElement(pub(crate) EvaluationElement)
where
@@ -120,7 +122,7 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct Proof
where
@@ -145,8 +147,8 @@ pub(crate) fn generate_proof(
k: ::Scalar,
a: ::Elem,
b: ::Elem,
- cs: impl Iterator- ::Elem> + ExactSizeIterator,
- ds: impl Iterator
- ::Elem> + ExactSizeIterator,
+ cs: impl ExactSizeIterator
- ::Elem>,
+ ds: impl ExactSizeIterator
- ::Elem>,
mode: Mode,
) -> Result>
where
@@ -194,9 +196,9 @@ where
&STR_CHALLENGE,
];
- let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(mode));
+ let dst = Dst::new::(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known.
- let c_scalar = CS::Group::hash_to_scalar::(&h2_input, &dst).unwrap();
+ let c_scalar = CS::Group::hash_to_scalar::(&h2_input, &dst.as_dst()).unwrap();
let s_scalar = r - &(c_scalar * &k);
Ok(Proof { c_scalar, s_scalar })
@@ -207,8 +209,8 @@ where
pub(crate) fn verify_proof(
a: ::Elem,
b: ::Elem,
- cs: impl Iterator
- ::Elem> + ExactSizeIterator,
- ds: impl Iterator
- ::Elem> + ExactSizeIterator,
+ cs: impl ExactSizeIterator
- ::Elem>,
+ ds: impl ExactSizeIterator
- ::Elem>,
proof: &Proof,
mode: Mode,
) -> Result<()>
@@ -254,9 +256,9 @@ where
&STR_CHALLENGE,
];
- let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(mode));
+ let dst = Dst::new::(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known.
- let c = CS::Group::hash_to_scalar::(&h2_input, &dst).unwrap();
+ let c = CS::Group::hash_to_scalar::(&h2_input, &dst.as_dst()).unwrap();
match c.ct_eq(&proof.c_scalar).into() {
true => Ok(()),
@@ -296,16 +298,16 @@ where
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
// seedDST = "Seed-" || contextString
- let seed_dst = GenericArray::from(STR_SEED).concat(create_context_string::(mode));
+ let seed_dst = Dst::new::(STR_SEED, mode);
// h1Input = I2OSP(len(Bm), 2) || Bm ||
// I2OSP(len(seedDST), 2) || seedDST
// seed = Hash(h1Input)
let seed = CS::Hash::new()
- .chain_update(&elem_len)
+ .chain_update(elem_len)
.chain_update(CS::Group::serialize_elem(b))
- .chain_update(i2osp_2_array(&seed_dst))
- .chain_update(seed_dst)
+ .chain_update(seed_dst.i2osp_2())
+ .chain_update_multi(&seed_dst.as_dst())
.finalize();
let seed_len = i2osp_2_array(&seed);
@@ -332,9 +334,9 @@ where
&STR_COMPOSITE,
];
- let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(mode));
+ let dst = Dst::new::(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known.
- let di = CS::Group::hash_to_scalar::(&h2_input, &dst).unwrap();
+ let di = CS::Group::hash_to_scalar::(&h2_input, &dst.as_dst()).unwrap();
m = c * &di + &m;
z = match k_option {
Some(_) => z,
@@ -365,8 +367,7 @@ where
::OutputSize:
IsLess + IsLessOrEqual<::BlockSize>,
{
- let context_string = create_context_string::(mode);
- let dst = GenericArray::from(STR_DERIVE_KEYPAIR).concat(context_string);
+ let dst = Dst::new::(STR_DERIVE_KEYPAIR, mode);
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
@@ -376,7 +377,7 @@ where
// || contextString)
let sk_s = CS::Group::hash_to_scalar::(
&[seed, &info_len, info, &counter.to_be_bytes()],
- &dst,
+ &dst.as_dst(),
)
.map_err(|_| Error::DeriveKeyPair)?;
@@ -388,7 +389,12 @@ where
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")]
pub fn derive_key(
seed: &[u8],
@@ -450,8 +456,8 @@ where
::OutputSize:
IsLess + IsLessOrEqual<::BlockSize>,
{
- let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(mode));
- CS::Group::hash_to_curve::(&[input], &dst).map_err(|_| Error::Input)
+ let dst = Dst::new::(STR_HASH_TO_GROUP, mode);
+ CS::Group::hash_to_curve::(&[input], &dst.as_dst()).map_err(|_| Error::Input)
}
/// Internal function that finalizes the hash input for OPRF, VOPRF & POPRF.
@@ -492,16 +498,70 @@ where
.finalize())
}
-/// Generates the contextString parameter as defined in
-///
-pub(crate) fn create_context_string(mode: Mode) -> GenericArray
+pub(crate) struct Dst> {
+ dst_1: GenericArray,
+ dst_2: [u8; 2],
+}
+
+impl> Dst {
+ pub(crate) fn new(par_1: T, mode: Mode) -> Self
+ where
+ T: Into>,
+ TL: ArrayLength + Add,
+ ::OutputSize:
+ IsLess + IsLessOrEqual<::BlockSize>,
+ {
+ let par_1 = par_1.into();
+ // Generates the contextString parameter as defined in
+ //
+ let par_2 = GenericArray::from(STR_VOPRF).concat([mode.to_u8()].into());
+
+ // See
+ 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 DigestExt for T
where
- ::OutputSize:
- IsLess + IsLessOrEqual<::BlockSize>,
+ T: Digest,
{
- GenericArray::from(STR_VOPRF)
- .concat([mode.to_u8()].into())
- .concat(CS::ID.to_be_bytes().into())
+ fn chain_update_multi(mut self, datas: &[&[u8]]) -> Self {
+ for data in datas {
+ self.update(data)
+ }
+
+ self
+ }
}
///////////////////////
diff --git a/src/error.rs b/src/error.rs
index 2819edb..5e794bf 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Errors which are produced during an execution of the protocol
diff --git a/src/group/elliptic_curve.rs b/src/group/elliptic_curve.rs
index edbd958..2ecafda 100644
--- a/src/group/elliptic_curve.rs
+++ b/src/group/elliptic_curve.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
use digest::core_api::BlockSizeUser;
-use digest::Digest;
+use digest::{FixedOutput, HashMarker};
use elliptic_curve::group::cofactor::CofactorGroup;
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::{
- AffinePoint, Field, 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::GenericArray;
@@ -24,32 +25,32 @@ impl Group for C
where
C: GroupDigest,
ProjectivePoint: CofactorGroup + ToEncodedPoint,
- FieldSize: ModulusSize,
+ FieldBytesSize: ModulusSize,
AffinePoint: FromEncodedPoint + ToEncodedPoint,
Scalar: FromOkm,
{
type Elem = ProjectivePoint;
- type ElemLen = as ModulusSize>::CompressedPointSize;
+ type ElemLen = as ModulusSize>::CompressedPointSize;
type Scalar = Scalar;
- type ScalarLen = FieldSize;
+ type ScalarLen = FieldBytesSize;
// Implements the `hash_to_curve()` function from
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
- fn hash_to_curve(input: &[&[u8]], dst: &[u8]) -> Result
+ fn hash_to_curve(input: &[&[u8]], dst: &[&[u8]]) -> Result
where
- H: Digest + BlockSizeUser,
+ H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess + IsLessOrEqual,
{
Self::hash_from_bytes::>(input, dst).map_err(|_| InternalError::Input)
}
// Implements the `HashToScalar()` function
- fn hash_to_scalar(input: &[&[u8]], dst: &[u8]) -> Result
+ fn hash_to_scalar(input: &[&[u8]], dst: &[&[u8]]) -> Result
where
- H: Digest + BlockSizeUser,
+ H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess + IsLessOrEqual,
{
::hash_to_scalar::>(input, dst)
@@ -92,7 +93,7 @@ where
#[cfg(test)]
fn zero_scalar() -> Self::Scalar {
- Scalar::::zero()
+ Scalar::::ZERO
}
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray {
@@ -100,7 +101,7 @@ where
}
fn deserialize_scalar(scalar_bits: &[u8]) -> Result {
- SecretKey::::from_be_bytes(scalar_bits)
+ SecretKey::::from_slice(scalar_bits)
.map(|secret_key| *secret_key.to_nonzero_scalar())
.map_err(|_| Error::Deserialization)
}
diff --git a/src/group/mod.rs b/src/group/mod.rs
index 132b78d..923ec9a 100644
--- a/src/group/mod.rs
+++ b/src/group/mod.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Defines the Group trait to specify the underlying prime order group
@@ -14,7 +15,7 @@ mod ristretto;
use core::ops::{Add, Mul, Sub};
use digest::core_api::BlockSizeUser;
-use digest::Digest;
+use digest::{FixedOutput, HashMarker};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
@@ -54,9 +55,9 @@ pub trait Group {
/// # Errors
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
/// then [`u16::MAX`].
- fn hash_to_curve(input: &[&[u8]], dst: &[u8]) -> Result
+ fn hash_to_curve(input: &[&[u8]], dst: &[&[u8]]) -> Result
where
- H: Digest + BlockSizeUser,
+ H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess + IsLessOrEqual;
/// Hashes a slice of pseudo-random bytes to a scalar
@@ -64,9 +65,9 @@ pub trait Group {
/// # Errors
/// [`Error::Input`](crate::Error::Input) if the `input` is empty or longer
/// then [`u16::MAX`].
- fn hash_to_scalar(input: &[&[u8]], dst: &[u8]) -> Result
+ fn hash_to_scalar(input: &[&[u8]], dst: &[&[u8]]) -> Result
where
- H: Digest + BlockSizeUser,
+ H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess + IsLessOrEqual;
/// Get the base point for the group
diff --git a/src/group/ristretto.rs b/src/group/ristretto.rs
index bf42d19..568e1b7 100644
--- a/src/group/ristretto.rs
+++ b/src/group/ristretto.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::Identity;
use digest::core_api::BlockSizeUser;
-use digest::Digest;
+use digest::{FixedOutput, HashMarker};
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
use generic_array::GenericArray;
@@ -22,21 +23,17 @@ use crate::{Error, InternalError, Result};
/// [`Group`] implementation for Ristretto255.
#[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;
#[cfg(feature = "ristretto255-ciphersuite")]
impl crate::CipherSuite for Ristretto255 {
- const ID: u16 = 0x0001;
+ const ID: &'static str = "ristretto255-SHA512";
type Group = Ristretto255;
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 {
type Elem = RistrettoPoint;
@@ -48,9 +45,9 @@ impl Group for Ristretto255 {
// Implements the `hash_to_ristretto255()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
- fn hash_to_curve(input: &[&[u8]], dst: &[u8]) -> Result
+ fn hash_to_curve(input: &[&[u8]], dst: &[&[u8]]) -> Result
where
- H: Digest + BlockSizeUser,
+ H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess + IsLessOrEqual,
{
let mut uniform_bytes = GenericArray::<_, U64>::default();
@@ -63,9 +60,9 @@ impl Group for Ristretto255 {
// Implements the `HashToScalar()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-07.html#section-4.1
- fn hash_to_scalar(input: &[&[u8]], dst: &[u8]) -> Result
+ fn hash_to_scalar(input: &[&[u8]], dst: &[&[u8]]) -> Result
where
- H: Digest + BlockSizeUser,
+ H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess + IsLessOrEqual,
{
let mut uniform_bytes = GenericArray::<_, U64>::default();
@@ -90,11 +87,8 @@ impl Group for Ristretto255 {
}
fn deserialize_elem(element_bits: &[u8]) -> Result {
- if element_bits.len() != 32 {
- return Err(Error::Deserialization);
- }
-
CompressedRistretto::from_slice(element_bits)
+ .map_err(|_| Error::Deserialization)?
.decompress()
.filter(|point| point != &RistrettoPoint::identity())
.ok_or(Error::Deserialization)
@@ -104,7 +98,7 @@ impl Group for Ristretto255 {
loop {
let scalar = Scalar::random(rng);
- if scalar != Scalar::zero() {
+ if scalar != Scalar::ZERO {
break scalar;
}
}
@@ -115,12 +109,12 @@ impl Group for Ristretto255 {
}
fn is_zero_scalar(scalar: Self::Scalar) -> subtle::Choice {
- scalar.ct_eq(&Scalar::zero())
+ scalar.ct_eq(&Scalar::ZERO)
}
#[cfg(test)]
fn zero_scalar() -> Self::Scalar {
- Scalar::zero()
+ Scalar::ZERO
}
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray {
@@ -131,8 +125,8 @@ impl Group for Ristretto255 {
scalar_bits
.try_into()
.ok()
- .and_then(Scalar::from_canonical_bytes)
- .filter(|scalar| scalar != &Scalar::zero())
+ .and_then(|bytes| Scalar::from_canonical_bytes(bytes).into())
+ .filter(|scalar| scalar != &Scalar::ZERO)
.ok_or(Error::Deserialization)
}
}
diff --git a/src/group/tests.rs b/src/group/tests.rs
index f12571f..58737ce 100644
--- a/src/group/tests.rs
+++ b/src/group/tests.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Includes a series of tests for the group implementations
@@ -15,6 +16,8 @@ use crate::{Error, Group, Result};
#[test]
fn test_group_properties() -> Result<()> {
use p256::NistP256;
+ use p384::NistP384;
+ use p521::NistP521;
#[cfg(feature = "ristretto255")]
{
@@ -27,6 +30,12 @@ fn test_group_properties() -> Result<()> {
test_identity_element_error::()?;
test_zero_scalar_error::()?;
+ test_identity_element_error::()?;
+ test_zero_scalar_error::()?;
+
+ test_identity_element_error::()?;
+ test_zero_scalar_error::()?;
+
Ok(())
}
diff --git a/src/lib.rs b/src/lib.rs
index 00fbe88..81dd4ff 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
//!
//! Note: This implementation is in sync with
-//! [draft-irtf-cfrg-voprf-11](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html),
+//! [draft-irtf-cfrg-voprf-11](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html),
//! but this specification is subject to change, until the final version
//! published by the IETF.
//!
@@ -532,24 +533,15 @@
//! a [`CipherSuite`].
//!
//! - The `ristretto255` feature enables using [`Ristretto255`] as the
-//! underlying group for the [Group] choice. A backend feature, which are
-//! re-exported from [curve25519-dalek] and allow for selecting the
-//! 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.
+//! underlying group for the [Group] choice. To select a specific backend see
+//! the [curve25519-dalek] documentation.
//!
//! [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]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
+#![cfg_attr(not(test), deny(unsafe_code))]
#![warn(
clippy::cargo,
clippy::missing_errors_doc,
@@ -564,9 +556,6 @@ extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
-#[cfg(feature = "serde")]
-extern crate serde_ as serde;
-
mod ciphersuite;
mod common;
mod error;
diff --git a/src/oprf.rs b/src/oprf.rs
index 1fa73f0..710b748 100644
--- a/src/oprf.rs
+++ b/src/oprf.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Contains the main OPRF API
@@ -41,7 +42,7 @@ use crate::{CipherSuite, Error, Group, Result};
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct OprfClient
where
@@ -59,7 +60,7 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct OprfServer
where
@@ -277,7 +278,7 @@ where
.chain_update(input.as_ref())
.chain_update(elem_len)
.chain_update(CS::Group::serialize_elem(unblinded_element))
- .chain_update(&STR_FINALIZE)
+ .chain_update(STR_FINALIZE)
.finalize())
})
}
@@ -291,11 +292,10 @@ where
mod tests {
use core::ptr;
- use generic_array::sequence::Concat;
use rand::rngs::OsRng;
use super::*;
- use crate::common::{create_context_string, STR_HASH_TO_GROUP};
+ use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
fn prf(
@@ -308,8 +308,8 @@ mod tests {
::OutputSize:
IsLess + IsLessOrEqual<::BlockSize>,
{
- let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(mode));
- let point = CS::Group::hash_to_curve::(&[input], &dst).unwrap();
+ let dst = Dst::new::(STR_HASH_TO_GROUP, mode);
+ let point = CS::Group::hash_to_curve::(&[input], &dst.as_dst()).unwrap();
let res = point * &key;
@@ -348,9 +348,8 @@ mod tests {
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
.unwrap();
- let dst =
- GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(Mode::Oprf));
- let point = CS::Group::hash_to_curve::(&[&input], &dst).unwrap();
+ let dst = Dst::new::(STR_HASH_TO_GROUP, Mode::Oprf);
+ let point = CS::Group::hash_to_curve::(&[&input], &dst.as_dst()).unwrap();
let res2 = finalize_after_unblind::(iter::once((input.as_ref(), point)), &[])
.next()
.unwrap()
@@ -427,6 +426,8 @@ mod tests {
#[test]
fn test_functionality() -> Result<()> {
use p256::NistP256;
+ use p384::NistP384;
+ use p521::NistP521;
#[cfg(feature = "ristretto255")]
{
@@ -447,6 +448,20 @@ mod tests {
zeroize_oprf_client::();
zeroize_oprf_server::();
+ base_retrieval::();
+ base_inversion_unsalted::();
+ server_evaluate::();
+
+ zeroize_oprf_client::();
+ zeroize_oprf_server::();
+
+ base_retrieval::();
+ base_inversion_unsalted::();
+ server_evaluate::();
+
+ zeroize_oprf_client::();
+ zeroize_oprf_server::();
+
Ok(())
}
}
diff --git a/src/poprf.rs b/src/poprf.rs
index 1dfabe7..a0fd531 100644
--- a/src/poprf.rs
+++ b/src/poprf.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Contains the main POPRF API
@@ -14,16 +15,14 @@ use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser};
-use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use crate::common::{
- create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof,
- hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof, BlindedElement,
- EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR,
- STR_INFO,
+ derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
+ server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
+ PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -41,7 +40,7 @@ use crate::{CipherSuite, Error, Group, Result};
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct PoprfClient
where
@@ -61,7 +60,7 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct PoprfServer
where
@@ -541,7 +540,7 @@ pub type PoprfServerBatchEvaluatePreparedEvaluationElements = Map<
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct PoprfPreparedTweak(
#[cfg_attr(feature = "serde", serde(with = "Scalar::"))]
@@ -616,10 +615,9 @@ where
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
let framed_info = [STR_INFO.as_slice(), &info_len, info];
- let dst =
- GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(Mode::Poprf));
+ let dst = Dst::new::(STR_HASH_TO_SCALAR, Mode::Poprf);
// This can't fail, the size of the `input` is known.
- let m = CS::Group::hash_to_scalar::(&framed_info, &dst).unwrap();
+ let m = CS::Group::hash_to_scalar::(&framed_info, &dst.as_dst()).unwrap();
let t = CS::Group::base_elem() * &m;
let tweaked_key = t + &pk;
@@ -654,10 +652,9 @@ where
let info_len = i2osp_2(info.len()).map_err(|_| Error::Info)?;
let framed_info = [STR_INFO.as_slice(), &info_len, info];
- let dst =
- GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::(Mode::Poprf));
+ let dst = Dst::new::(STR_HASH_TO_SCALAR, Mode::Poprf);
// This can't fail, the size of the `input` is known.
- let m = CS::Group::hash_to_scalar::(&framed_info, &dst).unwrap();
+ let m = CS::Group::hash_to_scalar::(&framed_info, &dst.as_dst()).unwrap();
let t = sk + &m;
@@ -723,7 +720,7 @@ where
)?;
Ok(blinds
- .zip(messages.into_iter())
+ .zip(messages)
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
}
@@ -810,8 +807,8 @@ mod tests {
{
let t = compute_tweak::(key, Some(info)).unwrap();
- let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::(mode));
- let point = CS::Group::hash_to_curve::(&[input], &dst).unwrap();
+ let dst = Dst::new::(STR_HASH_TO_GROUP, mode);
+ let point = CS::Group::hash_to_curve::(&[input], &dst.as_dst()).unwrap();
// evaluatedElement = G.ScalarInverse(t) * blindedElement
let res = point * &CS::Group::invert_scalar(t);
@@ -864,10 +861,9 @@ mod tests {
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
.unwrap();
let wrong_pk = {
- let dst = GenericArray::from(STR_HASH_TO_GROUP)
- .concat(create_context_string::(Mode::Oprf));
+ let dst = Dst::new::(STR_HASH_TO_GROUP, Mode::Oprf);
// Choose a group element that is unlikely to be the right public key
- CS::Group::hash_to_curve::(&[b"msg"], &dst).unwrap()
+ CS::Group::hash_to_curve::(&[b"msg"], &dst.as_dst()).unwrap()
};
let client_finalize_result = client_blind_result.state.finalize(
input,
@@ -970,6 +966,8 @@ mod tests {
#[test]
fn test_functionality() -> Result<()> {
use p256::NistP256;
+ use p384::NistP384;
+ use p521::NistP521;
#[cfg(feature = "ristretto255")]
{
@@ -990,6 +988,20 @@ mod tests {
zeroize_verifiable_client::();
zeroize_verifiable_server::();
+ verifiable_retrieval::();
+ verifiable_bad_public_key::();
+ verifiable_server_evaluate::();
+
+ zeroize_verifiable_client::();
+ zeroize_verifiable_server::();
+
+ verifiable_retrieval::();
+ verifiable_bad_public_key::();
+ verifiable_server_evaluate::();
+
+ zeroize_verifiable_client::();
+ zeroize_verifiable_server::();
+
Ok(())
}
}
diff --git a/src/serialization.rs b/src/serialization.rs
index 6a6fa60..7f1d38d 100644
--- a/src/serialization.rs
+++ b/src/serialization.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Handles the serialization of each of the components used in the VOPRF
//! protocol
@@ -306,11 +307,11 @@ fn deserialize_scalar(input: &mut &[u8]) -> Result {
}
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 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() {
return None;
}
@@ -389,6 +390,8 @@ mod test {
}
let _ = $item::::deserialize(&$bytes[..]);
+ let _ = $item::::deserialize(&$bytes[..]);
+ let _ = $item::::deserialize(&$bytes[..]);
};
}
diff --git a/src/tests/cfrg_vectors.rs b/src/tests/cfrg_vectors.rs
index b7c26df..581518d 100644
--- a/src/tests/cfrg_vectors.rs
+++ b/src/tests/cfrg_vectors.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! 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#"
## OPRF(ristretto255, SHA-512)
diff --git a/src/tests/mock_rng.rs b/src/tests/mock_rng.rs
index 3ccaee8..eea5442 100644
--- a/src/tests/mock_rng.rs
+++ b/src/tests/mock_rng.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
use alloc::vec::Vec;
use core::cmp::min;
@@ -50,7 +51,7 @@ impl RngCore for CycleRng {
#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
let len = min(self.v.len(), dest.len());
- (&mut dest[..len]).copy_from_slice(&self.v[..len]);
+ dest[..len].copy_from_slice(&self.v[..len]);
rotate_left(&mut self.v, len);
}
diff --git a/src/tests/mod.rs b/src/tests/mod.rs
index 56a7fc7..71e7089 100644
--- a/src/tests/mod.rs
+++ b/src/tests/mod.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
mod cfrg_vectors;
mod mock_rng;
diff --git a/src/tests/parser.rs b/src/tests/parser.rs
index 313859c..5dd9913 100644
--- a/src/tests/parser.rs
+++ b/src/tests/parser.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
use alloc::string::{String, ToString};
use alloc::vec::Vec;
@@ -96,7 +97,7 @@ fn parse_params(input: &str) -> String {
let key = iter.next().unwrap().split_whitespace().next().unwrap();
let val = iter.next().unwrap().split_whitespace().next().unwrap();
- param = format!(" \"{}\": \"{}", key, val);
+ param = format!(" \"{key}\": \"{val}");
} else {
let s = line.trim().to_string();
if s.contains('~') || s.contains('#') {
diff --git a/src/tests/test_cfrg_vectors.rs b/src/tests/test_cfrg_vectors.rs
index 9fe23bf..ce34085 100644
--- a/src/tests/test_cfrg_vectors.rs
+++ b/src/tests/test_cfrg_vectors.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
use alloc::string::String;
use alloc::vec;
@@ -14,7 +15,7 @@ use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::ArrayLength;
-use json::JsonValue;
+use serde_json::Value;
use crate::tests::mock_rng::CycleRng;
use crate::tests::parser::*;
@@ -40,7 +41,7 @@ struct VOPRFTestVectorParameters {
output: Vec>,
}
-fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
+fn populate_test_vectors(values: &Value) -> VOPRFTestVectorParameters {
VOPRFTestVectorParameters {
seed: decode(values, "Seed"),
sksm: decode(values, "skSm"),
@@ -57,18 +58,18 @@ fn populate_test_vectors(values: &JsonValue) -> VOPRFTestVectorParameters {
}
}
-fn decode(values: &JsonValue, key: &str) -> Vec {
+fn decode(values: &Value, key: &str) -> Vec {
values[key]
.as_str()
- .and_then(|s| hex::decode(&s).ok())
+ .and_then(|s| hex::decode(s).ok())
.unwrap_or_default()
}
-fn decode_vec(values: &JsonValue, key: &str) -> Vec> {
+fn decode_vec(values: &Value, key: &str) -> Vec> {
let s = values[key].as_str().unwrap();
let res = match s.contains(',') {
- true => Some(s.split(',').map(|x| hex::decode(&x).unwrap()).collect()),
- false => Some(vec![hex::decode(&s).unwrap()]),
+ true => Some(s.split(',').map(|x| hex::decode(x).unwrap()).collect()),
+ false => Some(vec![hex::decode(s).unwrap()]),
};
res.unwrap()
}
@@ -76,8 +77,10 @@ fn decode_vec(values: &JsonValue, key: &str) -> Vec> {
macro_rules! json_to_test_vectors {
( $v:ident, $cs:expr, $mode:expr ) => {
$v[$cs][$mode]
- .members()
- .map(|x| populate_test_vectors(&x))
+ .as_array()
+ .into_iter()
+ .flatten()
+ .map(populate_test_vectors)
.collect::>()
};
}
@@ -85,8 +88,10 @@ macro_rules! json_to_test_vectors {
#[test]
fn test_vectors() -> Result<()> {
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");
#[cfg(feature = "ristretto255")]
@@ -157,6 +162,60 @@ fn test_vectors() -> Result<()> {
test_poprf_finalize::(&p256_poprf_tvs)?;
test_poprf_evaluate::(&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::(&p384_oprf_tvs)?;
+ test_oprf_blind::(&p384_oprf_tvs)?;
+ test_oprf_blind_evaluate::(&p384_oprf_tvs)?;
+ test_oprf_finalize::(&p384_oprf_tvs)?;
+ test_oprf_evaluate::(&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::(&p384_voprf_tvs)?;
+ test_voprf_blind::(&p384_voprf_tvs)?;
+ test_voprf_blind_evaluate::(&p384_voprf_tvs)?;
+ test_voprf_finalize::(&p384_voprf_tvs)?;
+ test_voprf_evaluate::(&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::(&p384_poprf_tvs)?;
+ test_poprf_blind::(&p384_poprf_tvs)?;
+ test_poprf_blind_evaluate::(&p384_poprf_tvs)?;
+ test_poprf_finalize::(&p384_poprf_tvs)?;
+ test_poprf_evaluate::(&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::(&p521_oprf_tvs)?;
+ test_oprf_blind::(&p521_oprf_tvs)?;
+ test_oprf_blind_evaluate::(&p521_oprf_tvs)?;
+ test_oprf_finalize::(&p521_oprf_tvs)?;
+ test_oprf_evaluate::(&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::(&p521_voprf_tvs)?;
+ test_voprf_blind::(&p521_voprf_tvs)?;
+ test_voprf_blind_evaluate::(&p521_voprf_tvs)?;
+ test_voprf_finalize::(&p521_voprf_tvs)?;
+ test_voprf_evaluate::(&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::(&p521_poprf_tvs)?;
+ test_poprf_blind::(&p521_poprf_tvs)?;
+ test_poprf_blind_evaluate::(&p521_poprf_tvs)?;
+ test_poprf_finalize::(&p521_poprf_tvs)?;
+ test_poprf_evaluate::(&p521_poprf_tvs)?;
+
Ok(())
}
diff --git a/src/voprf.rs b/src/voprf.rs
index 4df3b05..cbbafb3 100644
--- a/src/voprf.rs
+++ b/src/voprf.rs
@@ -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
-// LICENSE-MIT file in the root directory of this source tree and the Apache
+// This source code is dual-licensed under either the MIT license found in the
+// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
-// of this source tree.
+// of this source tree. You may select, at your option, one of the above-listed
+// licenses.
//! Contains the main VOPRF API
@@ -39,7 +40,7 @@ use crate::{CipherSuite, Error, Group, Result};
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct VoprfClient
where
@@ -59,7 +60,7 @@ where
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
- serde(crate = "serde", bound = "")
+ serde(bound = "")
)]
pub struct VoprfServer
where
@@ -160,7 +161,7 @@ where
///
/// The resulting messages can each fail individually with [`Error::Input`]
/// 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,
clients: &'a IC,
messages: &'a IM,
@@ -169,7 +170,7 @@ where
) -> Result>
where
CS: 'a,
- I: AsRef<[u8]>,
+ I: 'a + AsRef<[u8]>,
&'a II: 'a + IntoIterator
- ,
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IC: 'a + IntoIterator
- >,
@@ -536,7 +537,7 @@ where
)?;
Ok(blinds
- .zip(messages.into_iter())
+ .zip(messages)
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
}
@@ -587,13 +588,12 @@ mod tests {
use ::alloc::vec;
use ::alloc::vec::Vec;
- use generic_array::sequence::Concat;
use generic_array::typenum::Sum;
use generic_array::ArrayLength;
use rand::rngs::OsRng;
use super::*;
- use crate::common::{create_context_string, STR_HASH_TO_GROUP};
+ use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
fn prf(
@@ -605,8 +605,8 @@ mod tests {