Compare commits

..
Author SHA1 Message Date
breakingbread 0bbf8fbfb5 chore: bump to v1.0.0-rc.1, reject trailing bytes, reject identity elemnts, more tests (#10)
Rust CI / cargo clippy (push) Successful in 26s
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m20s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m27s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m30s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m21s
Publish / publish (release) Successful in 19s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m34s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m30s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m26s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m30s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Reviewed-on: #10
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-02 23:56:34 +02:00
breakingbread 1451f937ca chore: bump to v1.0.0-rc.0, implement zeroize for deps and license fix (#9)
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 26s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m23s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m23s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m19s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m30s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m30s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m27s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 12s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 11s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 13s
Publish / publish (release) Successful in 17s
Reviewed-on: #9
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-02 15:16:25 +02:00
breakingbread eb00b86000 chore: bump to v1.0.0-pre.1 and mostly cleanup stuff (#8)
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 29s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m25s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m24s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m20s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m20s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m29s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / cargo audit (push) Successful in 5s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Publish / publish (release) Successful in 18s
Reviewed-on: #8
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-01 15:10:11 +02:00
breakingbread 385ee9b1cc ci: up to date push and pull_request (#6)
Rust CI / cargo clippy (push) Successful in 25s
Rust CI / cargo fmt (push) Successful in 2s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m24s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m15s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m26s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m35s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m28s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m28s
Publish / publish (release) Successful in 31s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 15s
Reviewed-on: #6
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-06-29 13:28:50 +02:00
breakingbread 51157080fc chore: update curve25519-dalek to 5.0.0-rc (#5)
Rust CI / cargo fmt (push) Successful in 2s
Rust CI / cargo clippy (push) Successful in 27s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m24s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m24s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m19s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m27s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 15s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 11s
Reviewed-on: #5
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-06-29 12:48:00 +02:00
breakingbread 617fc0241c chore: bump to 1.0.0-pre.0, new name voprf-vx, replace OprfCipherSuite trait with OprfHash type alias (#4)
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m17s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m24s
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 26s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m19s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m35s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m25s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m18s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m28s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m28s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m34s
Rust CI / cargo audit (push) Successful in 3s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Reviewed-on: #4
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-06-29 11:32:17 +02:00
breakingbread 9f9dc23aa1 chore: implemented Hash back into CipherSuite via hash2curve ExpandMsg (#3)
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m25s
Rust CI / cargo fmt (push) Successful in 4s
Rust CI / cargo clippy (push) Successful in 26s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m23s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m34s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m24s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m18s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m19s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m27s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m33s
Rust CI / cargo audit (push) Successful in 3s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 12s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 15s
Reviewed-on: vexahub/voprf-vexahub#3
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-06-28 18:16:42 +02:00
breakingbread 405c1901ef chore: bump to 0.7.0-pre.0, new ciphersuite impl (#2)
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 28s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m24s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m27s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m23s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m17s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m29s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m18s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m36s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m30s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m27s
Rust CI / cargo audit (push) Successful in 5s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 13s
Reviewed-on: vexahub/voprf-vexahub#2
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-06-28 13:14:48 +02:00
breakingbread 3ce3ac8817 chore: bump to 0.6.0-rc.0, migrate to elliptic-curve 0.14, hash2curve 0.14, rand_core 0.10 (#1)
Rust CI / cargo clippy (push) Successful in 27s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m26s
Rust CI / cargo fmt (push) Successful in 4s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m19s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m20s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m26s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m18s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m25s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m30s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m36s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 11s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 14s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 15s
Reviewed-on: vexahub/voprf-vexahub#1
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-06-27 17:04:58 +02:00
breakingbread d42e7948a1 Delete directory '.github' 2026-06-27 14:38:13 +02:00
Kevin LewiandGitHub 0a7dc184ca Publishing v0.6.0-pre.1 (#156) 2026-04-06 13:33:53 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f5b7e689e7 Bump actions/cache from 4 to 5 (#148)
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 14:46:54 -08:00
raphaelrobertandGitHub a22d46fd96 chore: update more dependencies (#145)
* update more dependencies

* cargo fmt

* address review comments

* fix format
2026-01-25 14:37:55 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
f23cdfab2d Bump actions/checkout from 4 to 6 (#149)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-23 14:54:37 -08:00
Kevin LewiandGitHub 256ab7bc52 Fixing docs issue (#147) 2025-11-10 01:32:43 -08:00
Kevin LewiandGitHub eb55e9f5b5 Publishing v0.6.0-pre.0 (#146) 2025-11-08 13:46:14 -08:00
raphaelrobertandGitHub e944f9db3b chore: update generic-array to v1 (#143)
* update generic-array to v1

* revert displaydoc removal

* fix cargo fmt
2025-11-04 11:40:16 -08:00
daxpeddaandGitHub f3f4fef0e9 Align Ristretto255::random_scalar() with spec (#142) 2025-05-07 15:40:24 -07:00
daxpeddaandGitHub 23aa7813e7 Enable curve25519-dalek/serde (#141) 2025-04-28 12:48:47 -07:00
daxpeddaandGitHub 0473d9db68 Bump MSRV to v1.83 (#140)
* Fix Clippy warnings for Rust v1.86

* Bump MSRV to v1.83

Signed-off-by: daxpedda <[email protected]>

---------

Signed-off-by: daxpedda <[email protected]>
2025-04-15 13:30:56 -07:00
Kevin LewiandGitHub f0531f0812 Publishing v0.5 (#133) 2024-03-06 17:49:35 -08:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
c93884aca3 Bump actions/cache from 3 to 4 (#132)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-17 14:33:57 -08:00
daxpeddaandGitHub 40769f7eca Fix ambiguous lifetime elision (#131) 2024-01-17 12:09:32 -08:00
Kevin LewiandGitHub 1b67086028 Publishing v0.5.0-pre.7 (#128) 2024-01-11 11:58:36 -08:00
daxpeddaandGitHub 68cc7d3709 Test P-521 (#127) 2023-11-12 16:14:52 -08:00
Kevin LewiandGitHub 59e3fedb21 Updating setup-rust-action (#125) 2023-09-21 18:45:22 -07:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
8da56845b8 Bump actions/checkout from 3 to 4 (#120)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

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

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-20 14:32:31 -07:00
Kevin LewiandGitHub c0162ec8d9 Fixing clippy IntoIterator warnings (#123) 2023-09-20 01:48:44 -07:00
Kevin LewiandGitHub ee91c9776c Publishing v0.5.0-pre.6 (#118) 2023-07-24 20:45:18 -07:00
daxpeddaandGitHub 0fdfdfdaee Bump curve25519-dalek to v4 (#116) 2023-07-24 16:39:40 -07:00
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
Kevin LewiandGitHub 2dc6a8b2c2 Publishing v0.4.0 (#83) 2022-09-15 02:18:34 -07:00
31 changed files with 2207 additions and 2454 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.
+102
View File
@@ -0,0 +1,102 @@
name: Rust CI
on:
push:
branches:
- master
pull_request:
types: [opened, reopened, synchronize]
concurrency:
group: ci-${{ gitea.ref }}
cancel-in-progress: true
jobs:
fmt:
name: cargo fmt
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Run cargo fmt
run: cargo fmt --all -- --check
clippy:
name: cargo clippy
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Run cargo clippy
run: cargo clippy --all-features --all-targets -- -D warnings
- name: Run cargo doc
run: cargo doc --no-deps --document-private-items --features danger,std
env:
RUSTDOCFLAGS: -D warnings
test:
name: test (${{ matrix.toolchain }} / ${{ matrix.backend_feature || 'no backend' }} / ${{ matrix.frontend_feature || 'no frontend' }})
runs-on: linux_amd64
strategy:
fail-fast: false
matrix:
backend_feature:
- --features ristretto255-ciphersuite
- ""
frontend_feature:
- ""
- --features danger
- --features serde
toolchain:
- stable
- "1.87.0"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@${{ matrix.toolchain }}
- name: Run cargo test
run: cargo test --no-default-features --lib --tests ${{ matrix.backend_feature }}
- name: Run cargo test with alloc
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features alloc
- name: Run cargo test with std
run: cargo test --no-default-features --lib --tests ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }} --features std
- name: Run cargo test with all features
run: cargo test --all-features
build-no-std:
name: no-std (${{ matrix.target }} / ${{ matrix.backend_feature || 'no backend' }})
runs-on: linux_amd64
strategy:
fail-fast: false
matrix:
target:
- wasm32-unknown-unknown
- thumbv6m-none-eabi
backend_feature:
- ""
- --features ristretto255-ciphersuite
frontend_feature:
- ""
- --features danger
- --features serde
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Build no-std
run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
audit:
name: cargo audit
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Run cargo audit
run: cargo audit -D warnings
+26
View File
@@ -0,0 +1,26 @@
name: Publish
on:
release:
types: [ published ]
jobs:
publish:
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Login to crates.io
run: cargo login $CRATES_IO_TOKEN
env:
CRATES_IO_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
- name: Dry run publish
run: cargo publish --dry-run --manifest-path Cargo.toml
- name: Publish
run: cargo publish --manifest-path Cargo.toml
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO_TOKEN }}
-12
View File
@@ -1,12 +0,0 @@
version: 2
updates:
- package-ecosystem: cargo
directory: /
schedule:
interval: daily
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
-175
View File
@@ -1,175 +0,0 @@
name: Rust CI
on:
push:
branches:
- main
pull_request:
types: [opened, repoened, synchronize]
jobs:
cargo-audit:
name: Audit
runs-on: ubuntu-latest
steps:
- name: Cache cargo-audit
uses: actions/cache@v3
with:
path: |
~/.cargo/.crates.toml
~/.cargo/.crates2.json
~/.cargo/bin/cargo-audit
key: cargo-audit
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Checkout sources
uses: actions/checkout@v3
- name: Run cargo audit
run: cargo audit -D warnings
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
backend_feature:
- --features ristretto255-ciphersuite,ristretto255-u64
- --features ristretto255-ciphersuite,ristretto255-u32
-
frontend_feature:
-
- --features danger
- --features serde
toolchain:
- stable
- 1.57.0
name: test
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install ${{ matrix.toolchain }} toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.toolchain }}
override: true
- name: Run cargo test
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features ${{ matrix.backend_feature }}
- name: Run cargo test with alloc
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features ${{ matrix.frontend_feature }},alloc ${{ matrix.backend_feature }}
- name: Run cargo test with std
uses: actions-rs/cargo@v1
with:
command: test
args: --no-default-features ${{ matrix.frontend_feature }},std ${{ matrix.backend_feature }}
build-no-std:
name: Build with no-std on ${{ matrix.target }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
# for wasm
- wasm32-unknown-unknown
# for any no_std target
- thumbv6m-none-eabi
backend_feature:
-
- --features ristretto255-ciphersuite,ristretto255-u64
- --features ristretto255-ciphersuite,ristretto255-u32
frontend_feature:
-
- --features danger
- --features serde
steps:
- uses: actions/checkout@v3
- uses: hecrj/setup-rust-action@v1
- run: rustup target add ${{ matrix.target }}
- run: cargo build --verbose --target=${{ matrix.target }} --no-default-features ${{ matrix.frontend_feature }} ${{ matrix.backend_feature }}
clippy:
name: cargo clippy
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
components: clippy
- name: Run cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: --all-targets -- -D warnings
- name: Run cargo doc
uses: actions-rs/cargo@v1
env:
RUSTDOCFLAGS: -D warnings
with:
command: doc
args: --no-deps --document-private-items --features danger,std
rustfmt:
name: cargo fmt
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Install nightly toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
components: rustfmt
- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
taplo:
name: Taplo
runs-on: ubuntu-latest
steps:
- name: Cache
uses: actions/cache@v3
with:
path: |
~/.cargo/.crates.toml
~/.cargo/.crates2.json
~/.cargo/bin/taplo
key: taplo
- name: Install Taplo
run: cargo install taplo-cli
- name: Checkout sources
uses: actions/checkout@v3
- name: Run Taplo
run: taplo fmt --check
-28
View File
@@ -1,28 +0,0 @@
name: Publish
on:
release:
types: [published]
jobs:
publish:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
steps:
- uses: hecrj/setup-rust-action@v1
with:
rust-version: ${{ matrix.rust }}
- uses: actions/checkout@master
- name: Login to crates.io
run: cargo login $CRATES_IO_TOKEN
env:
CRATES_IO_TOKEN: ${{ secrets.crates_io_token }}
- name: Dry run publish voprf
run: cargo publish --dry-run --manifest-path Cargo.toml
- name: Publish voprf
run: cargo publish --manifest-path Cargo.toml
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.crates_io_token }}
+89 -12
View File
@@ -1,23 +1,100 @@
# Changelog # Changelog
## 0.4.0-pre.4 (July 9, 2022) ## 1.0.0-rc.1 (July 3, 2026)
* Updated to be in sync with draft-irtf-cfrg-voprf-11
* Reject trailing bytes in all `deserialize` methods
* Reject identity element in `deterministic_blind_unchecked` to prevent blinding bypass
* Added roundtrip, trailing bytes, truncated, and empty input tests for serialization
## 1.0.0-rc.0 (July 2, 2026)
* Added missing license in Cargo manifest
* Implement `zeroize` feature for `digest`, `hybrid-array` and `sha2`
* Replaced license appendix in files while keeping original copyright
## 1.0.0-pre.1 (July 2, 2026)
* Simplified ciphersuite trait
* Moved multiplication operator to SecurityLevel type in Group trait
## 1.0.0-pre.0 (June 29, 2026)
Forked from [facebook/voprf](https://github.com/facebook/voprf/) at `0.6.0-pre.1`.
* MSRV bumped to 1.87
* Migrated from `elliptic-curve 0.13` to `0.14`
* Replaced `generic-array` with `hybrid-array 0.4`
* Updated `digest` to 0.11, `rand_core` to 0.10, `rand` to 0.10, `sha2` to 0.11
* Updated `p256`, `p384`, `p521` to `0.14`
* Replaced `elliptic-curve/hash2curve` feature with standalone `hash2curve 0.14` crate
* Removed `VoprfParameters` dependency to be replaced with `OprfParameters` + `GroupDigest`
* Added `SecurityLevel` associated type to `Group` trait for generic hash bounds
* Added `OkmLen` associated type to `Group` trait (`MapToCurve::Length`)
* Updated `hash_to_scalar` to use `MapToCurve::Length` as OKM length per RFC 9380
* Updated `random_scalar` for deterministic byte consumption with `rand_core 0.10`
* Auto-impl `CipherSuite` for any `OprfParameters + Group` type via `OprfHash<T>`
## 0.6.0-pre.1 (April 6, 2026)
* MSRV bumped to 1.85
* Updated rand_core dependency to 0.9
* Updated rand dependency to 0.9
* Updated subtle dependency to 2.6
* Fixed docs issue
## 0.6.0-pre.0 (November 8, 2025)
* MSRV bumped to 1.83
* Updated Ristretto255 random scalar generation
* Updated generic-array to v1
## 0.5.0 (March 6, 2024)
* Just a version bump from v0.5.0-pre.7
## 0.5.0-pre.7 (January 11, 2024)
* Updated to be in sync with RFC 9497
## 0.5.0-pre.6 (July 24, 2023)
* Updated curve25519-dalek dependency to 4
## 0.5.0-pre.5 (June 27, 2023)
* Updated curve25519-dalek dependency to 4.0.0-rc.3
## 0.5.0-pre.4 (May 20, 2023)
* Updated curve25519-dalek dependency to 4.0.0-rc.2
## 0.5.0-pre.3 (March 4, 2023)
* Updated to be in sync with draft-irtf-cfrg-voprf-19
* Increased MSRV to 1.65
* Updated p256 dependency to v0.13
* Added p384 tests
## 0.5.0-pre.2 (February 3, 2023)
* Increased MSRV to 1.60
* Updated p256 dependency to v0.12
* Updated curve25519-dalek dependency to 4.0.0-rc.1
## 0.5.0-pre.1 (December 19, 2022)
* Updated curve25519-dalek dependency to 4.0.0-pre.5
## 0.4.0 (September 15, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
the addition of the POPRF mode
* Adds the evaluate() function to the servers to calculate the output of the OPRF * Adds the evaluate() function to the servers to calculate the output of the OPRF
directly directly
* Renames the former evaluate() function to blind_evaluate to match the spec * Renames the former evaluate() function to blind_evaluate to match the spec
* Fixes the order of parameters for PoprfClient::blind to align it with the * Fixes the order of parameters for PoprfClient::blind to align it with the
other clients other clients
## 0.4.0-pre.3 (July 1, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-10, with
the only difference from -09 being a constant string change
## 0.4.0-pre.2 (April 21, 2022)
* Exposes the derive_key function under the "danger" feature * Exposes the derive_key function under the "danger" feature
## 0.4.0-pre.1 (April 1, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-09, with
the addition of the POPRF mode
* Added support for running the API without performing allocations * Added support for running the API without performing allocations
* Revamped the way the Group trait was used, so as to be more easily * Revamped the way the Group trait was used, so as to be more easily
extendable to other groups extendable to other groups
-76
View File
@@ -1,76 +0,0 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource-conduct@fb.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
+3 -21
View File
@@ -2,29 +2,11 @@
We want to make contributing to this project as easy and transparent as We want to make contributing to this project as easy and transparent as
possible. possible.
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `main`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. If you haven't already, complete the Contributor License Agreement ("CLA").
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here: <https://code.facebook.com/cla>
## Issues ## Issues
We use GitHub issues to track public bugs. Please ensure your description is We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue. clear and has sufficient instructions to be able to reproduce the issue.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
disclosure of security bugs. In those cases, please go through the process
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-vx, 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.
+43 -38
View File
@@ -1,62 +1,67 @@
[package] [package]
authors = ["Kevin Lewi <klewi@fb.com>"] authors = ["VexaHub Developers", "Kevin Lewi <lewi.kevin.k@gmail.com>"]
categories = ["no-std", "algorithms", "cryptography"] categories = ["no-std", "algorithms", "cryptography"]
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)" description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
edition = "2021" edition = "2024"
keywords = ["oprf"] keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"]
license = "MIT" license = "MIT OR Apache-2.0"
name = "voprf" name = "voprf-vx"
readme = "README.md" readme = "README.md"
repository = "https://github.com/novifinancial/voprf/" repository = "https://github.com/vexahub/voprf-vx/"
rust-version = "1.57" rust-version = "1.87"
version = "0.4.0-pre.4" version = "1.0.0-rc.1"
[features] [features]
alloc = [] alloc = []
danger = [] danger = []
default = ["ristretto255-ciphersuite", "ristretto255-u64", "serde"] doctest = ["dep:p256", "dep:sha2"]
ristretto255 = ["curve25519-dalek", "generic-array/more_lengths"] default = ["ristretto255-ciphersuite", "dep:serde"]
ristretto255-ciphersuite = ["ristretto255", "sha2"] ristretto255 = ["dep:curve25519-dalek"]
ristretto255-fiat-u32 = ["curve25519-dalek/fiat_u32_backend", "ristretto255"] ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
ristretto255-fiat-u64 = ["curve25519-dalek/fiat_u64_backend", "ristretto255"] serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
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 = "5.0.0-rc.1", default-features = false, features = ["rand_core", "zeroize"], optional = true }
derive-where = { version = "=1.0.0-rc.3", features = ["zeroize-on-drop"] } derive-where = { version = "1", features = ["zeroize-on-drop"] }
digest = "0.10" digest = { version = "0.11", features = ["zeroize"] }
displaydoc = { version = "0.2", default-features = false } displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.12", features = [ elliptic-curve = { version = "0.14", features = [
"hash2curve", "sec1",
"sec1",
"voprf",
] } ] }
generic-array = "0.14" hash2curve = "0.14"
rand_core = { version = "0.6", default-features = false } hybrid-array = { version = "0.4", features = ["zeroize"] }
serde_ = { version = "1", package = "serde", default-features = false, features = [ rand_core = { version = "0.10", default-features = false }
"derive", serde = { version = "1", default-features = false, features = [
"derive",
], optional = true } ], optional = true }
sha2 = { version = "0.10", default-features = false, optional = true } sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
subtle = { version = "2.3", default-features = false } p256 = { version = "0.14.0-rc.15", default-features = false, features = ["hash2curve", "oprf"], optional = true }
subtle = { version = "2.6", default-features = false }
zeroize = { version = "1.5", default-features = false } zeroize = { version = "1.5", default-features = false }
[dev-dependencies] [dev-dependencies]
generic-array = { version = "0.14", features = ["more_lengths"] }
hex = "0.4" hex = "0.4"
json = "0.12" p256 = { version = "0.14.0-rc.15", default-features = false, features = [
p256 = { version = "0.11", default-features = false, features = [ "hash2curve",
"hash2curve", "oprf",
"voprf", ] }
p384 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve",
"oprf",
] }
p521 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve",
"oprf",
] } ] }
proptest = "1" proptest = "1"
rand = "0.8" rand = "0.10"
regex = "1" regex = "1"
sha2 = "0.10" serde_json = "1"
sha2 = "0.11"
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["danger", "std"] all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = [] targets = []
features = ["doctest"]
-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.
+11 -7
View File
@@ -1,14 +1,14 @@
# voprf ![Build Status](https://github.com/novifinancial/voprf/workflows/Rust%20CI/badge.svg) # voprf
An implementation of a (verifiable) oblivious pseudorandom function (VOPRF) An implementation of a (verifiable) oblivious pseudorandom function (VOPRF)
A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation. A VOPRF is a verifiable oblivious pseudorandom function, a protocol between a client and a server. The regular (non-verifiable) OPRF is also supported in this implementation.
This implementation is based on the [Internet Draft for VOPRF](https://github.com/cfrg/draft-irtf-cfrg-voprf). This implementation is based on [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
Documentation Documentation
------------- -------------
The API can be found [here](https://docs.rs/voprf/) along with an example for usage. The API can be found [here](https://docs.rs/voprf-vx/) along with an example for usage.
Installation Installation
------------ ------------
@@ -16,20 +16,24 @@ 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.0-pre.4" voprf = { package = "voprf-vx", version = "1.0.0-rc.1" }
``` ```
### Minimum Supported Rust Version ### Minimum Supported Rust Version
Rust **1.57** or higher. Rust **1.87** or higher.
Contributors Contributors
------------ ------------
The author of this code is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)). This is a fork of [facebook/voprf](https://github.com/facebook/voprf/) maintained by [VexaHub](https://github.com/vexahub).
The original author is Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)).
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md). To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
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.
+30
View File
@@ -0,0 +1,30 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"dependencyDashboard": true,
"osvVulnerabilityAlerts": true,
"rangeStrategy": "auto",
"packageRules": [
{
"matchManagers": [
"cargo"
],
"groupName": "rust deps"
},
{
"matchManagers": [
"cargo"
],
"matchUpdateTypes": [
"major"
],
"automerge": false
}
],
"lockFileMaintenance": {
"enabled": true
},
"configMigration": true
}
-7
View File
@@ -1,8 +1 @@
format_code_in_doc_comments = true
format_strings = true
group_imports = "StdExternalCrate"
imports_granularity = "Module"
license_template_path = ".cargo/license.rs"
newline_style = "Unix" newline_style = "Unix"
unstable_features = true
wrap_comments = true
+28 -24
View File
@@ -1,28 +1,28 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! 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::{Digest, OutputSizeUser};
use elliptic_curve::VoprfParameters;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
use crate::Group; use crate::Group;
use digest::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256};
use digest::{FixedOutput, HashMarker, OutputSizeUser};
use hash2curve::{ExpandMsg, GroupDigest, MapToCurve, OprfParameters};
use hybrid_array::ArraySize;
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, True, U2};
/// Configures the underlying primitives used in VOPRF /// Configures the underlying primitives used in VOPRF
pub trait CipherSuite pub trait CipherSuite
where where
<Self::Hash as OutputSizeUser>::OutputSize: <Self::Hash as OutputSizeUser>::OutputSize: ArraySize
IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>, + IsLess<U256>
+ IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<<Self::Group as Group>::SecurityLevel, U2>, Output = True>,
{ {
/// The ciphersuite identifier as dictated by /// The ciphersuite identifier as dictated by
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/> /// <https://www.rfc-editor.org/rfc/rfc9497>
const ID: u16; const ID: &'static [u8];
/// 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,19 +30,23 @@ 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 /// The hash function associated with a curve's OPRF `expand_message` implementation.
type OprfHash<T> =
<<T as GroupDigest>::ExpandMsg as ExpandMsg<<T as MapToCurve>::SecurityLevel>>::Hash;
impl<T: OprfParameters> CipherSuite for T
where where
T: Group, T: Group,
T::Hash: BlockSizeUser + Digest, OprfHash<T>: BlockSizeUser + Default + FixedOutput + HashMarker,
<T::Hash as OutputSizeUser>::OutputSize: <OprfHash<T> as OutputSizeUser>::OutputSize: ArraySize
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>, + IsLess<U256>
+ IsLessOrEqual<<OprfHash<T> as BlockSizeUser>::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<<T as Group>::SecurityLevel, U2>, Output = True>,
{ {
const ID: u16 = T::ID; const ID: &'static [u8] = T::ID;
type Group = T; type Group = T;
type Hash = OprfHash<T>;
type Hash = T::Hash;
} }
+121 -121
View File
@@ -1,21 +1,17 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! 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::{Digest, Output, OutputSizeUser}; use digest::{Digest, Output, OutputSizeUser};
use generic_array::sequence::Concat; use hybrid_array::typenum::{IsLess, U2, U9, U256, Unsigned};
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U11, U2, U256}; use hybrid_array::{Array, ArrayN, ArraySize};
use generic_array::{ArrayLength, GenericArray}; use rand_core::{TryCryptoRng, TryRng};
use rand_core::{CryptoRng, RngCore};
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@@ -28,14 +24,14 @@ use crate::{CipherSuite, Error, Group, InternalError, Result};
/////////////// ///////////////
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize"; pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
pub(crate) const STR_SEED: [u8; 5] = *b"Seed-"; pub(crate) const STR_SEED: ArrayN<u8, 5> = Array(*b"Seed-");
pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair"; pub(crate) const STR_DERIVE_KEYPAIR: ArrayN<u8, 13> = Array(*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: ArrayN<u8, 13> = Array(*b"HashToScalar-");
pub(crate) const STR_HASH_TO_GROUP: [u8; 12] = *b"HashToGroup-"; pub(crate) const STR_HASH_TO_GROUP: ArrayN<u8, 12> = Array(*b"HashToGroup-");
/// Determines the mode of operation (either base mode or verifiable mode). This /// Determines the mode of operation (either base mode or verifiable mode). This
/// is only used for custom implementations for [`Group`]. /// is only used for custom implementations for [`Group`].
@@ -72,15 +68,12 @@ 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>"))]
pub(crate) <CS::Group as Group>::Elem, pub(crate) <CS::Group as Group>::Elem,
) );
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
/// The server's response to the [BlindedElement] message from a client (either /// The server's response to the [BlindedElement] message from a client (either
/// verifiable or not) to a server (either verifiable or not). /// verifiable or not) to a server (either verifiable or not).
@@ -89,15 +82,12 @@ 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>"))]
pub(crate) <CS::Group as Group>::Elem, pub(crate) <CS::Group as Group>::Elem,
) );
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
/// Contains prepared [`EvaluationElement`]s by a server batch evaluate /// Contains prepared [`EvaluationElement`]s by a server batch evaluate
/// preparation. /// preparation.
@@ -106,12 +96,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
/// A proof produced by a server that the OPRF output matches against a server /// A proof produced by a server that the OPRF output matches against a server
/// public key. /// public key.
@@ -120,13 +107,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) c_scalar: <CS::Group as Group>::Scalar, pub(crate) c_scalar: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
@@ -140,24 +123,20 @@ where
/// Can only fail with [`Error::Batch`]. /// Can only fail with [`Error::Batch`].
#[allow(clippy::many_single_char_names)] #[allow(clippy::many_single_char_names)]
pub(crate) fn generate_proof<CS: CipherSuite, R: RngCore + CryptoRng>( pub(crate) fn generate_proof<CS: CipherSuite, R: TryRng + TryCryptoRng>(
rng: &mut R, rng: &mut R,
k: <CS::Group as Group>::Scalar, k: <CS::Group as Group>::Scalar,
a: <CS::Group as Group>::Elem, a: <CS::Group as Group>::Elem,
b: <CS::Group as Group>::Elem, b: <CS::Group as Group>::Elem,
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator, cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator, ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
mode: Mode, mode: Mode,
) -> Result<Proof<CS>> ) -> Result<Proof<CS>> {
where // https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.1
let (m, z) = compute_composites::<CS, _, _>(Some(k), b, cs, ds, mode)?; let (m, z) = compute_composites::<CS, _, _>(Some(k), b, cs, ds, mode)?;
let r = CS::Group::random_scalar(rng); let r = CS::Group::random_scalar(rng)?;
let t2 = a * &r; let t2 = a * &r;
let t3 = m * &r; let t3 = m * &r;
@@ -194,9 +173,9 @@ where
&STR_CHALLENGE, &STR_CHALLENGE,
]; ];
let dst = GenericArray::from(STR_HASH_TO_SCALAR).concat(create_context_string::<CS>(mode)); let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, mode);
// This can't fail, the size of the `input` is known. // This can't fail, the size of the `input` is known.
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst).unwrap(); let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
let s_scalar = r - &(c_scalar * &k); let s_scalar = r - &(c_scalar * &k);
Ok(Proof { c_scalar, s_scalar }) Ok(Proof { c_scalar, s_scalar })
@@ -207,16 +186,12 @@ where
pub(crate) fn verify_proof<CS: CipherSuite>( pub(crate) fn verify_proof<CS: CipherSuite>(
a: <CS::Group as Group>::Elem, a: <CS::Group as Group>::Elem,
b: <CS::Group as Group>::Elem, b: <CS::Group as Group>::Elem,
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator, cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator, ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
proof: &Proof<CS>, proof: &Proof<CS>,
mode: Mode, mode: Mode,
) -> Result<()> ) -> Result<()> {
where // https://www.rfc-editor.org/rfc/rfc9497#section-2.2.2
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.2
let (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?; let (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?;
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar); let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar); let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
@@ -254,9 +229,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(()),
@@ -280,12 +255,8 @@ fn compute_composites<
c_slice: IC, c_slice: IC,
d_slice: ID, d_slice: ID,
mode: Mode, mode: Mode,
) -> Result<ComputeCompositesResult<CS>> ) -> Result<ComputeCompositesResult<CS>> {
where // https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
// https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#section-2.2.1
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes(); let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
@@ -296,18 +267,18 @@ 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::<<CS::Hash as OutputSizeUser>::OutputSize>();
let mut m = CS::Group::identity_elem(); let mut m = CS::Group::identity_elem();
let mut z = CS::Group::identity_elem(); let mut z = CS::Group::identity_elem();
@@ -332,9 +303,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,
@@ -360,13 +331,8 @@ pub(crate) fn derive_key_internal<CS: CipherSuite>(
seed: &[u8], seed: &[u8],
info: &[u8], info: &[u8],
mode: Mode, mode: Mode,
) -> Result<<CS::Group as Group>::Scalar, Error> ) -> Result<<CS::Group as Group>::Scalar, Error> {
where let dst = Dst::new::<CS, _>(STR_DERIVE_KEYPAIR, mode);
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let context_string = create_context_string::<CS>(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 +342,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,17 +354,18 @@ 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],
info: &[u8], info: &[u8],
mode: Mode, mode: Mode,
) -> Result<<CS::Group as Group>::Scalar, Error> ) -> Result<<CS::Group as Group>::Scalar, Error> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
derive_key_internal::<CS>(seed, info, mode) derive_key_internal::<CS>(seed, info, mode)
} }
@@ -412,11 +379,7 @@ pub(crate) fn derive_keypair<CS: CipherSuite>(
seed: &[u8], seed: &[u8],
info: &[u8], info: &[u8],
mode: Mode, mode: Mode,
) -> Result<DeriveKeypairResult<CS>, Error> ) -> Result<DeriveKeypairResult<CS>, Error> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let sk_s = derive_key_internal::<CS>(seed, info, mode)?; let sk_s = derive_key_internal::<CS>(seed, info, mode)?;
let pk_s = CS::Group::base_elem() * &sk_s; let pk_s = CS::Group::base_elem() * &sk_s;
@@ -432,12 +395,14 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
input: &[u8], input: &[u8],
blind: &<CS::Group as Group>::Scalar, blind: &<CS::Group as Group>::Scalar,
mode: Mode, mode: Mode,
) -> Result<<CS::Group as Group>::Elem> ) -> Result<<CS::Group as Group>::Elem> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let hashed_point = hash_to_group::<CS>(input, mode)?; let hashed_point = hash_to_group::<CS>(input, mode)?;
// Identity element would nullify blinding, revealing the input.
if CS::Group::is_identity_elem(hashed_point).into() {
return Err(Error::Input);
}
Ok(hashed_point * blind) Ok(hashed_point * blind)
} }
@@ -445,13 +410,9 @@ where
pub(crate) fn hash_to_group<CS: CipherSuite>( pub(crate) fn hash_to_group<CS: CipherSuite>(
input: &[u8], input: &[u8],
mode: Mode, mode: Mode,
) -> Result<<CS::Group as Group>::Elem> ) -> Result<<CS::Group as Group>::Elem> {
where let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
<CS::Hash as OutputSizeUser>::OutputSize: CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input)
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
CS::Group::hash_to_curve::<CS::Hash>(&[input], &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.
@@ -459,12 +420,8 @@ where
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>( pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
input: &[u8], input: &[u8],
info: Option<&[u8]>, info: Option<&[u8]>,
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>, issued_element: Array<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
) -> Result<Output<CS::Hash>> ) -> Result<Output<CS::Hash>> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
// OPRF & VOPRF // OPRF & VOPRF
// hashInput = I2OSP(len(input), 2) || input || // hashInput = I2OSP(len(input), 2) || input ||
// I2OSP(len(issuedElement), 2) || issuedElement || // I2OSP(len(issuedElement), 2) || issuedElement ||
@@ -486,22 +443,67 @@ where
.chain_update(info.as_ref()); .chain_update(info.as_ref());
} }
Ok(hash Ok(hash
.chain_update(i2osp_2(issued_element.as_ref().len()).map_err(|_| Error::Input)?) .chain_update(i2osp_2(issued_element.as_slice().len()).map_err(|_| Error::Input)?)
.chain_update(issued_element) .chain_update(issued_element)
.chain_update(STR_FINALIZE) .chain_update(STR_FINALIZE)
.finalize()) .finalize())
} }
/// Generates the contextString parameter as defined in pub(crate) struct Dst<L: ArraySize> {
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/> dst_1: Array<u8, L>,
pub(crate) fn create_context_string<CS: CipherSuite>(mode: Mode) -> GenericArray<u8, U11> dst_2: &'static [u8],
}
impl<L: ArraySize> Dst<L> {
pub(crate) fn new<CS, TL>(par_1: Array<u8, TL>, mode: Mode) -> Self
where
CS: CipherSuite,
TL: ArraySize + Add<U9, Output = L>,
{
// Generates the contextString parameter as defined in
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
let par_2 = ArrayN::<u8, 7>::from(STR_OPRF)
.concat(ArrayN::<u8, 1>::from([mode.to_u8()]))
.concat(ArrayN::<u8, 1>::from([b'-']));
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]
}
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
}
} }
/////////////////////// ///////////////////////
@@ -515,8 +517,6 @@ pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
.map_err(|_| InternalError::I2osp) .map_err(|_| InternalError::I2osp)
} }
pub(crate) fn i2osp_2_array<L: ArrayLength<u8> + IsLess<U256>>( pub(crate) fn i2osp_2_array<L: ArraySize + IsLess<U256>>() -> Array<u8, U2> {
_: &GenericArray<u8, L>,
) -> GenericArray<u8, U2> {
L::U16.to_be_bytes().into() L::U16.to_be_bytes().into()
} }
+7 -11
View File
@@ -1,19 +1,14 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Errors which are produced during an execution of the protocol //! Errors which are produced during an execution of the protocol
use displaydoc::Display;
/// [`Result`](core::result::Result) shorthand that uses [`Error`]. /// [`Result`](core::result::Result) shorthand that uses [`Error`].
pub type Result<T, E = Error> = core::result::Result<T, E>; pub type Result<T, E = Error> = core::result::Result<T, E>;
/// Represents an error in the manipulation of internal cryptographic data /// Represents an error in the manipulation of internal cryptographic data
#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, displaydoc::Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Error { pub enum Error {
/// Size of info is longer then [`u16::MAX`]. /// Size of info is longer then [`u16::MAX`].
Info, Info,
@@ -29,6 +24,8 @@ pub enum Error {
ProofVerification, ProofVerification,
/// The protocol has failed and can't be completed. /// The protocol has failed and can't be completed.
Protocol, Protocol,
/// Random number generator failure.
Rng,
} }
/// Only used to implement [`Group`](crate::Group). /// Only used to implement [`Group`](crate::Group).
@@ -40,5 +37,4 @@ pub enum InternalError {
I2osp, I2osp,
} }
#[cfg(feature = "std")] impl core::error::Error for Error {}
impl std::error::Error for Error {}
+66 -40
View File
@@ -1,58 +1,73 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
use digest::core_api::BlockSizeUser; use core::ops::{Add, Mul};
use digest::Digest; use digest::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256};
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::sec1::{FromSec1Point, ModulusSize, ToSec1Point};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::{ use elliptic_curve::{
AffinePoint, Field, FieldSize, Group as _, ProjectivePoint, PublicKey, Scalar, SecretKey, AffinePoint, Field, FieldBytes, FieldBytesSize, Group as _, ProjectivePoint, PublicKey, Scalar,
SecretKey,
}; };
use generic_array::typenum::{IsLess, IsLessOrEqual, U256}; use hash2curve::{ExpandMsgXmd, GroupDigest, MapToCurve, hash_to_scalar};
use generic_array::GenericArray; use hybrid_array::typenum::{IsGreaterOrEqual, Prod, Sum, True, U2};
use rand_core::{CryptoRng, RngCore}; use hybrid_array::{Array, ArraySize};
use rand_core::TryCryptoRng;
use super::Group; use super::Group;
use crate::{Error, InternalError, Result}; use crate::{Error, InternalError, Result};
type ElemLen<C> = <ScalarLen<C> as ModulusSize>::CompressedPointSize;
type ScalarLen<C> = FieldBytesSize<C>;
impl<C> Group for C impl<C> Group for C
where where
C: GroupDigest, C: GroupDigest,
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>, C::SecurityLevel: Mul<U2>,
FieldSize<Self>: ModulusSize, C::SecurityLevel: ArraySize,
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>, <C::SecurityLevel as Mul<U2>>::Output: ArraySize,
Scalar<Self>: FromOkm, ProjectivePoint<Self>: CofactorGroup + ToSec1Point<Self>,
ScalarLen<Self>: ModulusSize,
ScalarLen<Self>: ArraySize,
ScalarLen<Self>: hybrid_array::typenum::NonZero,
Scalar<Self>: elliptic_curve::ops::Reduce<Array<u8, <C as MapToCurve>::Length>>,
AffinePoint<Self>: FromSec1Point<Self> + ToSec1Point<Self>,
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
ScalarLen<Self>: Add<ElemLen<Self>>,
Sum<ScalarLen<Self>, ElemLen<Self>>: ArraySize,
// `ProofLen`
ScalarLen<Self>: Add<ScalarLen<Self>>,
Sum<ScalarLen<Self>, ScalarLen<Self>>: ArraySize,
ElemLen<Self>: ArraySize,
{ {
type Elem = ProjectivePoint<Self>; type Elem = ProjectivePoint<Self>;
type ElemLen = <FieldSize<Self> as ModulusSize>::CompressedPointSize; type ElemLen = ElemLen<Self>;
type Scalar = Scalar<Self>; type Scalar = Scalar<Self>;
type ScalarLen = FieldSize<Self>; type ScalarLen = ScalarLen<Self>;
type SecurityLevel = C::SecurityLevel;
// 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://www.rfc-editor.org/rfc/rfc9380.html#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 Self::hash_from_bytes(input, dst).map_err(|_| InternalError::Input)
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
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, Output = True>
+ IsGreaterOrEqual<Prod<C::SecurityLevel, U2>, Output = True>,
{ {
<Self as GroupDigest>::hash_to_scalar::<ExpandMsgXmd<H>>(input, dst) hash_to_scalar::<C, ExpandMsgXmd<H>, <C as MapToCurve>::Length>(input, dst)
.map_err(|_| InternalError::Input) .map_err(|_| InternalError::Input)
} }
@@ -64,10 +79,10 @@ where
ProjectivePoint::<Self>::identity() ProjectivePoint::<Self>::identity()
} }
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> { fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
let bytes = elem.to_encoded_point(true); let bytes = elem.to_sec1_point(true);
let bytes = bytes.as_bytes(); let bytes = bytes.as_bytes();
let mut result = GenericArray::default(); let mut result = Array::default();
result[..bytes.len()].copy_from_slice(bytes); result[..bytes.len()].copy_from_slice(bytes);
result result
} }
@@ -78,8 +93,16 @@ where
.map_err(|_| Error::Deserialization) .map_err(|_| Error::Deserialization)
} }
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar { fn random_scalar<R: TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
*SecretKey::<Self>::random(rng).to_nonzero_scalar() loop {
let mut bytes = FieldBytes::<Self>::default();
rng.try_fill_bytes(&mut bytes).map_err(|_| Error::Rng)?;
if let Ok(key) = SecretKey::<Self>::from_slice(&bytes) {
return Ok(*key.to_nonzero_scalar());
}
}
} }
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar { fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar {
@@ -92,15 +115,18 @@ 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) -> Array<u8, Self::ScalarLen> {
scalar.into() let bytes: FieldBytes<Self> = scalar.into();
let mut result = Array::<u8, Self::ScalarLen>::default();
result.as_mut_slice().copy_from_slice(bytes.as_ref());
result
} }
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)
} }
+45 -25
View File
@@ -1,9 +1,6 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Defines the Group trait to specify the underlying prime order group //! Defines the Group trait to specify the underlying prime order group
@@ -13,11 +10,11 @@ mod ristretto;
use core::ops::{Add, Mul, Sub}; use core::ops::{Add, Mul, Sub};
use digest::core_api::BlockSizeUser; use digest::block_api::BlockSizeUser;
use digest::Digest; use digest::{FixedOutput, HashMarker};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256}; use hybrid_array::typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, Sum, True, U2, U256};
use generic_array::{ArrayLength, GenericArray}; use hybrid_array::{Array, ArraySize};
use rand_core::{CryptoRng, RngCore}; use rand_core::{TryCryptoRng, TryRng};
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
pub use ristretto::Ristretto255; pub use ristretto::Ristretto255;
use subtle::{Choice, ConstantTimeEq}; use subtle::{Choice, ConstantTimeEq};
@@ -26,8 +23,16 @@ use zeroize::Zeroize;
use crate::{InternalError, Result}; use crate::{InternalError, Result};
/// A prime-order subgroup of a base field (EC, prime-order field ...). This /// A prime-order subgroup of a base field (EC, prime-order field ...). This
/// subgroup is noted additively — as in the draft RFC — in this trait. /// subgroup is noted additively — as in the RFC — in this trait.
pub trait Group { pub trait Group
where
// `VoprfClientLen`, `PoprfClientLen`, `VoprfServerLen`, `PoprfServerLen`
Self::ScalarLen: Add<Self::ElemLen>,
Sum<Self::ScalarLen, Self::ElemLen>: ArraySize,
// `ProofLen`
Self::ScalarLen: Add<Self::ScalarLen>,
Sum<Self::ScalarLen, Self::ScalarLen>: ArraySize,
{
/// The type of group elements /// The type of group elements
type Elem: ConstantTimeEq type Elem: ConstantTimeEq
+ Copy + Copy
@@ -36,7 +41,7 @@ pub trait Group {
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>; + for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
/// The byte length necessary to represent group elements /// The byte length necessary to represent group elements
type ElemLen: ArrayLength<u8> + 'static; type ElemLen: ArraySize + 'static;
/// The type of base field scalars /// The type of base field scalars
type Scalar: ConstantTimeEq type Scalar: ConstantTimeEq
@@ -47,27 +52,39 @@ pub trait Group {
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>; + for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
/// The byte length necessary to represent scalars /// The byte length necessary to represent scalars
type ScalarLen: ArrayLength<u8> + 'static; type ScalarLen: ArraySize + 'static;
/// Security parameter `k` in bytes (i.e. `k / 8`), as defined in
/// [RFC 9380 §8](https://www.rfc-editor.org/rfc/rfc9380#section-8).
///
/// Used to enforce `H::OutputSize >= 2 * SecurityLevel` in
/// `hash_to_curve` and `hash_to_scalar`, which corresponds to the
/// `expand_message` requirement `len_in_bytes = 2 * k / 8`.
type SecurityLevel: ArraySize + Mul<U2>;
/// Transforms a password and domain separation tag (DST) into a curve point /// Transforms a password and domain separation tag (DST) into a curve point
/// ///
/// # 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, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
/// Hashes a slice of pseudo-random bytes to a scalar /// Hashes a slice of pseudo-random bytes to a scalar
/// ///
/// # 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, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>;
/// Get the base point for the group /// Get the base point for the group
fn base_elem() -> Self::Elem; fn base_elem() -> Self::Elem;
@@ -81,7 +98,7 @@ pub trait Group {
} }
/// Serializes the `self` group element /// Serializes the `self` group element
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen>; fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen>;
/// Return an element from its fixed-length bytes representation. If the /// Return an element from its fixed-length bytes representation. If the
/// element is the identity element, return an error. /// element is the identity element, return an error.
@@ -91,8 +108,11 @@ pub trait Group {
/// is not a valid point on the group or the identity element. /// is not a valid point on the group or the identity element.
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>; fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
/// picks a scalar at random /// Picks a scalar at random.
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar; ///
/// # Errors
/// [`Error::Rng`](crate::Error::Rng) if the random number generator fails.
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar>;
/// The multiplicative inverse of this scalar /// The multiplicative inverse of this scalar
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar; fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
@@ -105,7 +125,7 @@ pub trait Group {
fn zero_scalar() -> Self::Scalar; fn zero_scalar() -> Self::Scalar;
/// Serializes a scalar to bytes /// Serializes a scalar to bytes
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen>; fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen>;
/// Return a scalar from its fixed-length bytes representation. If the /// Return a scalar from its fixed-length bytes representation. If the
/// scalar is zero or invalid, then return an error. /// scalar is zero or invalid, then return an error.
+66 -52
View File
@@ -1,20 +1,21 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory use core::num::NonZeroU16;
// of this source tree.
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::block_api::BlockSizeUser;
use digest::Digest; use digest::{FixedOutput, HashMarker};
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander}; use hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64}; use hybrid_array::Array;
use generic_array::GenericArray; use hybrid_array::typenum::{
use rand_core::{CryptoRng, RngCore}; IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U16, U32, U256,
};
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq; use subtle::ConstantTimeEq;
use super::Group; use super::Group;
@@ -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 [u8] = b"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;
@@ -46,34 +43,52 @@ impl Group for Ristretto255 {
type ScalarLen = U32; type ScalarLen = U32;
// Implements the `hash_to_ristretto255()` function from type SecurityLevel = U16;
// 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>
where
H: Digest + BlockSizeUser,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
let mut uniform_bytes = GenericArray::<_, U64>::default();
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into())) // Implements the `hash_to_ristretto255()` function from
// https://www.rfc-editor.org/rfc/rfc9380.html#appendix-B
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
{
let mut uniform_bytes = [0u8; 64];
<ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(
input,
dst,
NonZeroU16::new(64).unwrap(),
)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes)
.map_err(|_| InternalError::Input)?;
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
} }
// 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.rfc-editor.org/rfc/rfc9497#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, Output = True>
+ IsGreaterOrEqual<Prod<Self::SecurityLevel, U2>, Output = True>,
{ {
let mut uniform_bytes = GenericArray::<_, U64>::default(); let mut uniform_bytes = [0u8; 64];
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into())) <ExpandMsgXmd<H> as ExpandMsg<U16>>::expand_message(
input,
dst,
NonZeroU16::new(64).unwrap(),
)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes)
.map_err(|_| InternalError::Input)?;
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
} }
fn base_elem() -> Self::Elem { fn base_elem() -> Self::Elem {
@@ -85,27 +100,26 @@ impl Group for Ristretto255 {
} }
// serialization of a group element // serialization of a group element
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> { fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
elem.compress().to_bytes().into() elem.compress().to_bytes().into()
} }
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)
} }
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar { fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self::Scalar> {
loop { loop {
let scalar = Scalar::random(rng); let mut scalar_bytes = [0u8; 32];
rng.try_fill_bytes(&mut scalar_bytes)
.map_err(|_| Error::Rng)?;
if scalar != Scalar::zero() { if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) {
break scalar; break Ok(scalar);
} }
} }
} }
@@ -115,15 +129,15 @@ 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) -> Array<u8, Self::ScalarLen> {
scalar.to_bytes().into() scalar.to_bytes().into()
} }
@@ -131,8 +145,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)
} }
} }
+11 -6
View File
@@ -1,9 +1,6 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Includes a series of tests for the group implementations //! Includes a series of tests for the group implementations
@@ -15,6 +12,8 @@ use crate::{Error, Group, Result};
#[test] #[test]
fn test_group_properties() -> Result<()> { fn test_group_properties() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -27,6 +26,12 @@ fn test_group_properties() -> Result<()> {
test_identity_element_error::<NistP256>()?; test_identity_element_error::<NistP256>()?;
test_zero_scalar_error::<NistP256>()?; test_zero_scalar_error::<NistP256>()?;
test_identity_element_error::<NistP384>()?;
test_zero_scalar_error::<NistP384>()?;
test_identity_element_error::<NistP521>()?;
test_zero_scalar_error::<NistP521>()?;
Ok(()) Ok(())
} }
+89 -106
View File
@@ -1,16 +1,11 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! 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), //! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
//! but this specification is subject to change, until the final version
//! published by the IETF.
//! //!
//! # Overview //! # Overview
//! //!
@@ -21,7 +16,7 @@
//! We will use the following choice in this example: //! We will use the following choice in this example:
//! //!
//! ```ignore //! ```ignore
//! type CipherSuite = voprf::Ristretto255; //! type CipherSuite = voprf_vx::Ristretto255;
//! ``` //! ```
//! //!
//! ## Modes of Operation //! ## Modes of Operation
@@ -52,14 +47,14 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng; //! use rand::rngs::SysRng;
//! use rand::RngCore; //! use rand::Rng;
//! use voprf::OprfServer; //! use voprf_vx::OprfServer;
//! //!
//! let mut server_rng = OsRng; //! let mut server_rng = SysRng;
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng); //! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
//! ``` //! ```
//! //!
@@ -72,14 +67,14 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng; //! use rand::rngs::SysRng;
//! use rand::RngCore; //! use rand::Rng;
//! use voprf::OprfClient; //! use voprf_vx::OprfClient;
//! //!
//! let mut client_rng = OsRng; //! let mut client_rng = SysRng;
//! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng) //! let client_blind_result = OprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
//! .expect("Unable to construct client"); //! .expect("Unable to construct client");
//! ``` //! ```
@@ -93,19 +88,19 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient; //! # use voprf_vx::OprfClient;
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind( //! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input", //! # b"input",
//! # &mut client_rng, //! # &mut client_rng,
//! # ).expect("Unable to construct client"); //! # ).expect("Unable to construct client");
//! # use voprf::OprfServer; //! # use voprf_vx::OprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message); //! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
//! ``` //! ```
@@ -118,19 +113,19 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient; //! # use voprf_vx::OprfClient;
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind( //! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input", //! # b"input",
//! # &mut client_rng, //! # &mut client_rng,
//! # ).expect("Unable to construct client"); //! # ).expect("Unable to construct client");
//! # use voprf::OprfServer; //! # use voprf_vx::OprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message); //! # let message = server.blind_evaluate(&client_blind_result.message);
//! let client_finalize_result = client_blind_result //! let client_finalize_result = client_blind_result
@@ -151,19 +146,19 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient; //! # use voprf_vx::OprfClient;
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind( //! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input", //! # b"input",
//! # &mut client_rng, //! # &mut client_rng,
//! # ).expect("Unable to construct client"); //! # ).expect("Unable to construct client");
//! # use voprf::OprfServer; //! # use voprf_vx::OprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message); //! # let message = server.blind_evaluate(&client_blind_result.message);
//! let client_finalize_result = client_blind_result //! let client_finalize_result = client_blind_result
@@ -198,14 +193,14 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng; //! use rand::rngs::SysRng;
//! use rand::RngCore; //! use rand::Rng;
//! use voprf::VoprfServer; //! use voprf_vx::VoprfServer;
//! //!
//! let mut server_rng = OsRng; //! let mut server_rng = SysRng;
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! //!
//! // To be sent to the client //! // To be sent to the client
@@ -225,14 +220,14 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng; //! use rand::rngs::SysRng;
//! use rand::RngCore; //! use rand::Rng;
//! use voprf::VoprfClient; //! use voprf_vx::VoprfClient;
//! //!
//! let mut client_rng = OsRng; //! let mut client_rng = SysRng;
//! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng) //! let client_blind_result = VoprfClient::<CipherSuite>::blind(b"input", &mut client_rng)
//! .expect("Unable to construct client"); //! .expect("Unable to construct client");
//! ``` //! ```
@@ -247,19 +242,19 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient}; //! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind( //! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input", //! # b"input",
//! # &mut client_rng, //! # &mut client_rng,
//! # ).expect("Unable to construct client"); //! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer; //! # use voprf_vx::VoprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerEvaluateResult { message, proof } = //! let VoprfServerEvaluateResult { message, proof } =
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message); //! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
@@ -274,19 +269,19 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient; //! # use voprf_vx::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind( //! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input", //! # b"input",
//! # &mut client_rng, //! # &mut client_rng,
//! # ).expect("Unable to construct client"); //! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer; //! # use voprf_vx::VoprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate( //! # let server_evaluate_result = server.blind_evaluate(
//! # &mut server_rng, //! # &mut server_rng,
@@ -315,19 +310,19 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient; //! # use voprf_vx::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind( //! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input", //! # b"input",
//! # &mut client_rng, //! # &mut client_rng,
//! # ).expect("Unable to construct client"); //! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer; //! # use voprf_vx::VoprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate( //! # let server_evaluate_result = server.blind_evaluate(
//! # &mut server_rng, //! # &mut server_rng,
@@ -369,13 +364,13 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient; //! # use voprf_vx::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! let mut client_rng = OsRng; //! let mut client_rng = SysRng;
//! let mut client_states = vec![]; //! let mut client_states = vec![];
//! let mut client_messages = vec![]; //! let mut client_messages = vec![];
//! for _ in 0..10 { //! for _ in 0..10 {
@@ -393,13 +388,13 @@
//! //!
//! ``` //! ```
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient}; //! # use voprf_vx::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let mut client_states = vec![]; //! # let mut client_states = vec![];
//! # let mut client_messages = vec![]; //! # let mut client_messages = vec![];
//! # for _ in 0..10 { //! # for _ in 0..10 {
@@ -410,8 +405,8 @@
//! # client_states.push(client_blind_result.state); //! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message); //! # client_messages.push(client_blind_result.message);
//! # } //! # }
//! # use voprf::VoprfServer; //! # use voprf_vx::VoprfServer;
//! let mut server_rng = OsRng; //! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter()); //! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect(); //! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
@@ -427,13 +422,13 @@
//! ``` //! ```
//! # #[cfg(feature = "alloc")] { //! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient}; //! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let mut client_states = vec![]; //! # let mut client_states = vec![];
//! # let mut client_messages = vec![]; //! # let mut client_messages = vec![];
//! # for _ in 0..10 { //! # for _ in 0..10 {
@@ -444,8 +439,8 @@
//! # client_states.push(client_blind_result.state); //! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message); //! # client_messages.push(client_blind_result.message);
//! # } //! # }
//! # use voprf::VoprfServer; //! # use voprf_vx::VoprfServer;
//! let mut server_rng = OsRng; //! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerBatchEvaluateResult { messages, proof } = server //! let VoprfServerBatchEvaluateResult { messages, proof } = server
//! .batch_blind_evaluate(&mut server_rng, &client_messages) //! .batch_blind_evaluate(&mut server_rng, &client_messages)
@@ -461,13 +456,13 @@
//! ``` //! ```
//! # #[cfg(feature = "alloc")] { //! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")] //! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255; //! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))] //! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256; //! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient}; //! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore}; //! # use rand::{rngs::SysRng, Rng};
//! # //! #
//! # let mut client_rng = OsRng; //! # let mut client_rng = SysRng;
//! # let mut client_states = vec![]; //! # let mut client_states = vec![];
//! # let mut client_messages = vec![]; //! # let mut client_messages = vec![];
//! # for _ in 0..10 { //! # for _ in 0..10 {
@@ -478,8 +473,8 @@
//! # client_states.push(client_blind_result.state); //! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message); //! # client_messages.push(client_blind_result.message);
//! # } //! # }
//! # use voprf::VoprfServer; //! # use voprf_vx::VoprfServer;
//! # let mut server_rng = OsRng; //! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap(); //! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server //! # let VoprfServerBatchEvaluateResult { messages, proof } = server
//! # .batch_blind_evaluate(&mut server_rng, &client_messages) //! # .batch_blind_evaluate(&mut server_rng, &client_messages)
@@ -511,7 +506,7 @@
//! and [PoprfClient] are used, and that each of the functions accept an //! and [PoprfClient] are used, and that each of the functions accept an
//! additional (and optional) info parameter which represents the public input. //! additional (and optional) info parameter which represents the public input.
//! See //! See
//! <https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-11.html#name-poprf-public-input> //! <https://www.rfc-editor.org/rfc/rfc9497#name-poprf-public-input>
//! for more detailed information on how this public input should be used. //! for more detailed information on how this public input should be used.
//! //!
//! # Features //! # Features
@@ -532,24 +527,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_cfg))]
#![cfg_attr(not(test), deny(unsafe_code))]
#![warn( #![warn(
clippy::cargo, clippy::cargo,
clippy::missing_errors_doc, clippy::missing_errors_doc,
@@ -564,9 +550,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;
+59 -96
View File
@@ -1,24 +1,20 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Contains the main OPRF API //! Contains the main OPRF API
use core::iter::{self, Map}; use core::iter::{self, Map};
use derive_where::derive_where; use derive_where::derive_where;
use digest::core_api::BlockSizeUser; use digest::{Digest, Output};
use digest::{Digest, Output, OutputSizeUser}; use hybrid_array::Array;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use hybrid_array::typenum::Unsigned;
use generic_array::GenericArray; use rand_core::{TryCryptoRng, TryRng};
use rand_core::{CryptoRng, RngCore};
use crate::common::{ use crate::common::{
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2, BlindedElement, EvaluationElement, Mode, STR_FINALIZE, derive_key_internal,
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE, deterministic_blind_unchecked, hash_to_group, i2osp_2, server_evaluate_hash_input,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::serialization::serde::Scalar; use crate::serialization::serde::Scalar;
@@ -41,13 +37,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) blind: <CS::Group as Group>::Scalar, pub(crate) blind: <CS::Group as Group>::Scalar,
} }
@@ -59,13 +51,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) sk: <CS::Group as Group>::Scalar, pub(crate) sk: <CS::Group as Group>::Scalar,
} }
@@ -75,21 +63,17 @@ where
// =================== // // =================== //
///////////////////////// /////////////////////////
impl<CS: CipherSuite> OprfClient<CS> impl<CS: CipherSuite> OprfClient<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Computes the first step for the multiplicative blinding version of /// Computes the first step for the multiplicative blinding version of
/// DH-OPRF. /// DH-OPRF.
/// ///
/// # Errors /// # Errors
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
pub fn blind<R: RngCore + CryptoRng>( pub fn blind<R: TryRng + TryCryptoRng>(
input: &[u8], input: &[u8],
blinding_factor_rng: &mut R, blinding_factor_rng: &mut R,
) -> Result<OprfClientBlindResult<CS>> { ) -> Result<OprfClientBlindResult<CS>> {
let blind = CS::Group::random_scalar(blinding_factor_rng); let blind = CS::Group::random_scalar(blinding_factor_rng)?;
Self::deterministic_blind_unchecked_inner(input, blind) Self::deterministic_blind_unchecked_inner(input, blind)
} }
@@ -153,18 +137,14 @@ where
} }
} }
impl<CS: CipherSuite> OprfServer<CS> impl<CS: CipherSuite> OprfServer<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Produces a new instance of a [OprfServer] using a supplied RNG /// Produces a new instance of a [OprfServer] using a supplied RNG
/// ///
/// # Errors /// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed. /// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> { pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default(); let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.fill_bytes(&mut seed); rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
Self::new_from_seed(&seed, &[]) Self::new_from_seed(&seed, &[])
} }
@@ -193,7 +173,7 @@ where
Ok(Self { sk }) Ok(Self { sk })
} }
// Only used for tests /// Only used for tests
#[cfg(test)] #[cfg(test)]
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar { pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
self.sk self.sk
@@ -230,11 +210,7 @@ where
/// Contains the fields that are returned by a non-verifiable client blind /// Contains the fields that are returned by a non-verifiable client blind
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct OprfClientBlindResult<CS: CipherSuite> pub struct OprfClientBlindResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The state to be persisted on the client /// The state to be persisted on the client
pub state: OprfClient<CS>, pub state: OprfClient<CS>,
/// The message to send to the server /// The message to send to the server
@@ -260,11 +236,7 @@ fn finalize_after_unblind<
>( >(
inputs_and_unblinded_elements: IE, inputs_and_unblinded_elements: IE,
_unused: &'a [u8], _unused: &'a [u8],
) -> FinalizeAfterUnblindResult<CS, I, IE> ) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
inputs_and_unblinded_elements.map(|(input, unblinded_element)| { inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes(); let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
@@ -277,7 +249,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,25 +263,21 @@ where
mod tests { mod tests {
use core::ptr; use core::ptr;
use generic_array::sequence::Concat; use rand::TryRng;
use rand::rngs::OsRng; use rand::rngs::SysRng;
use super::*; use super::*;
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
use crate::Group; use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
fn prf<CS: CipherSuite>( fn prf<CS: CipherSuite>(
input: &[u8], input: &[u8],
key: <CS::Group as Group>::Scalar, key: <CS::Group as Group>::Scalar,
info: &[u8], info: &[u8],
mode: Mode, mode: Mode,
) -> Output<CS::Hash> ) -> Output<CS::Hash> {
where let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
<CS::Hash as OutputSizeUser>::OutputSize: let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
let res = point * &key; let res = point * &key;
@@ -319,13 +287,9 @@ mod tests {
.unwrap() .unwrap()
} }
fn base_retrieval<CS: CipherSuite>() fn base_retrieval<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap(); let server = OprfServer::<CS>::new(&mut rng).unwrap();
let message = server.blind_evaluate(&client_blind_result.message); let message = server.blind_evaluate(&client_blind_result.message);
@@ -334,23 +298,18 @@ mod tests {
assert_eq!(client_finalize_result, res2); assert_eq!(client_finalize_result, res2);
} }
fn base_inversion_unsalted<CS: CipherSuite>() fn base_inversion_unsalted<CS: CipherSuite>() {
where let mut rng = SysRng;
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let mut rng = OsRng;
let mut input = [0u8; 64]; let mut input = [0u8; 64];
rng.fill_bytes(&mut input); rng.try_fill_bytes(&mut input).unwrap();
let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap(); let client_blind_result = OprfClient::<CS>::blind(&input, &mut rng).unwrap();
let client_finalize_result = client_blind_result let client_finalize_result = client_blind_result
.state .state
.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()
@@ -359,13 +318,9 @@ mod tests {
assert_eq!(client_finalize_result, res2); assert_eq!(client_finalize_result, res2);
} }
fn server_evaluate<CS: CipherSuite>() fn server_evaluate<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap(); let server = OprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&client_blind_result.message); let server_result = server.blind_evaluate(&client_blind_result.message);
@@ -387,13 +342,9 @@ mod tests {
assert!(client_finalize != server_evaluate); assert!(client_finalize != server_evaluate);
} }
fn zeroize_oprf_client<CS: CipherSuite>() fn zeroize_oprf_client<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state; let mut state = client_blind_result.state;
@@ -405,13 +356,9 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0)); assert!(message.serialize().iter().all(|&x| x == 0));
} }
fn zeroize_oprf_server<CS: CipherSuite>() fn zeroize_oprf_server<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap(); let server = OprfServer::<CS>::new(&mut rng).unwrap();
let mut message = server.blind_evaluate(&client_blind_result.message); let mut message = server.blind_evaluate(&client_blind_result.message);
@@ -427,6 +374,8 @@ mod tests {
#[test] #[test]
fn test_functionality() -> Result<()> { fn test_functionality() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -447,6 +396,20 @@ mod tests {
zeroize_oprf_client::<NistP256>(); zeroize_oprf_client::<NistP256>();
zeroize_oprf_server::<NistP256>(); zeroize_oprf_server::<NistP256>();
base_retrieval::<NistP384>();
base_inversion_unsalted::<NistP384>();
server_evaluate::<NistP384>();
zeroize_oprf_client::<NistP384>();
zeroize_oprf_server::<NistP384>();
base_retrieval::<NistP521>();
base_inversion_unsalted::<NistP521>();
server_evaluate::<NistP521>();
zeroize_oprf_client::<NistP521>();
zeroize_oprf_server::<NistP521>();
Ok(()) Ok(())
} }
} }
+81 -159
View File
@@ -1,9 +1,6 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Contains the main POPRF API //! Contains the main POPRF API
@@ -12,18 +9,15 @@ use alloc::vec::Vec;
use core::iter::{self, Map, Repeat, Zip}; use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where; use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser}; use digest::{Digest, Output, OutputSizeUser};
use generic_array::sequence::Concat; use hybrid_array::typenum::Unsigned;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use hybrid_array::{Array, ArraySize};
use generic_array::GenericArray; use rand_core::{TryCryptoRng, TryRng};
use rand_core::{CryptoRng, RngCore};
use crate::common::{ use crate::common::{
create_context_string, derive_keypair, deterministic_blind_unchecked, generate_proof, BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof, BlindedElement, STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
STR_INFO,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar}; use crate::serialization::serde::{Element, Scalar};
@@ -41,13 +35,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) blind: <CS::Group as Group>::Scalar, pub(crate) blind: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -61,13 +51,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) sk: <CS::Group as Group>::Scalar, pub(crate) sk: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -79,21 +65,17 @@ where
// =================== // // =================== //
///////////////////////// /////////////////////////
impl<CS: CipherSuite> PoprfClient<CS> impl<CS: CipherSuite> PoprfClient<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Computes the first step for the multiplicative blinding version of /// Computes the first step for the multiplicative blinding version of
/// DH-OPRF. /// DH-OPRF.
/// ///
/// # Errors /// # Errors
/// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`]. /// [`Error::Input`] if the `input` is empty or longer than [`u16::MAX`].
pub fn blind<R: RngCore + CryptoRng>( pub fn blind<R: TryRng + TryCryptoRng>(
input: &[u8], input: &[u8],
blinding_factor_rng: &mut R, blinding_factor_rng: &mut R,
) -> Result<PoprfClientBlindResult<CS>> { ) -> Result<PoprfClientBlindResult<CS>> {
let blind = CS::Group::random_scalar(blinding_factor_rng); let blind = CS::Group::random_scalar(blinding_factor_rng)?;
Self::deterministic_blind_unchecked_inner(input, blind) Self::deterministic_blind_unchecked_inner(input, blind)
} }
@@ -146,7 +128,10 @@ where
proof: &Proof<CS>, proof: &Proof<CS>,
pk: <CS::Group as Group>::Elem, pk: <CS::Group as Group>::Elem,
info: Option<&[u8]>, info: Option<&[u8]>,
) -> Result<Output<CS::Hash>> { ) -> Result<Output<CS::Hash>>
where
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{
let clients = core::array::from_ref(self); let clients = core::array::from_ref(self);
let messages = core::array::from_ref(evaluation_element); let messages = core::array::from_ref(evaluation_element);
@@ -181,6 +166,7 @@ where
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>, &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator, <&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{ {
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?; let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
@@ -194,18 +180,14 @@ where
} }
} }
impl<CS: CipherSuite> PoprfServer<CS> impl<CS: CipherSuite> PoprfServer<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Produces a new instance of a [PoprfServer] using a supplied RNG /// Produces a new instance of a [PoprfServer] using a supplied RNG
/// ///
/// # Errors /// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed. /// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> { pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default(); let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.fill_bytes(&mut seed); rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
Self::new_from_seed(&seed, &[]) Self::new_from_seed(&seed, &[])
} }
@@ -236,7 +218,7 @@ where
Ok(Self { sk, pk }) Ok(Self { sk, pk })
} }
// Only used for tests /// Only used for tests
#[cfg(test)] #[cfg(test)]
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar { pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
self.sk self.sk
@@ -249,7 +231,7 @@ where
/// # Errors /// # Errors
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed. /// - [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn blind_evaluate<R: RngCore + CryptoRng>( pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
&self, &self,
rng: &mut R, rng: &mut R,
blinded_element: &BlindedElement<CS>, blinded_element: &BlindedElement<CS>,
@@ -287,7 +269,7 @@ where
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`. /// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed. /// - [`Error::Protocol`] if the protocol fails and can't be completed.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, IE>( pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
&self, &self,
rng: &mut R, rng: &mut R,
blinded_elements: &'a IE, blinded_elements: &'a IE,
@@ -360,7 +342,7 @@ where
pub fn batch_blind_evaluate_finish< pub fn batch_blind_evaluate_finish<
'a, 'a,
'b, 'b,
R: RngCore + CryptoRng, R: TryRng + TryCryptoRng,
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator, IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
IE, IE,
>( >(
@@ -385,7 +367,7 @@ where
tweaked_key, tweaked_key,
prepared_evaluation_elements prepared_evaluation_elements
.into_iter() .into_iter()
.map(|element| element.0 .0), .map(|element| element.0.0),
blinded_elements.map(|element| element.0), blinded_elements.map(|element| element.0),
Mode::Poprf, Mode::Poprf,
)?; )?;
@@ -393,7 +375,7 @@ where
let messages = prepared_evaluation_elements.into_iter().map(<fn( let messages = prepared_evaluation_elements.into_iter().map(<fn(
&PreparedEvaluationElement<CS>, &PreparedEvaluationElement<CS>,
) -> _>::from( ) -> _>::from(
|element| EvaluationElement(element.0 .0), |element| EvaluationElement(element.0.0),
)); ));
Ok(PoprfServerBatchEvaluateFinishResult { messages, proof }) Ok(PoprfServerBatchEvaluateFinishResult { messages, proof })
@@ -428,11 +410,7 @@ where
} }
} }
impl<CS: CipherSuite> BlindedElement<CS> impl<CS: CipherSuite> BlindedElement<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Creates a [BlindedElement] from a raw group element. /// Creates a [BlindedElement] from a raw group element.
/// ///
/// # Caution /// # Caution
@@ -451,11 +429,7 @@ where
} }
} }
impl<CS: CipherSuite> EvaluationElement<CS> impl<CS: CipherSuite> EvaluationElement<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Creates an [EvaluationElement] from a raw group element. /// Creates an [EvaluationElement] from a raw group element.
/// ///
/// # Caution /// # Caution
@@ -481,11 +455,7 @@ where
/// Contains the fields that are returned by a verifiable client blind /// Contains the fields that are returned by a verifiable client blind
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct PoprfClientBlindResult<CS: CipherSuite> pub struct PoprfClientBlindResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The state to be persisted on the client /// The state to be persisted on the client
pub state: PoprfClient<CS>, pub state: PoprfClient<CS>,
/// The message to send to the server /// The message to send to the server
@@ -498,11 +468,7 @@ pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
/// Contains the fields that are returned by a verifiable server evaluate /// Contains the fields that are returned by a verifiable server evaluate
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct PoprfServerEvaluateResult<CS: CipherSuite> pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The message to send to the client /// The message to send to the client
pub message: EvaluationElement<CS>, pub message: EvaluationElement<CS>,
/// The proof for the client to verify /// The proof for the client to verify
@@ -512,11 +478,7 @@ where
/// Contains the fields that are returned by a verifiable server batch evaluate /// Contains the fields that are returned by a verifiable server batch evaluate
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The messages to send to the client /// The messages to send to the client
pub messages: Vec<EvaluationElement<CS>>, pub messages: Vec<EvaluationElement<CS>>,
/// The proof for the client to verify /// The proof for the client to verify
@@ -541,24 +503,17 @@ 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>"))]
<CS::Group as Group>::Scalar, <CS::Group as Group>::Scalar,
) );
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
/// Contains the fields that are returned by a partially verifiable server batch /// Contains the fields that are returned by a partially verifiable server batch
/// evaluate prepare /// evaluate prepare
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)] #[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Prepared [`EvaluationElement`]. /// Prepared [`EvaluationElement`].
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>, pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
/// Prepared tweak. /// Prepared tweak.
@@ -577,8 +532,6 @@ pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)] #[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I> pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
where where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>, &'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
{ {
/// The [`EvaluationElement`]s to send to the client /// The [`EvaluationElement`]s to send to the client
@@ -599,11 +552,7 @@ where
fn compute_tweaked_key<CS: CipherSuite>( fn compute_tweaked_key<CS: CipherSuite>(
pk: <CS::Group as Group>::Elem, pk: <CS::Group as Group>::Elem,
info: Option<&[u8]>, info: Option<&[u8]>,
) -> Result<<CS::Group as Group>::Elem> ) -> Result<<CS::Group as Group>::Elem> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
// None for info is treated the same as empty bytes // None for info is treated the same as empty bytes
let info = info.unwrap_or_default(); let info = info.unwrap_or_default();
@@ -616,10 +565,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;
@@ -638,11 +586,7 @@ where
fn compute_tweak<CS: CipherSuite>( fn compute_tweak<CS: CipherSuite>(
sk: <CS::Group as Group>::Scalar, sk: <CS::Group as Group>::Scalar,
info: Option<&[u8]>, info: Option<&[u8]>,
) -> Result<<CS::Group as Group>::Scalar> ) -> Result<<CS::Group as Group>::Scalar> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
// None for info is treated the same as empty bytes // None for info is treated the same as empty bytes
let info = info.unwrap_or_default(); let info = info.unwrap_or_default();
@@ -654,10 +598,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;
@@ -694,8 +637,6 @@ fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
info: Option<&[u8]>, info: Option<&[u8]>,
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>> ) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
where where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>, &'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>, &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
@@ -723,7 +664,7 @@ where
)?; )?;
Ok(blinds Ok(blinds
.zip(messages.into_iter()) .zip(messages)
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind))) .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
} }
@@ -731,7 +672,7 @@ type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
Zip<Zip<IE, II>, Repeat<&'a [u8]>>, Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
fn( fn(
((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]), ((<<CS as CipherSuite>::Group as Group>::Elem, &[u8]), &[u8]),
) -> Result<GenericArray<u8, <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize>>, ) -> Result<Output<<CS as CipherSuite>::Hash>>,
>; >;
/// Can only fail with [`Error::Batch`] and returned values can only fail with /// Can only fail with [`Error::Batch`] and returned values can only fail with
@@ -747,8 +688,7 @@ fn finalize_after_unblind<
info: Option<&'a [u8]>, info: Option<&'a [u8]>,
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>> ) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
where where
<CS::Hash as OutputSizeUser>::OutputSize: <<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{ {
if unblinded_elements.len() != inputs.len() { if unblinded_elements.len() != inputs.len() {
return Err(Error::Batch); return Err(Error::Batch);
@@ -787,31 +727,24 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::ops::Add;
use core::ptr; use core::ptr;
use generic_array::typenum::Sum; use rand::rngs::SysRng;
use generic_array::ArrayLength;
use rand::rngs::OsRng;
use super::*; use super::*;
use crate::common::STR_HASH_TO_GROUP;
use crate::Group; use crate::Group;
use crate::common::STR_HASH_TO_GROUP;
fn prf<CS: CipherSuite>( fn prf<CS: CipherSuite>(
input: &[u8], input: &[u8],
key: <CS::Group as Group>::Scalar, key: <CS::Group as Group>::Scalar,
info: &[u8], info: &[u8],
mode: Mode, mode: Mode,
) -> Output<CS::Hash> ) -> Output<CS::Hash> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
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);
@@ -823,14 +756,10 @@ mod tests {
.unwrap() .unwrap()
} }
fn verifiable_retrieval<CS: CipherSuite>() fn verifiable_retrieval<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let info = b"info"; let info = b"info";
let mut rng = OsRng; let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap(); let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server let server_result = server
@@ -850,24 +779,19 @@ mod tests {
assert_eq!(client_finalize_result, res2); assert_eq!(client_finalize_result, res2);
} }
fn verifiable_bad_public_key<CS: CipherSuite>() fn verifiable_bad_public_key<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let info = b"info"; let info = b"info";
let mut rng = OsRng; let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap(); let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server let server_result = server
.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,
@@ -879,14 +803,10 @@ mod tests {
assert!(client_finalize_result.is_err()); assert!(client_finalize_result.is_err());
} }
fn verifiable_server_evaluate<CS: CipherSuite>() fn verifiable_server_evaluate<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let info = Some(b"info".as_slice()); let info = Some(b"info".as_slice());
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = PoprfServer::<CS>::new(&mut rng).unwrap(); let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server let server_result = server
@@ -916,15 +836,9 @@ mod tests {
assert!(client_finalize != server_evaluate); assert!(client_finalize != server_evaluate);
} }
fn zeroize_verifiable_client<CS: CipherSuite>() fn zeroize_verifiable_client<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state; let mut state = client_blind_result.state;
@@ -936,18 +850,10 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0)); assert!(message.serialize().iter().all(|&x| x == 0));
} }
fn zeroize_verifiable_server<CS: CipherSuite>() fn zeroize_verifiable_server<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
{
let input = b"input"; let input = b"input";
let info = b"info"; let info = b"info";
let mut rng = OsRng; let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap(); let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server let server_result = server
@@ -970,6 +876,8 @@ mod tests {
#[test] #[test]
fn test_functionality() -> Result<()> { fn test_functionality() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -990,6 +898,20 @@ mod tests {
zeroize_verifiable_client::<NistP256>(); zeroize_verifiable_client::<NistP256>();
zeroize_verifiable_server::<NistP256>(); zeroize_verifiable_server::<NistP256>();
verifiable_retrieval::<NistP384>();
verifiable_bad_public_key::<NistP384>();
verifiable_server_evaluate::<NistP384>();
zeroize_verifiable_client::<NistP384>();
zeroize_verifiable_server::<NistP384>();
verifiable_retrieval::<NistP521>();
verifiable_bad_public_key::<NistP521>();
verifiable_server_evaluate::<NistP521>();
zeroize_verifiable_client::<NistP521>();
zeroize_verifiable_server::<NistP521>();
Ok(()) Ok(())
} }
} }
+198 -93
View File
@@ -1,20 +1,12 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! 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
use core::ops::Add; use hybrid_array::Array;
use hybrid_array::typenum::{Sum, Unsigned};
use digest::core_api::BlockSizeUser;
use digest::OutputSizeUser;
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, Unsigned, U256};
use generic_array::{ArrayLength, GenericArray};
use crate::{ use crate::{
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer, BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
@@ -29,13 +21,9 @@ use crate::{
/// Length of [`OprfClient`] in bytes for serialization. /// Length of [`OprfClient`] in bytes for serialization.
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen; pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> OprfClient<CS> impl<CS: CipherSuite> OprfClient<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> { pub fn serialize(&self) -> Array<u8, OprfClientLen<CS>> {
CS::Group::serialize_scalar(self.blind) CS::Group::serialize_scalar(self.blind)
} }
@@ -46,6 +34,10 @@ where
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?; let blind = deserialize_scalar::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { blind }) Ok(Self { blind })
} }
} }
@@ -56,17 +48,9 @@ pub type VoprfClientLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ElemLen, <<CS as CipherSuite>::Group as Group>::ElemLen,
>; >;
impl<CS: CipherSuite> VoprfClient<CS> impl<CS: CipherSuite> VoprfClient<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, VoprfClientLen<CS>> pub fn serialize(&self) -> Array<u8, VoprfClientLen<CS>> {
where
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
VoprfClientLen<CS>: ArrayLength<u8>,
{
<CS::Group as Group>::serialize_scalar(self.blind) <CS::Group as Group>::serialize_scalar(self.blind)
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element)) .concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
} }
@@ -79,6 +63,10 @@ where
let blind = deserialize_scalar::<CS::Group>(&mut input)?; let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?; let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { Ok(Self {
blind, blind,
blinded_element, blinded_element,
@@ -92,17 +80,9 @@ pub type PoprfClientLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ElemLen, <<CS as CipherSuite>::Group as Group>::ElemLen,
>; >;
impl<CS: CipherSuite> PoprfClient<CS> impl<CS: CipherSuite> PoprfClient<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, PoprfClientLen<CS>> pub fn serialize(&self) -> Array<u8, PoprfClientLen<CS>> {
where
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
PoprfClientLen<CS>: ArrayLength<u8>,
{
<CS::Group as Group>::serialize_scalar(self.blind) <CS::Group as Group>::serialize_scalar(self.blind)
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element)) .concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
} }
@@ -115,6 +95,10 @@ where
let blind = deserialize_scalar::<CS::Group>(&mut input)?; let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?; let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { Ok(Self {
blind, blind,
blinded_element, blinded_element,
@@ -125,13 +109,9 @@ where
/// Length of [`OprfServer`] in bytes for serialization. /// Length of [`OprfServer`] in bytes for serialization.
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen; pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> OprfServer<CS> impl<CS: CipherSuite> OprfServer<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> { pub fn serialize(&self) -> Array<u8, OprfServerLen<CS>> {
CS::Group::serialize_scalar(self.sk) CS::Group::serialize_scalar(self.sk)
} }
@@ -142,6 +122,10 @@ where
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?; let sk = deserialize_scalar::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { sk }) Ok(Self { sk })
} }
} }
@@ -152,17 +136,9 @@ pub type VoprfServerLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ElemLen, <<CS as CipherSuite>::Group as Group>::ElemLen,
>; >;
impl<CS: CipherSuite> VoprfServer<CS> impl<CS: CipherSuite> VoprfServer<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, VoprfServerLen<CS>> pub fn serialize(&self) -> Array<u8, VoprfServerLen<CS>> {
where
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
VoprfServerLen<CS>: ArrayLength<u8>,
{
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk)) CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
} }
@@ -174,6 +150,10 @@ where
let sk = deserialize_scalar::<CS::Group>(&mut input)?; let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?; let pk = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { sk, pk }) Ok(Self { sk, pk })
} }
} }
@@ -184,17 +164,9 @@ pub type PoprfServerLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ElemLen, <<CS as CipherSuite>::Group as Group>::ElemLen,
>; >;
impl<CS: CipherSuite> PoprfServer<CS> impl<CS: CipherSuite> PoprfServer<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, PoprfServerLen<CS>> pub fn serialize(&self) -> Array<u8, PoprfServerLen<CS>> {
where
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
PoprfServerLen<CS>: ArrayLength<u8>,
{
CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk)) CS::Group::serialize_scalar(self.sk).concat(CS::Group::serialize_elem(self.pk))
} }
@@ -206,6 +178,10 @@ where
let sk = deserialize_scalar::<CS::Group>(&mut input)?; let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?; let pk = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { sk, pk }) Ok(Self { sk, pk })
} }
} }
@@ -216,17 +192,9 @@ pub type ProofLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen, <<CS as CipherSuite>::Group as Group>::ScalarLen,
>; >;
impl<CS: CipherSuite> Proof<CS> impl<CS: CipherSuite> Proof<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, ProofLen<CS>> pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
where
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
ProofLen<CS>: ArrayLength<u8>,
{
CS::Group::serialize_scalar(self.c_scalar) CS::Group::serialize_scalar(self.c_scalar)
.concat(CS::Group::serialize_scalar(self.s_scalar)) .concat(CS::Group::serialize_scalar(self.s_scalar))
} }
@@ -239,6 +207,10 @@ where
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?; let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?; let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Proof { c_scalar, s_scalar }) Ok(Proof { c_scalar, s_scalar })
} }
} }
@@ -246,13 +218,9 @@ where
/// Length of [`BlindedElement`] in bytes for serialization. /// Length of [`BlindedElement`] in bytes for serialization.
pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen; pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
impl<CS: CipherSuite> BlindedElement<CS> impl<CS: CipherSuite> BlindedElement<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, BlindedElementLen<CS>> { pub fn serialize(&self) -> Array<u8, BlindedElementLen<CS>> {
CS::Group::serialize_elem(self.0) CS::Group::serialize_elem(self.0)
} }
@@ -263,6 +231,10 @@ where
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?; let value = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self(value)) Ok(Self(value))
} }
} }
@@ -270,13 +242,9 @@ where
/// Length of [`EvaluationElement`] in bytes for serialization. /// Length of [`EvaluationElement`] in bytes for serialization.
pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen; pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
impl<CS: CipherSuite> EvaluationElement<CS> impl<CS: CipherSuite> EvaluationElement<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes /// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, EvaluationElementLen<CS>> { pub fn serialize(&self) -> Array<u8, EvaluationElementLen<CS>> {
CS::Group::serialize_elem(self.0) CS::Group::serialize_elem(self.0)
} }
@@ -287,6 +255,10 @@ where
pub fn deserialize(mut input: &[u8]) -> Result<Self> { pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?; let value = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self(value)) Ok(Self(value))
} }
} }
@@ -306,11 +278,11 @@ fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
} }
trait SliceExt { trait SliceExt {
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self>; fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self>;
} }
impl<T> SliceExt for [T] { impl<T> SliceExt for [T] {
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self> { fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
if take > self.len() { if take > self.len() {
return None; return None;
} }
@@ -325,7 +297,7 @@ impl<T> SliceExt for [T] {
pub(crate) mod serde { pub(crate) mod serde {
use core::marker::PhantomData; use core::marker::PhantomData;
use generic_array::GenericArray; use hybrid_array::Array;
use serde::de::{Deserializer, Error}; use serde::de::{Deserializer, Error};
use serde::ser::Serializer; use serde::ser::Serializer;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -339,7 +311,7 @@ pub(crate) mod serde {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
GenericArray::<_, G::ElemLen>::deserialize(deserializer) Array::<_, G::ElemLen>::deserialize(deserializer)
.and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom)) .and_then(|bytes| G::deserialize_elem(&bytes).map_err(D::Error::custom))
} }
@@ -358,7 +330,7 @@ pub(crate) mod serde {
where where
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
GenericArray::<_, G::ScalarLen>::deserialize(deserializer) Array::<_, G::ScalarLen>::deserialize(deserializer)
.and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom)) .and_then(|bytes| G::deserialize_scalar(&bytes).map_err(D::Error::custom))
} }
@@ -381,6 +353,7 @@ mod test {
VoprfClient, VoprfServer, VoprfClient, VoprfServer,
}; };
// Fuzz: no panics on arbitrary input
macro_rules! test_deserialize { macro_rules! test_deserialize {
($item:ident, $bytes:ident) => { ($item:ident, $bytes:ident) => {
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
@@ -388,10 +361,43 @@ mod test {
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]); let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
} }
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]); let _ = $item::<::p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<::p384::NistP384>::deserialize(&$bytes[..]);
let _ = $item::<::p521::NistP521>::deserialize(&$bytes[..]);
}; };
} }
// Roundtrip: serialize to deserialize == original
macro_rules! test_roundtrip {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let recovered = $item::<$cs>::deserialize(&bytes).expect("roundtrip deserialize");
assert_eq!(original.serialize(), recovered.serialize());
}};
}
// Trailing bytes: valid serialization + extra byte must fail
macro_rules! test_trailing {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let mut extended = bytes.to_vec();
extended.push(0x00);
assert!($item::<$cs>::deserialize(&extended).is_err());
}};
}
// Truncated: valid serialization minus one byte must fail
macro_rules! test_truncated {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let truncated = &bytes[..bytes.len() - 1];
assert!($item::<$cs>::deserialize(truncated).is_err());
}};
}
proptest! { proptest! {
#[test] #[test]
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) { fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
@@ -439,4 +445,103 @@ mod test {
test_deserialize!(Proof, bytes); test_deserialize!(Proof, bytes);
} }
} }
macro_rules! structured_tests {
($cs:ty, $mod:ident) => {
mod $mod {
use super::*;
use rand::rngs::SysRng;
#[test]
fn roundtrip_oprf_client() {
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_roundtrip!(OprfClient, $cs, client);
}
#[test]
fn roundtrip_oprf_server() {
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
test_roundtrip!(OprfServer, $cs, server);
}
#[test]
fn roundtrip_voprf_client() {
let client = VoprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_roundtrip!(VoprfClient, $cs, client);
}
#[test]
fn roundtrip_voprf_server() {
let server = VoprfServer::<$cs>::new(&mut SysRng).expect("new");
test_roundtrip!(VoprfServer, $cs, server);
}
#[test]
fn roundtrip_poprf_client() {
let client = PoprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_roundtrip!(PoprfClient, $cs, client);
}
#[test]
fn roundtrip_poprf_server() {
let server = PoprfServer::<$cs>::new(&mut SysRng).expect("new");
test_roundtrip!(PoprfServer, $cs, server);
}
#[test]
fn trailing_oprf_client() {
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_trailing!(OprfClient, $cs, client);
}
#[test]
fn trailing_oprf_server() {
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
test_trailing!(OprfServer, $cs, server);
}
#[test]
fn truncated_oprf_client() {
let client = OprfClient::<$cs>::blind(b"input", &mut SysRng)
.expect("blind")
.state;
test_truncated!(OprfClient, $cs, client);
}
#[test]
fn truncated_oprf_server() {
let server = OprfServer::<$cs>::new(&mut SysRng).expect("new");
test_truncated!(OprfServer, $cs, server);
}
#[test]
fn empty_input_fails() {
assert!(OprfClient::<$cs>::deserialize(&[]).is_err());
assert!(OprfServer::<$cs>::deserialize(&[]).is_err());
assert!(VoprfClient::<$cs>::deserialize(&[]).is_err());
assert!(VoprfServer::<$cs>::deserialize(&[]).is_err());
assert!(PoprfClient::<$cs>::deserialize(&[]).is_err());
assert!(PoprfServer::<$cs>::deserialize(&[]).is_err());
assert!(BlindedElement::<$cs>::deserialize(&[]).is_err());
assert!(EvaluationElement::<$cs>::deserialize(&[]).is_err());
assert!(Proof::<$cs>::deserialize(&[]).is_err());
}
}
};
}
#[cfg(feature = "ristretto255")]
structured_tests!(crate::Ristretto255, ristretto255);
structured_tests!(::p256::NistP256, p256);
structured_tests!(::p384::NistP384, p384);
structured_tests!(::p521::NistP521, p521);
} }
+913 -1029
View File
File diff suppressed because it is too large Load Diff
+26 -22
View File
@@ -1,14 +1,12 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
use alloc::vec::Vec; use alloc::vec::Vec;
use core::cmp::min; use core::cmp::min;
use rand_core::{CryptoRng, Error, RngCore}; use core::convert::Infallible;
use rand_core::{TryCryptoRng, TryRng};
/// A simple implementation of `RngCore` for testing purposes. /// A simple implementation of `RngCore` for testing purposes.
/// ///
@@ -37,29 +35,35 @@ fn rotate_left<T>(data: &mut [T], steps: usize) {
data.reverse(); data.reverse();
} }
impl RngCore for CycleRng { impl TryRng for CycleRng {
fn next_u32(&mut self) -> u32 { type Error = Infallible;
unimplemented!()
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut buf = [0u8; 4];
self.try_fill_bytes(&mut buf)?;
Ok(u32::from_le_bytes(buf))
} }
#[inline] fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
fn next_u64(&mut self) -> u64 { let mut buf = [0u8; 8];
unimplemented!()
self.try_fill_bytes(&mut buf)?;
Ok(u64::from_le_bytes(buf))
} }
#[inline] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
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]);
rotate_left(&mut self.v, len);
}
#[inline] dest[..len].copy_from_slice(&self.v[..len]);
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.fill_bytes(dest); rotate_left(&mut self.v, len);
Ok(()) Ok(())
} }
} }
// This is meant for testing only // This is meant for testing only
impl CryptoRng for CycleRng {} impl TryCryptoRng for CycleRng {}
+3 -6
View File
@@ -1,9 +1,6 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
mod cfrg_vectors; mod cfrg_vectors;
mod mock_rng; mod mock_rng;
+11 -19
View File
@@ -1,9 +1,6 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -14,55 +11,50 @@ 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"\nA\.\d\. {2}(?P<ciphersuite>.+?)\n\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();
let mut count = 1; for (count, caps) in (1..).zip(re.captures_iter(input)) {
for caps in re.captures_iter(input) {
let ciphersuite = format!( let ciphersuite = format!(
"\"{}\": {{ {} }}", "\"{}\": {{ {} }}",
&caps["ciphersuite"], &caps["ciphersuite"],
parse_modes(chunks[count]) parse_modes(chunks[count])
); );
ciphersuites.push(ciphersuite); ciphersuites.push(ciphersuite);
count += 1;
} }
ciphersuites.join(",\n") ciphersuites.join(",\n")
} }
fn parse_modes(input: &str) -> String { fn parse_modes(input: &str) -> String {
let re = regex::Regex::new(r"### (?P<mode>.*+) Mode").unwrap(); let re = regex::Regex::new(r"A\.\d.\d\. {2}(?P<mode>.*?) Mode").unwrap();
let mut modes = vec![]; let mut modes = vec![];
let chunks: Vec<&str> = re.split(input).collect(); let chunks: Vec<&str> = re.split(input).collect();
let mut count = 1; for (count, caps) in (1..).zip(re.captures_iter(input)) {
for caps in re.captures_iter(input) {
let mode = format!( let mode = format!(
"\"{}\": [\n {} \n]", "\"{}\": [\n {} \n]",
&caps["mode"], &caps["mode"],
parse_vectors(chunks[count]) parse_vectors(chunks[count])
); );
modes.push(mode); modes.push(mode);
count += 1;
} }
modes.join(",\n") modes.join(",\n")
} }
fn parse_vectors(input: &str) -> String { fn parse_vectors(input: &str) -> String {
let re = regex::Regex::new(r"Test Vector.*+\n").unwrap(); let re = regex::Regex::new(r"A\.\d.\d\.\d\. {2}Test Vector.*+\n").unwrap();
let mut vectors = vec![]; let mut vectors = vec![];
let chunks: Vec<&str> = re.split(input).collect(); let chunks: Vec<&str> = re.split(input).collect();
let init_params = parse_params(chunks[0]); let init_params = parse_params(chunks[0]);
let mut count = 1; for (count, _) in (1..).zip(re.captures_iter(input)) {
for _ in re.captures_iter(input) {
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count])); let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
vectors.push(params); vectors.push(params);
count += 1;
} }
vectors.join(",\n") vectors.join(",\n")
@@ -96,7 +88,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('#') {
+95 -110
View File
@@ -1,20 +1,12 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
use alloc::string::String; use alloc::string::String;
use alloc::vec; use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::ops::Add;
use digest::core_api::BlockSizeUser; use serde_json::Value;
use digest::OutputSizeUser;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::ArrayLength;
use json::JsonValue;
use crate::tests::mock_rng::CycleRng; use crate::tests::mock_rng::CycleRng;
use crate::tests::parser::*; use crate::tests::parser::*;
@@ -40,7 +32,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 +49,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 +68,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 +79,10 @@ macro_rules! json_to_test_vectors {
#[test] #[test]
fn test_vectors() -> Result<()> { fn test_vectors() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
let rfc = json::parse(rfc_to_json(super::cfrg_vectors::VECTORS).as_str()) let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
.expect("Could not parse json"); .expect("Could not parse json");
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
@@ -95,7 +91,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 +103,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 +115,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 +127,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 +136,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 +145,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,14 +153,64 @@ 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)?;
let p521_oprf_tvs =
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("OPRF"));
assert_ne!(p521_oprf_tvs.len(), 0);
test_oprf_seed_to_key::<NistP521>(&p521_oprf_tvs)?;
test_oprf_blind::<NistP521>(&p521_oprf_tvs)?;
test_oprf_blind_evaluate::<NistP521>(&p521_oprf_tvs)?;
test_oprf_finalize::<NistP521>(&p521_oprf_tvs)?;
test_oprf_evaluate::<NistP521>(&p521_oprf_tvs)?;
let p521_voprf_tvs =
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("VOPRF"));
assert_ne!(p521_voprf_tvs.len(), 0);
test_voprf_seed_to_key::<NistP521>(&p521_voprf_tvs)?;
test_voprf_blind::<NistP521>(&p521_voprf_tvs)?;
test_voprf_blind_evaluate::<NistP521>(&p521_voprf_tvs)?;
test_voprf_finalize::<NistP521>(&p521_voprf_tvs)?;
test_voprf_evaluate::<NistP521>(&p521_voprf_tvs)?;
let p521_poprf_tvs =
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("POPRF"));
assert_ne!(p521_poprf_tvs.len(), 0);
test_poprf_seed_to_key::<NistP521>(&p521_poprf_tvs)?;
test_poprf_blind::<NistP521>(&p521_poprf_tvs)?;
test_poprf_blind_evaluate::<NistP521>(&p521_poprf_tvs)?;
test_poprf_finalize::<NistP521>(&p521_poprf_tvs)?;
test_poprf_evaluate::<NistP521>(&p521_poprf_tvs)?;
Ok(()) Ok(())
} }
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
let server = OprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?; let server = OprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;
@@ -176,11 +222,7 @@ where
Ok(()) Ok(())
} }
fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
let server = VoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?; let server = VoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;
@@ -196,11 +238,7 @@ where
Ok(()) Ok(())
} }
fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
let server = PoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?; let server = PoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;
@@ -217,11 +255,7 @@ where
} }
// Tests input -> blind, blinded_element // Tests input -> blind, blinded_element
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?; let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
@@ -242,11 +276,7 @@ where
} }
// Tests input -> blind, blinded_element // Tests input -> blind, blinded_element
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?; let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
@@ -267,11 +297,7 @@ where
} }
// Tests input -> blind, blinded_element // Tests input -> blind, blinded_element
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?; let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
@@ -292,11 +318,7 @@ where
} }
// Tests sksm, blinded_element -> evaluation_element // Tests sksm, blinded_element -> evaluation_element
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?; let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -313,13 +335,7 @@ where
Ok(()) Ok(())
} }
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
{
for parameters in tvs { for parameters in tvs {
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?; let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -340,18 +356,12 @@ where
assert_eq!(&parameter, &message.serialize().as_slice()); assert_eq!(&parameter, &message.serialize().as_slice());
} }
assert_eq!(&parameters.proof, &proof.serialize().as_slice()); assert_eq!(&parameters.proof, &proof.serialize().to_vec());
} }
Ok(()) Ok(())
} }
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
{
for parameters in tvs { for parameters in tvs {
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone()); let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?; let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -372,8 +382,7 @@ where
blinded_elements.iter(), blinded_elements.iter(),
&prepared_evaluation_elements, &prepared_evaluation_elements,
&prepared_tweak, &prepared_tweak,
) )?;
.unwrap();
let messages: Vec<_> = messages.collect(); let messages: Vec<_> = messages.collect();
@@ -381,17 +390,13 @@ where
assert_eq!(&parameter, &message.serialize().as_slice()); assert_eq!(&parameter, &message.serialize().as_slice());
} }
assert_eq!(&parameters.proof, &proof.serialize().as_slice()); assert_eq!(&parameters.proof, &proof.serialize().to_vec());
} }
Ok(()) Ok(())
} }
// Tests input, blind, evaluation_element -> output // Tests input, blind, evaluation_element -> output
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let client = let client =
@@ -408,11 +413,7 @@ where
Ok(()) Ok(())
} }
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
let mut clients = vec![]; let mut clients = vec![];
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
@@ -447,11 +448,7 @@ where
Ok(()) Ok(())
} }
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
let mut clients = vec![]; let mut clients = vec![];
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
@@ -485,11 +482,7 @@ where
} }
// Tests input, sksm -> output // Tests input, sksm -> output
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?; let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -502,11 +495,7 @@ where
Ok(()) Ok(())
} }
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?; let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -519,11 +508,7 @@ where
Ok(()) Ok(())
} }
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
for parameters in tvs { for parameters in tvs {
for i in 0..parameters.input.len() { for i in 0..parameters.input.len() {
let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?; let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;
+79 -143
View File
@@ -1,9 +1,6 @@
// Copyright (c) Facebook, Inc. and its affiliates. // SPDX-License-Identifier: MIT OR Apache-2.0
// // Copyright (c) VexaHub and contributors.
// This source code is licensed under both the MIT license found in the // Copyright (c) Meta Platforms, Inc. and affiliates.
// LICENSE-MIT file in the root directory of this source tree and the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree.
//! Contains the main VOPRF API //! Contains the main VOPRF API
@@ -12,16 +9,15 @@ use alloc::vec::Vec;
use core::iter::{self, Map, Repeat, Zip}; use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where; use derive_where::derive_where;
use digest::core_api::BlockSizeUser; use digest::{Digest, Output};
use digest::{Digest, Output, OutputSizeUser}; use hybrid_array::Array;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use hybrid_array::typenum::Unsigned;
use generic_array::GenericArray; use rand_core::{TryCryptoRng, TryRng};
use rand_core::{CryptoRng, RngCore};
use crate::common::{ use crate::common::{
BlindedElement, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2, derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode, server_evaluate_hash_input, verify_proof,
PreparedEvaluationElement, Proof, STR_FINALIZE,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar}; use crate::serialization::serde::{Element, Scalar};
@@ -39,13 +35,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) blind: <CS::Group as Group>::Scalar, pub(crate) blind: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -59,13 +51,9 @@ 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
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) sk: <CS::Group as Group>::Scalar, pub(crate) sk: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))] #[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -77,21 +65,17 @@ where
// =================== // // =================== //
///////////////////////// /////////////////////////
impl<CS: CipherSuite> VoprfClient<CS> impl<CS: CipherSuite> VoprfClient<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Computes the first step for the multiplicative blinding version of /// Computes the first step for the multiplicative blinding version of
/// DH-OPRF. /// DH-OPRF.
/// ///
/// # Errors /// # Errors
/// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`]. /// [`Error::Input`] if the `input` is empty or longer then [`u16::MAX`].
pub fn blind<R: RngCore + CryptoRng>( pub fn blind<R: TryRng + TryCryptoRng>(
input: &[u8], input: &[u8],
blinding_factor_rng: &mut R, blinding_factor_rng: &mut R,
) -> Result<VoprfClientBlindResult<CS>> { ) -> Result<VoprfClientBlindResult<CS>> {
let blind = CS::Group::random_scalar(blinding_factor_rng); let blind = CS::Group::random_scalar(blinding_factor_rng)?;
Self::deterministic_blind_unchecked_inner(input, blind) Self::deterministic_blind_unchecked_inner(input, blind)
} }
@@ -160,7 +144,7 @@ where
/// ///
/// The resulting messages can each fail individually with [`Error::Input`] /// The resulting messages can each fail individually with [`Error::Input`]
/// if the `input` is empty or longer then [`u16::MAX`]. /// if the `input` is empty or longer then [`u16::MAX`].
pub fn batch_finalize<'a, I: 'a, II, IC, IM>( pub fn batch_finalize<'a, I, II, IC, IM>(
inputs: &'a II, inputs: &'a II,
clients: &'a IC, clients: &'a IC,
messages: &'a IM, messages: &'a IM,
@@ -169,7 +153,7 @@ where
) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>> ) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>>
where where
CS: 'a, CS: 'a,
I: AsRef<[u8]>, I: 'a + AsRef<[u8]>,
&'a II: 'a + IntoIterator<Item = I>, &'a II: 'a + IntoIterator<Item = I>,
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator, <&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>, &'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
@@ -196,25 +180,21 @@ where
} }
} }
// Only used for test functions /// Only used for test functions
#[cfg(test)] #[cfg(test)]
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar { pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
self.blind self.blind
} }
} }
impl<CS: CipherSuite> VoprfServer<CS> impl<CS: CipherSuite> VoprfServer<CS> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Produces a new instance of a [VoprfServer] using a supplied RNG /// Produces a new instance of a [VoprfServer] using a supplied RNG
/// ///
/// # Errors /// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed. /// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> { pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default(); let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.fill_bytes(&mut seed); rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
// This can't fail as the hash output is type constrained. // This can't fail as the hash output is type constrained.
Self::new_from_seed(&seed, &[]) Self::new_from_seed(&seed, &[])
} }
@@ -245,7 +225,7 @@ where
Ok(Self { sk, pk }) Ok(Self { sk, pk })
} }
// Only used for tests /// Only used for tests
#[cfg(test)] #[cfg(test)]
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar { pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
self.sk self.sk
@@ -254,7 +234,7 @@ where
/// Computes the second step for the multiplicative blinding version of /// Computes the second step for the multiplicative blinding version of
/// DH-OPRF. This message is sent from the server (who holds the OPRF key) /// DH-OPRF. This message is sent from the server (who holds the OPRF key)
/// to the client. /// to the client.
pub fn blind_evaluate<R: RngCore + CryptoRng>( pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
&self, &self,
rng: &mut R, rng: &mut R,
blinded_element: &BlindedElement<CS>, blinded_element: &BlindedElement<CS>,
@@ -287,7 +267,7 @@ where
/// [`Error::Batch`] if the number of `blinded_elements` and /// [`Error::Batch`] if the number of `blinded_elements` and
/// `evaluation_elements` don't match or is longer then [`u16::MAX`] /// `evaluation_elements` don't match or is longer then [`u16::MAX`]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, I>( pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, I>(
&self, &self,
rng: &mut R, rng: &mut R,
blinded_elements: &'a I, blinded_elements: &'a I,
@@ -338,7 +318,7 @@ where
pub fn batch_blind_evaluate_finish< pub fn batch_blind_evaluate_finish<
'a, 'a,
'b, 'b,
R: RngCore + CryptoRng, R: TryRng + TryCryptoRng,
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator, IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
IE, IE,
>( >(
@@ -359,14 +339,14 @@ where
g, g,
self.pk, self.pk,
blinded_elements.map(|element| element.0), blinded_elements.map(|element| element.0),
evaluation_elements.into_iter().map(|element| element.0 .0), evaluation_elements.into_iter().map(|element| element.0.0),
Mode::Voprf, Mode::Voprf,
)?; )?;
let messages = evaluation_elements.into_iter().map(<fn( let messages = evaluation_elements.into_iter().map(<fn(
&PreparedEvaluationElement<CS>, &PreparedEvaluationElement<CS>,
) -> EvaluationElement<CS>>::from( ) -> EvaluationElement<CS>>::from(
|element| EvaluationElement(element.0 .0), |element| EvaluationElement(element.0.0),
)); ));
Ok(VoprfServerBatchEvaluateFinishResult { messages, proof }) Ok(VoprfServerBatchEvaluateFinishResult { messages, proof })
@@ -401,11 +381,7 @@ where
/// Contains the fields that are returned by a verifiable client blind /// Contains the fields that are returned by a verifiable client blind
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct VoprfClientBlindResult<CS: CipherSuite> pub struct VoprfClientBlindResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The state to be persisted on the client /// The state to be persisted on the client
pub state: VoprfClient<CS>, pub state: VoprfClient<CS>,
/// The message to send to the server /// The message to send to the server
@@ -422,11 +398,7 @@ pub type VoprfClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnb
/// Contains the fields that are returned by a verifiable server evaluate /// Contains the fields that are returned by a verifiable server evaluate
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct VoprfServerEvaluateResult<CS: CipherSuite> pub struct VoprfServerEvaluateResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The message to send to the client /// The message to send to the client
pub message: EvaluationElement<CS>, pub message: EvaluationElement<CS>,
/// The proof for the client to verify /// The proof for the client to verify
@@ -436,11 +408,7 @@ where
/// Contains the fields that are returned by a verifiable server batch evaluate /// Contains the fields that are returned by a verifiable server batch evaluate
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)] #[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub struct VoprfServerBatchEvaluateResult<CS: CipherSuite> pub struct VoprfServerBatchEvaluateResult<CS: CipherSuite> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// The messages to send to the client /// The messages to send to the client
pub messages: Vec<EvaluationElement<CS>>, pub messages: Vec<EvaluationElement<CS>>,
/// The proof for the client to verify /// The proof for the client to verify
@@ -471,8 +439,6 @@ pub type VoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)] #[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
pub struct VoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I> pub struct VoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
where where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>, &'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
{ {
/// The [`EvaluationElement`]s to send to the client /// The [`EvaluationElement`]s to send to the client
@@ -510,8 +476,6 @@ fn verifiable_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
proof: &Proof<CS>, proof: &Proof<CS>,
) -> Result<VoprfUnblindResult<'a, CS, IC, IM>> ) -> Result<VoprfUnblindResult<'a, CS, IC, IM>>
where where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>, &'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator, <&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>, &'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
@@ -536,7 +500,7 @@ where
)?; )?;
Ok(blinds Ok(blinds
.zip(messages.into_iter()) .zip(messages)
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind))) .map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
} }
@@ -553,11 +517,7 @@ fn finalize_after_unblind<
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>, IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
>( >(
inputs_and_unblinded_elements: IE, inputs_and_unblinded_elements: IE,
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> ) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
inputs_and_unblinded_elements.map(|(input, unblinded_element)| { inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes(); let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
@@ -582,31 +542,23 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use core::ops::Add;
use core::ptr; use core::ptr;
use ::alloc::vec; use ::alloc::vec;
use ::alloc::vec::Vec; use ::alloc::vec::Vec;
use generic_array::sequence::Concat; use rand::rngs::SysRng;
use generic_array::typenum::Sum;
use generic_array::ArrayLength;
use rand::rngs::OsRng;
use super::*; use super::*;
use crate::common::{create_context_string, STR_HASH_TO_GROUP};
use crate::Group; use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
fn prf<CS: CipherSuite>( fn prf<CS: CipherSuite>(
input: &[u8], input: &[u8],
key: <CS::Group as Group>::Scalar, key: <CS::Group as Group>::Scalar,
mode: Mode, mode: Mode,
) -> Output<CS::Hash> ) -> Output<CS::Hash> {
where let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
<CS::Hash as OutputSizeUser>::OutputSize: let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = GenericArray::from(STR_HASH_TO_GROUP).concat(create_context_string::<CS>(mode));
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst).unwrap();
let res = point * &key; let res = point * &key;
@@ -616,13 +568,9 @@ mod tests {
.unwrap() .unwrap()
} }
fn verifiable_retrieval<CS: CipherSuite>() fn verifiable_retrieval<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
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);
@@ -639,19 +587,15 @@ mod tests {
assert_eq!(client_finalize_result, res2); assert_eq!(client_finalize_result, res2);
} }
fn verifiable_batch_retrieval<CS: CipherSuite>() fn verifiable_batch_retrieval<CS: CipherSuite>() {
where let mut rng = SysRng;
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let mut rng = OsRng;
let mut inputs = vec![]; let mut inputs = vec![];
let mut client_states = vec![]; let mut client_states = vec![];
let mut client_messages = vec![]; let mut client_messages = vec![];
let num_iterations = 10; let num_iterations = 10;
for _ in 0..num_iterations { for _ in 0..num_iterations {
let mut input = [0u8; 32]; let mut input = [0u8; 32];
rng.fill_bytes(&mut input); rng.try_fill_bytes(&mut input).unwrap();
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
inputs.push(input); inputs.push(input);
client_states.push(client_blind_result.state); client_states.push(client_blind_result.state);
@@ -687,19 +631,15 @@ mod tests {
assert_eq!(client_finalize_result, res2); assert_eq!(client_finalize_result, res2);
} }
fn verifiable_batch_bad_public_key<CS: CipherSuite>() fn verifiable_batch_bad_public_key<CS: CipherSuite>() {
where let mut rng = SysRng;
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let mut rng = OsRng;
let mut inputs = vec![]; let mut inputs = vec![];
let mut client_states = vec![]; let mut client_states = vec![];
let mut client_messages = vec![]; let mut client_messages = vec![];
let num_iterations = 10; let num_iterations = 10;
for _ in 0..num_iterations { for _ in 0..num_iterations {
let mut input = [0u8; 32]; let mut input = [0u8; 32];
rng.fill_bytes(&mut input); rng.try_fill_bytes(&mut input).unwrap();
let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(&input, &mut rng).unwrap();
inputs.push(input); inputs.push(input);
client_states.push(client_blind_result.state); client_states.push(client_blind_result.state);
@@ -718,31 +658,25 @@ 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);
assert!(client_finalize_result.is_err()); assert!(client_finalize_result.is_err());
} }
fn verifiable_bad_public_key<CS: CipherSuite>() fn verifiable_bad_public_key<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
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,
@@ -753,13 +687,9 @@ mod tests {
assert!(client_finalize_result.is_err()); assert!(client_finalize_result.is_err());
} }
fn verifiable_server_evaluate<CS: CipherSuite>() fn verifiable_server_evaluate<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
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);
@@ -786,15 +716,9 @@ mod tests {
assert!(client_finalize != server_evaluate); assert!(client_finalize != server_evaluate);
} }
fn zeroize_voprf_client<CS: CipherSuite>() fn zeroize_voprf_client<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state; let mut state = client_blind_result.state;
@@ -806,17 +730,9 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0)); assert!(message.serialize().iter().all(|&x| x == 0));
} }
fn zeroize_voprf_server<CS: CipherSuite>() fn zeroize_voprf_server<CS: CipherSuite>() {
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ElemLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ElemLen>: ArrayLength<u8>,
<CS::Group as Group>::ScalarLen: Add<<CS::Group as Group>::ScalarLen>,
Sum<<CS::Group as Group>::ScalarLen, <CS::Group as Group>::ScalarLen>: ArrayLength<u8>,
{
let input = b"input"; let input = b"input";
let mut rng = OsRng; let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap(); let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
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);
@@ -837,6 +753,8 @@ mod tests {
#[test] #[test]
fn test_functionality() -> Result<()> { fn test_functionality() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -861,6 +779,24 @@ mod tests {
zeroize_voprf_client::<NistP256>(); zeroize_voprf_client::<NistP256>();
zeroize_voprf_server::<NistP256>(); zeroize_voprf_server::<NistP256>();
verifiable_retrieval::<NistP384>();
verifiable_batch_retrieval::<NistP384>();
verifiable_bad_public_key::<NistP384>();
verifiable_batch_bad_public_key::<NistP384>();
verifiable_server_evaluate::<NistP384>();
zeroize_voprf_client::<NistP384>();
zeroize_voprf_server::<NistP384>();
verifiable_retrieval::<NistP521>();
verifiable_batch_retrieval::<NistP521>();
verifiable_bad_public_key::<NistP521>();
verifiable_batch_bad_public_key::<NistP521>();
verifiable_server_evaluate::<NistP521>();
zeroize_voprf_client::<NistP521>();
zeroize_voprf_server::<NistP521>();
Ok(()) Ok(())
} }
} }