Compare commits

..
21 Commits
Author SHA1 Message Date
Kevin LewiandGitHub eafa134c94 Publishing v0.5.0-pre.5 (#115) 2023-06-27 15:10:37 -07:00
daxpeddaandGitHub 209b957ae4 Bump curve25519-dalek to v4.0.0-rc.3 (#113) 2023-06-26 11:09:06 -07:00
Kevin LewiandGitHub f79ebf9844 Updating dual-license language (#110) 2023-05-22 23:04:39 -07:00
Kevin LewiandGitHub 20a35da7ba Publishing 0.5.0-pre.4 (#109) 2023-05-21 00:29:21 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4bd2cf466e 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>
2023-04-02 11:21:28 +09:00
Kevin LewiandGitHub 829c6add0f Publishing 0.5.0-pre.3 (#107) 2023-03-04 15:09:02 -08:00
daxpeddaandGitHub 8b895cc631 Update RustCrypto dependencies to v0.13 (#106) 2023-03-04 14:54:13 -08:00
daxpeddaandGitHub 83eb78b232 Test P-384 (#84) 2023-02-08 15:48:12 -08:00
daxpeddaandGitHub 5badeff8d2 Update to draft 19 (#101) 2023-02-08 00:18:04 -08:00
Kevin LewiandGitHub 40d81294db Publishing 0.5.0-pre.2 (#104) 2023-02-03 13:26:11 -08:00
daxpeddaandGitHub 8363d26f6f Bump curve25519-dalek to v4.0.0-rc.1 (#102) 2023-02-03 11:19:00 -08:00
daxpeddaandGitHub 5bce3e3206 Use explicit crate features (#100) 2023-02-01 11:37:47 -08:00
daxpeddaandGitHub 2787151e1d Update curve25519-dalek (#94) 2023-01-31 14:19:48 -08:00
daxpeddaandGitHub 0409db6f40 Depend on ProjectivePoint: ToEncodedPoint (#95) 2023-01-31 14:19:33 -08:00
daxpeddaandGitHub 74eaebe446 Fix Clippy (#96) 2023-01-31 10:31:13 -08:00
daxpeddaandGitHub c8de51672b Replace json with serde_json (#92) 2023-01-19 14:17:49 -08:00
daxpeddaandGitHub daa8dc048f Upgrade p256 to v0.12 (#90)
* Upgrade `p256` to v0.12

* Upgrade MSRV to 1.60
2023-01-19 11:11:56 -08:00
Kevin LewiandGitHub 2a351ceb4d Publishing 0.5.0-pre.1 (#88) 2022-12-19 13:17:58 -08:00
Kevin LewiandGitHub 8f60a10b8d Adding all-features CI test (#87) 2022-12-17 18:20:57 -08:00
daxpeddaandGitHub 1691125b09 Update curve25519-dalek to 4.0.0-pre.5 (#86)
* Update `curve25519-dalek`

* Improve documentation
2022-12-17 18:12:23 -08:00
daxpeddaandGitHub 6913b5deaa Fix Clippy (#85) 2022-12-10 14:21:04 -08:00
24 changed files with 1265 additions and 1147 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.
+10 -6
View File
@@ -35,8 +35,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,7 +43,7 @@ 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
@@ -75,6 +74,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,8 +93,7 @@ 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
@@ -120,7 +124,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
+20
View File
@@ -1,5 +1,25 @@
# Changelog # Changelog
## 0.5.0-pre.5 (June 27, 2023)
* Updated curve25519-dalek dependency to 4.0.0-rc.3
## 0.5.0-pre.4 (May 20, 2023)
* Updated curve25519-dalek dependency to 4.0.0-rc.2
## 0.5.0-pre.3 (March 4, 2023)
* Updated to be in sync with draft-irtf-cfrg-voprf-19
* Increased MSRV to 1.65
* Updated p256 dependency to v0.13
* Added p384 tests
## 0.5.0-pre.2 (February 3, 2023)
* Increased MSRV to 1.60
* Updated p256 dependency to v0.12
* Updated curve25519-dalek dependency to 4.0.0-rc.1
## 0.5.0-pre.1 (December 19, 2022)
* Updated curve25519-dalek dependency to 4.0.0-pre.5
## 0.4.0 (September 15, 2022) ## 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.
+21 -18
View File
@@ -7,37 +7,35 @@ 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.5.0-pre.5"
[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.0.0-rc.3", 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 }
@@ -47,16 +45,21 @@ zeroize = { version = "1.5", 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", "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.
+5 -3
View File
@@ -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.5.0-pre.4"
``` ```
### 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;
+88 -34
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;
@@ -33,7 +35,7 @@ pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite"; pub(crate) const STR_COMPOSITE: [u8; 9] = *b"Composite";
pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge"; pub(crate) const STR_CHALLENGE: [u8; 9] = *b"Challenge";
pub(crate) const STR_INFO: [u8; 4] = *b"Info"; pub(crate) const STR_INFO: [u8; 4] = *b"Info";
pub(crate) const STR_VOPRF: [u8; 8] = *b"VOPRF10-"; pub(crate) const STR_OPRF: [u8; 7] = *b"OPRFV1-";
pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-"; pub(crate) const STR_HASH_TO_SCALAR: [u8; 13] = *b"HashToScalar-";
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-"; pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-";
@@ -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
@@ -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 })
@@ -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,64 @@ 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: &'static str,
}
impl<L: ArrayLength<u8>> Dst<L> {
pub(crate) fn new<CS: CipherSuite, T, TL: ArrayLength<u8>>(par_1: T, mode: Mode) -> Self
where
T: Into<GenericArray<u8, TL>>,
TL: 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_OPRF)
.concat([mode.to_u8()].into())
.concat([b'-'].into());
let dst_1 = par_1.concat(par_2);
let dst_2 = CS::ID;
assert!(
L::USIZE + dst_2.len() <= u16::MAX.into(),
"constructed DST longer then {}",
u16::MAX
);
Self { dst_1, dst_2 }
}
pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
[&self.dst_1, self.dst_2.as_bytes()]
}
pub(crate) fn i2osp_2(&self) -> [u8; 2] {
u16::try_from(L::USIZE + self.dst_2.len())
.unwrap()
.to_be_bytes()
}
}
trait DigestExt {
fn chain_update_multi(self, data: &[&[u8]]) -> Self;
}
impl<T> DigestExt for T
where 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)
} }
} }
+9 -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,7 @@ 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;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -27,6 +29,9 @@ 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>()?;
Ok(()) Ok(())
} }
+11 -22
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.
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF) //! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
//! //!
//! Note: This implementation is in sync with //! Note: This implementation is in sync with
//! [draft-irtf-cfrg-voprf-11](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html), //! [draft-irtf-cfrg-voprf-19](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html),
//! but this specification is subject to change, until the final version //! but this specification is subject to change, until the final version
//! published by the IETF. //! published by the IETF.
//! //!
@@ -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;
+21 -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,7 @@ mod tests {
#[test] #[test]
fn test_functionality() -> Result<()> { fn test_functionality() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -447,6 +447,13 @@ 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>();
Ok(()) Ok(())
} }
} }
+27 -23
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;
@@ -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,7 @@ mod tests {
#[test] #[test]
fn test_functionality() -> Result<()> { fn test_functionality() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -990,6 +987,13 @@ 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>();
Ok(()) Ok(())
} }
} }
+6 -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.
//! 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
@@ -389,6 +390,7 @@ mod test {
} }
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]); let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<p384::NistP384>::deserialize(&$bytes[..]);
}; };
} }
+889 -891
View File
File diff suppressed because it is too large Load Diff
+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;
+7 -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.
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -14,7 +15,7 @@ pub(crate) fn rfc_to_json(input: &str) -> String {
} }
fn parse_ciphersuites(input: &str) -> String { fn parse_ciphersuites(input: &str) -> String {
let re = regex::Regex::new(r"## OPRF\((?P<ciphersuite>.+?)\)").unwrap(); let re = regex::Regex::new(r"\n## (?P<ciphersuite>.+?)\n").unwrap();
let mut ciphersuites = vec![]; let mut ciphersuites = vec![];
let chunks: Vec<&str> = re.split(input).collect(); let chunks: Vec<&str> = re.split(input).collect();
@@ -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('#') {
+51 -20
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,9 @@ 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;
let rfc = json::parse(rfc_to_json(super::cfrg_vectors::VECTORS).as_str()) let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
.expect("Could not parse json"); .expect("Could not parse json");
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
@@ -95,7 +99,7 @@ fn test_vectors() -> Result<()> {
let ristretto_oprf_tvs = json_to_test_vectors!( let ristretto_oprf_tvs = json_to_test_vectors!(
rfc, rfc,
String::from("ristretto255, SHA-512"), String::from("ristretto255-SHA512"),
String::from("OPRF") String::from("OPRF")
); );
assert_ne!(ristretto_oprf_tvs.len(), 0); assert_ne!(ristretto_oprf_tvs.len(), 0);
@@ -107,7 +111,7 @@ fn test_vectors() -> Result<()> {
let ristretto_voprf_tvs = json_to_test_vectors!( let ristretto_voprf_tvs = json_to_test_vectors!(
rfc, rfc,
String::from("ristretto255, SHA-512"), String::from("ristretto255-SHA512"),
String::from("VOPRF") String::from("VOPRF")
); );
assert_ne!(ristretto_voprf_tvs.len(), 0); assert_ne!(ristretto_voprf_tvs.len(), 0);
@@ -119,7 +123,7 @@ fn test_vectors() -> Result<()> {
let ristretto_poprf_tvs = json_to_test_vectors!( let ristretto_poprf_tvs = json_to_test_vectors!(
rfc, rfc,
String::from("ristretto255, SHA-512"), String::from("ristretto255-SHA512"),
String::from("POPRF") String::from("POPRF")
); );
assert_ne!(ristretto_poprf_tvs.len(), 0); assert_ne!(ristretto_poprf_tvs.len(), 0);
@@ -131,7 +135,7 @@ fn test_vectors() -> Result<()> {
} }
let p256_oprf_tvs = let p256_oprf_tvs =
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("OPRF")); json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("OPRF"));
assert_ne!(p256_oprf_tvs.len(), 0); assert_ne!(p256_oprf_tvs.len(), 0);
test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?; test_oprf_seed_to_key::<NistP256>(&p256_oprf_tvs)?;
test_oprf_blind::<NistP256>(&p256_oprf_tvs)?; test_oprf_blind::<NistP256>(&p256_oprf_tvs)?;
@@ -140,7 +144,7 @@ fn test_vectors() -> Result<()> {
test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?; test_oprf_evaluate::<NistP256>(&p256_oprf_tvs)?;
let p256_voprf_tvs = let p256_voprf_tvs =
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("VOPRF")); json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("VOPRF"));
assert_ne!(p256_voprf_tvs.len(), 0); assert_ne!(p256_voprf_tvs.len(), 0);
test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?; test_voprf_seed_to_key::<NistP256>(&p256_voprf_tvs)?;
test_voprf_blind::<NistP256>(&p256_voprf_tvs)?; test_voprf_blind::<NistP256>(&p256_voprf_tvs)?;
@@ -149,7 +153,7 @@ fn test_vectors() -> Result<()> {
test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?; test_voprf_evaluate::<NistP256>(&p256_voprf_tvs)?;
let p256_poprf_tvs = let p256_poprf_tvs =
json_to_test_vectors!(rfc, String::from("P-256, SHA-256"), String::from("POPRF")); json_to_test_vectors!(rfc, String::from("P256-SHA256"), String::from("POPRF"));
assert_ne!(p256_poprf_tvs.len(), 0); assert_ne!(p256_poprf_tvs.len(), 0);
test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?; test_poprf_seed_to_key::<NistP256>(&p256_poprf_tvs)?;
test_poprf_blind::<NistP256>(&p256_poprf_tvs)?; test_poprf_blind::<NistP256>(&p256_poprf_tvs)?;
@@ -157,6 +161,33 @@ fn test_vectors() -> Result<()> {
test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?; test_poprf_finalize::<NistP256>(&p256_poprf_tvs)?;
test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?; test_poprf_evaluate::<NistP256>(&p256_poprf_tvs)?;
let p384_oprf_tvs =
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("OPRF"));
assert_ne!(p384_oprf_tvs.len(), 0);
test_oprf_seed_to_key::<NistP384>(&p384_oprf_tvs)?;
test_oprf_blind::<NistP384>(&p384_oprf_tvs)?;
test_oprf_blind_evaluate::<NistP384>(&p384_oprf_tvs)?;
test_oprf_finalize::<NistP384>(&p384_oprf_tvs)?;
test_oprf_evaluate::<NistP384>(&p384_oprf_tvs)?;
let p384_voprf_tvs =
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("VOPRF"));
assert_ne!(p384_voprf_tvs.len(), 0);
test_voprf_seed_to_key::<NistP384>(&p384_voprf_tvs)?;
test_voprf_blind::<NistP384>(&p384_voprf_tvs)?;
test_voprf_blind_evaluate::<NistP384>(&p384_voprf_tvs)?;
test_voprf_finalize::<NistP384>(&p384_voprf_tvs)?;
test_voprf_evaluate::<NistP384>(&p384_voprf_tvs)?;
let p384_poprf_tvs =
json_to_test_vectors!(rfc, String::from("P384-SHA384"), String::from("POPRF"));
assert_ne!(p384_poprf_tvs.len(), 0);
test_poprf_seed_to_key::<NistP384>(&p384_poprf_tvs)?;
test_poprf_blind::<NistP384>(&p384_poprf_tvs)?;
test_poprf_blind_evaluate::<NistP384>(&p384_poprf_tvs)?;
test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;
Ok(()) Ok(())
} }
+24 -16
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
@@ -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,7 @@ mod tests {
#[test] #[test]
fn test_functionality() -> Result<()> { fn test_functionality() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -861,6 +860,15 @@ 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>();
Ok(()) Ok(())
} }
} }