Compare commits

..
40 Commits
Author SHA1 Message Date
vxrenovate a77d4f4863 chore(deps): update rust deps
Rust CI / cargo fmt (pull_request) Successful in 1m5s
Rust CI / cargo clippy (pull_request) Successful in 1m46s
Rust CI / test (1.87.0 / no backend / no frontend) (pull_request) Successful in 3m37s
Rust CI / test (stable / no backend / no frontend) (pull_request) Successful in 3m6s
Rust CI / test (1.87.0 / no backend / --features danger) (pull_request) Successful in 3m18s
Rust CI / test (stable / no backend / --features danger) (pull_request) Successful in 3m3s
Rust CI / test (1.87.0 / no backend / --features serde) (pull_request) Successful in 3m32s
Rust CI / test (stable / no backend / --features serde) (pull_request) Successful in 3m8s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (pull_request) Successful in 3m30s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (pull_request) Successful in 3m12s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (pull_request) Successful in 3m24s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (pull_request) Successful in 3m10s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (pull_request) Successful in 3m26s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (pull_request) Successful in 3m10s
Rust CI / cargo audit (pull_request) Successful in 6m4s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (pull_request) Successful in 1m23s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (pull_request) Successful in 1m23s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (pull_request) Successful in 1m22s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (pull_request) Successful in 1m22s
2026-08-05 00:12:17 +00:00
vxrenovateandbreakingbread 8d462be1f7 chore(deps): update rust crate p521 to v0.14.0 (#13)
Rust CI / cargo fmt (push) Successful in 6s
Rust CI / cargo clippy (push) Successful in 25s
Rust CI / cargo fmt (pull_request) Has been cancelled
Rust CI / cargo clippy (pull_request) Has been cancelled
Rust CI / test (1.87.0 / no backend / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / no backend / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.87.0 / no backend / --features danger) (pull_request) Has been cancelled
Rust CI / test (stable / no backend / --features danger) (pull_request) Has been cancelled
Rust CI / test (1.87.0 / no backend / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / no backend / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (pull_request) Has been cancelled
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (pull_request) Has been cancelled
Rust CI / cargo audit (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / no backend) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / no backend) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (pull_request) Has been cancelled
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m25s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m17s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m33s
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 1m16s
Rust CI / test (1.87.0 / no backend / --features serde) (push) Successful in 1m32s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m25s
Rust CI / test (stable / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m27s
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 1m29s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m35s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m28s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (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 12s
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [p521](https://github.com/RustCrypto/elliptic-curves/tree/master/p521) ([source](https://github.com/RustCrypto/elliptic-curves)) | dev-dependencies | patch | `0.14.0-rc.15` → `0.14.0` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTIuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI1Mi4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

---------

Co-authored-by: UneBaguette <[email protected]>
Reviewed-on: #13
Co-authored-by: Renovate Bot <[email protected]>
Co-committed-by: Renovate Bot <[email protected]>
2026-07-10 15:39:48 +02:00
breakingbread 201fb57d9f release: 1.0.0 (#12)
Rust CI / cargo audit (push) Successful in 4s
Rust CI / cargo fmt (push) Successful in 3s
Rust CI / cargo clippy (push) Successful in 28s
Publish / publish (release) Successful in 17s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m26s
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 1m26s
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 1m30s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / no frontend) (push) Successful in 1m33s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m24s
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 1m27s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m34s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m40s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features serde) (push) Successful in 1m29s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 10s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 11s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 10s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 13s
- Deduplicate serialization with impl_serde_scalar, impl_serde_elem, and impl_serde_scalar_elem macros
- Move finalize_after_unblind to common.rs, shared by OPRF and VOPRF
- Add shared test helpers (test_all_curves macro, prf function)
- Update dependencies to stable releases

Reviewed-on: #12
Co-authored-by: UneBaguette <[email protected]>
Co-committed-by: UneBaguette <[email protected]>
2026-07-08 23:43:11 +02:00
vxrenovateandbreakingbread ef59638de0 chore(deps): update rust deps (#11)
Rust CI / cargo fmt (push) Successful in 4s
Rust CI / cargo clippy (push) Successful in 27s
Rust CI / test (stable / no backend / no frontend) (push) Successful in 1m21s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m25s
Rust CI / test (1.87.0 / no backend / no frontend) (push) Successful in 1m26s
Rust CI / test (1.87.0 / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m35s
Rust CI / test (stable / no backend / --features danger) (push) Successful in 1m21s
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 1m23s
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 1m26s
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 1m30s
Rust CI / cargo audit (push) Successful in 4s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 10s
Rust CI / no-std (thumbv6m-none-eabi / no backend) (push) Successful in 13s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 11s
Rust CI / no-std (thumbv6m-none-eabi / --features ristretto255-ciphersuite) (push) Successful in 15s
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek) ([source](https://github.com/dalek-cryptography/curve25519-dalek/tree/HEAD/curve25519-dalek)) | dependencies | patch | `5.0.0-rc.1` → `5.0.0` |
| [p256](https://github.com/RustCrypto/elliptic-curves/tree/master/p256) ([source](https://github.com/RustCrypto/elliptic-curves)) | dev-dependencies | patch | `0.14.0-rc.15` → `0.14.0` |
| [p256](https://github.com/RustCrypto/elliptic-curves/tree/master/p256) ([source](https://github.com/RustCrypto/elliptic-curves)) | dependencies | patch | `0.14.0-rc.15` → `0.14.0` |
| [p384](https://github.com/RustCrypto/elliptic-curves/tree/master/p384) ([source](https://github.com/RustCrypto/elliptic-curves)) | dev-dependencies | patch | `0.14.0-rc.15` → `0.14.0` |

---

### Release Notes

<details>
<summary>dalek-cryptography/curve25519-dalek (curve25519-dalek)</summary>

### [`v5.0.0`](https://github.com/dalek-cryptography/curve25519-dalek/blob/HEAD/curve25519-dalek/CHANGELOG.md#500---2026-07-06)

##### Breaking Changes

- Update edition to 2024
- Update the MSRV from 1.60 to 1.85
- Remove `group-bits` feature due to soundness issues with underlying trait ([#&#8203;909](https://github.com/dalek-cryptography/curve25519-dalek/pull/909))
- Re-export `rand_core` ([#&#8203;908](https://github.com/dalek-cryptography/curve25519-dalek/pull/908))
- Rename `Scalar::batch_invert` -> `Scalar::invert_batch` for consistency. Also make it no-alloc. ([#&#8203;789](https://github.com/dalek-cryptography/curve25519-dalek/pull/789))
- Remove deprecated functions `FieldElement::as_bytes()` and `EdwardsPoint::nonspec_map_to_curve()` ([#&#8203;778](https://github.com/dalek-cryptography/curve25519-dalek/pull/778))
- Upgrade `rand_core` dependency to v0.10.0
- Upgrade `digest` and `sha2` deps

##### Other Changes

- Perf: Use maximum available NAF window size in `VartimePrecomputedStraus` ([#&#8203;848](https://github.com/dalek-cryptography/curve25519-dalek/pull/848))
- Perf: Skip checking 8 candidate points in `RistrettoPoint::lizard_decode` ([#&#8203;882](https://github.com/dalek-cryptography/curve25519-dalek/pull/882))
- Add Lizard bytes-to-point injection for Ristretto. Gated under `lizard` feature. ([#&#8203;826](https://github.com/dalek-cryptography/curve25519-dalek/pull/826))
- Add an allocating batch inversion called `Scalar::invert_batch_alloc` ([#&#8203;789](https://github.com/dalek-cryptography/curve25519-dalek/pull/789))
- Add `Scalar::div_by_2` ([#&#8203;805](https://github.com/dalek-cryptography/curve25519-dalek/pull/805))
- Add `EdwardsPoint::hash_to_curve` ([#&#8203;786](https://github.com/dalek-cryptography/curve25519-dalek/pull/786))
- Undeprecate `Scalar::from_bits()` ([#&#8203;780](https://github.com/dalek-cryptography/curve25519-dalek/pull/780))
- Use constant-time equality testing for compressed Ristretto and Edwards points, rather than autoderived equality

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTIuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI1Mi4xIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: #11
Co-authored-by: Renovate Bot <[email protected]>
Co-committed-by: Renovate Bot <[email protected]>
2026-07-07 19:27:31 +02:00
breakingbread 313f1b66bb chore: commit Cargo.lock, update gitignore per current Cargo guidance
Rust CI / cargo fmt (push) Successful in 2s
Rust CI / test (1.87.0 / no backend / --features danger) (push) Successful in 1m23s
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 / no frontend) (push) Successful in 1m16s
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 1m29s
Rust CI / test (stable / no backend / --features serde) (push) Successful in 1m23s
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 1m34s
Rust CI / test (stable / --features ristretto255-ciphersuite / --features danger) (push) Successful in 1m27s
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 3s
Rust CI / no-std (wasm32-unknown-unknown / no backend) (push) Successful in 12s
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 12s
Rust CI / no-std (wasm32-unknown-unknown / --features ristretto255-ciphersuite) (push) Successful in 13s
2026-07-05 00:15:42 +02:00
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
35 changed files with 3047 additions and 2747 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
-179
View File
@@ -1,179 +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
-
frontend_feature:
-
- --features danger
- --features serde
toolchain:
- stable
- 1.65.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 }}
- name: Run cargo test with all features enabled
uses: actions-rs/cargo@v1
with:
command: test
args: --all-features
build-no-std:
name: Build with no-std on ${{ matrix.target }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target:
# for wasm
- wasm32-unknown-unknown
# for any no_std target
- thumbv6m-none-eabi
backend_feature:
-
- --features ristretto255-ciphersuite
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-features --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 }}
+7 -5
View File
@@ -1,10 +1,12 @@
# Generated by Cargo # Generated by Cargo
# will have compiled files and executables # will have compiled files and executables
/target/ debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries pkg/
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html demo/
Cargo.lock
# These are backup files generated by rustfmt # These are backup files generated by rustfmt
**/*.rs.bk **/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
+79
View File
@@ -1,20 +1,99 @@
# Changelog # Changelog
## 1.0.0 (July 8, 2026)
* Deduplicated serialization with `impl_serde_scalar`, `impl_serde_elem`, and `impl_serde_scalar_elem` macros
* Moved `finalize_after_unblind` to `common.rs`, shared by OPRF and VOPRF
* Added shared test helpers (`test_all_curves` macro, `prf` function)
* Updated dependencies to stable releases
## 1.0.0-rc.1 (July 3, 2026)
* 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) ## 0.5.0-pre.3 (March 4, 2023)
* Updated to be in sync with draft-irtf-cfrg-voprf-19 * Updated to be in sync with draft-irtf-cfrg-voprf-19
* Increased MSRV to 1.65 * Increased MSRV to 1.65
* Updated p256 dependency to v0.13 * Updated p256 dependency to v0.13
* Added p384 tests * Added p384 tests
## 0.5.0-pre.2 (February 3, 2023) ## 0.5.0-pre.2 (February 3, 2023)
* Increased MSRV to 1.60 * Increased MSRV to 1.60
* Updated p256 dependency to v0.12 * Updated p256 dependency to v0.12
* Updated curve25519-dalek dependency to 4.0.0-rc.1 * Updated curve25519-dalek dependency to 4.0.0-rc.1
## 0.5.0-pre.1 (December 19, 2022) ## 0.5.0-pre.1 (December 19, 2022)
* Updated curve25519-dalek dependency to 4.0.0-pre.5 * Updated curve25519-dalek dependency to 4.0.0-pre.5
## 0.4.0 (September 15, 2022) ## 0.4.0 (September 15, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with * Updated to be in sync with draft-irtf-cfrg-voprf-11, with
the addition of the POPRF mode the addition of the POPRF mode
* 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
-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.
Generated
+855
View File
@@ -0,0 +1,855 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "base16ct"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
[[package]]
name = "bit-set"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
[[package]]
name = "block-buffer"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
"hybrid-array",
"zeroize",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
dependencies = [
"cfg-if",
"cpufeatures",
"rand_core 0.10.1",
]
[[package]]
name = "cmov"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "cpubits"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-bigint"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271"
dependencies = [
"cpubits",
"ctutils",
"hybrid-array",
"num-traits",
"rand_core 0.10.1",
"subtle",
"zeroize",
]
[[package]]
name = "crypto-common"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
"rand_core 0.10.1",
]
[[package]]
name = "ctutils"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
dependencies = [
"cmov",
"subtle",
]
[[package]]
name = "curve25519-dalek"
version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"digest",
"fiat-crypto",
"rand_core 0.10.1",
"rustc_version",
"serde",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.118",
]
[[package]]
name = "der"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d"
dependencies = [
"const-oid",
"zeroize",
]
[[package]]
name = "derive-where"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.118",
]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"zeroize",
]
[[package]]
name = "displaydoc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "elliptic-curve"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65"
dependencies = [
"base16ct",
"crypto-bigint",
"crypto-common",
"digest",
"ff",
"group",
"hybrid-array",
"rand_core 0.10.1",
"sec1",
"subtle",
"zeroize",
]
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "ff"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f"
dependencies = [
"rand_core 0.10.1",
"subtle",
]
[[package]]
name = "fiat-crypto"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
"r-efi 5.3.0",
"wasip2",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
]
[[package]]
name = "group"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
dependencies = [
"ff",
"rand_core 0.10.1",
"subtle",
]
[[package]]
name = "hash2curve"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1eaf40612d7d854743e7189228a6d528f0f6e8502cf6a0cb831d28a218b7f3f6"
dependencies = [
"digest",
"elliptic-curve",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hybrid-array"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
dependencies = [
"serde",
"subtle",
"typenum",
"zeroize",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "p256"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a"
dependencies = [
"elliptic-curve",
"hash2curve",
"primefield",
"primeorder",
"sha2",
]
[[package]]
name = "p384"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d17b851e6b3e378ab4ecb07fa2ed23f4d15f075735f8fec9fa1e7bdce5f8301f"
dependencies = [
"elliptic-curve",
"fiat-crypto",
"hash2curve",
"primefield",
"primeorder",
"sha2",
]
[[package]]
name = "p521"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ad64cc32c2dc466317c12ee5853e61f159f9eab1fe7efade0395dc2e7b43449"
dependencies = [
"base16ct",
"elliptic-curve",
"hash2curve",
"primefield",
"primeorder",
"sha2",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "primefield"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4"
dependencies = [
"crypto-bigint",
"crypto-common",
"ff",
"rand_core 0.10.1",
"subtle",
"zeroize",
]
[[package]]
name = "primeorder"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06"
dependencies = [
"elliptic-curve",
"primefield",
"wnaf",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "proptest"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
dependencies = [
"bit-set",
"bit-vec",
"bitflags",
"num-traits",
"rand 0.9.4",
"rand_chacha",
"rand_xorshift",
"regex-syntax",
"rusty-fork",
"tempfile",
"unarray",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [
"rand_chacha",
"rand_core 0.9.5",
]
[[package]]
name = "rand"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
dependencies = [
"chacha20",
"getrandom 0.4.3",
"rand_core 0.10.1",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.5",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "rand_core"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rand_xorshift"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a"
dependencies = [
"rand_core 0.9.5",
]
[[package]]
name = "regex"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rusty-fork"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2"
dependencies = [
"fnv",
"quick-error",
"tempfile",
"wait-timeout",
]
[[package]]
name = "sec1"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
dependencies = [
"base16ct",
"ctutils",
"der",
"hybrid-array",
"subtle",
"zeroize",
]
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn 3.0.3",
]
[[package]]
name = "serde_json"
version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "sha2"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.3",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unarray"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "voprf-vx"
version = "1.0.0"
dependencies = [
"curve25519-dalek",
"derive-where",
"digest",
"displaydoc",
"elliptic-curve",
"hash2curve",
"hex",
"hybrid-array",
"p256",
"p384",
"p521",
"proptest",
"rand 0.10.2",
"rand_core 0.10.1",
"regex",
"serde",
"serde_json",
"sha2",
"subtle",
"zeroize",
]
[[package]]
name = "wait-timeout"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
dependencies = [
"libc",
]
[[package]]
name = "wasip2"
version = "1.0.4+wasi-0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wnaf"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1"
dependencies = [
"ff",
"group",
"hybrid-array",
]
[[package]]
name = "zerocopy"
version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.118",
]
[[package]]
name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+31 -29
View File
@@ -1,65 +1,67 @@
[package] [package]
authors = ["Kevin Lewi <[email protected]>"] authors = ["VexaHub Developers", "Kevin Lewi <[email protected]>"]
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/facebook/voprf/" repository = "https://github.com/vexahub/voprf-vx/"
rust-version = "1.65" rust-version = "1.87"
version = "0.5.0-pre.3" version = "1.0.0"
[features] [features]
alloc = [] alloc = []
danger = [] danger = []
doctest = ["dep:p256", "dep:sha2"]
default = ["ristretto255-ciphersuite", "dep:serde"] default = ["ristretto255-ciphersuite", "dep:serde"]
ristretto255 = ["dep:curve25519-dalek", "generic-array/more_lengths"] ristretto255 = ["dep:curve25519-dalek"]
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"] ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
serde = ["generic-array/serde", "dep:serde"] serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
std = ["alloc"] std = ["alloc"]
[dependencies] [dependencies]
curve25519-dalek = { version = "=4.0.0-rc.1", default-features = false, features = [ curve25519-dalek = { version = "5", default-features = false, features = ["rand_core", "zeroize"], optional = true }
"rand_core",
"zeroize",
], optional = true }
derive-where = { version = "1", features = ["zeroize-on-drop"] } derive-where = { version = "1", features = ["zeroize-on-drop"] }
digest = "0.10" digest = { version = "0.11", features = ["zeroize"] }
displaydoc = { version = "0.2", default-features = false } displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.13", 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"] }
rand_core = { version = "0.10", default-features = false }
serde = { version = "1", default-features = false, features = [ serde = { version = "1", default-features = false, features = [
"derive", "derive",
], optional = true } ], optional = true }
sha2 = { version = "0.10", default-features = false, optional = true } sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
subtle = { version = "2.3", default-features = false } p256 = { version = "0.14", 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"
p256 = { version = "0.13", default-features = false, features = [ p256 = { version = "0.14", default-features = false, features = [
"hash2curve", "hash2curve",
"voprf", "oprf",
] } ] }
p384 = { version = "0.13", default-features = false, features = [ p384 = { version = "0.14", default-features = false, features = [
"hash2curve", "hash2curve",
"voprf", "oprf",
] }
p521 = { version = "0.14", default-features = false, features = [
"hash2curve",
"oprf",
] } ] }
proptest = "1" proptest = "1"
rand = "0.8" rand = "0.10"
regex = "1" regex = "1"
serde_json = "1" serde_json = "1"
sha2 = "0.10" sha2 = "0.11"
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true
rustdoc-args = ["--cfg", "docsrs"] 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.5.0-pre.3" voprf-vx = "1.0.0"
``` ```
### Minimum Supported Rust Version ### Minimum Supported Rust Version
Rust **1.65** 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
+27 -23
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::{FixedOutput, HashMarker, 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: &'static str; 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`].
@@ -33,16 +33,20 @@ where
type Hash: BlockSizeUser + Default + FixedOutput + HashMarker; 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 + Default + FixedOutput + HashMarker, 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: &'static str = T::ID; const ID: &'static [u8] = T::ID;
type Group = T; type Group = T;
type Hash = OprfHash<T>;
type Hash = T::Hash;
} }
+88 -113
View File
@@ -1,22 +1,18 @@
// 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::iter::Map;
use core::ops::Add; 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, U2, U256, U9}; 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")]
@@ -29,14 +25,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_OPRF: [u8; 7] = *b"OPRFV1-"; 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`].
@@ -78,10 +74,7 @@ impl Mode {
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).
@@ -95,10 +88,7 @@ where
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.
@@ -109,10 +99,7 @@ where
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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.
@@ -123,11 +110,7 @@ where
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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>"))]
@@ -141,24 +124,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;
@@ -195,7 +174,7 @@ where
&STR_CHALLENGE, &STR_CHALLENGE,
]; ];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, 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.as_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);
@@ -208,16 +187,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);
@@ -255,7 +230,7 @@ where
&STR_CHALLENGE, &STR_CHALLENGE,
]; ];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, 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.as_dst()).unwrap(); let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
@@ -281,12 +256,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();
@@ -297,7 +268,7 @@ 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 = Dst::new::<CS, _, _>(STR_SEED, 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
@@ -308,7 +279,7 @@ where
.chain_update(seed_dst.i2osp_2()) .chain_update(seed_dst.i2osp_2())
.chain_update_multi(&seed_dst.as_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();
@@ -333,7 +304,7 @@ where
&STR_COMPOSITE, &STR_COMPOSITE,
]; ];
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, 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.as_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;
@@ -361,12 +332,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 dst = Dst::new::<CS, _, _>(STR_DERIVE_KEYPAIR, mode);
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?; let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
@@ -399,11 +366,7 @@ 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)
} }
@@ -417,11 +380,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;
@@ -437,12 +396,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)
} }
@@ -450,12 +411,8 @@ 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:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input) CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).map_err(|_| Error::Input)
} }
@@ -464,12 +421,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 ||
@@ -491,31 +444,55 @@ 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())
} }
pub(crate) struct Dst<L: ArrayLength<u8>> { pub(crate) type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
dst_1: GenericArray<u8, L>, IE,
dst_2: &'static str, fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
>;
/// Returned values can only fail with [`Error::Input`].
pub(crate) fn finalize_after_unblind<
'a,
CS: CipherSuite,
I: AsRef<[u8]>,
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
>(
inputs_and_unblinded_elements: IE,
) -> FinalizeAfterUnblindResult<'a, CS, I, IE> {
inputs_and_unblinded_elements.map(|(input, unblinded_element)| {
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
Ok(CS::Hash::new()
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
.chain_update(input.as_ref())
.chain_update(elem_len)
.chain_update(CS::Group::serialize_elem(unblinded_element))
.chain_update(STR_FINALIZE)
.finalize())
})
} }
impl<L: ArrayLength<u8>> Dst<L> { pub(crate) struct Dst<L: ArraySize> {
pub(crate) fn new<CS: CipherSuite, T, TL: ArrayLength<u8>>(par_1: T, mode: Mode) -> Self dst_1: Array<u8, L>,
dst_2: &'static [u8],
}
impl<L: ArraySize> Dst<L> {
pub(crate) fn new<CS, TL>(par_1: Array<u8, TL>, mode: Mode) -> Self
where where
T: Into<GenericArray<u8, TL>>, CS: CipherSuite,
TL: Add<U9, Output = L>, TL: ArraySize + Add<U9, Output = L>,
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{ {
let par_1 = par_1.into();
// Generates the contextString parameter as defined in // Generates the contextString parameter as defined in
// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/> // <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
let par_2 = GenericArray::from(STR_OPRF) let par_2 = ArrayN::<u8, 7>::from(STR_OPRF)
.concat([mode.to_u8()].into()) .concat(ArrayN::<u8, 1>::from([mode.to_u8()]))
.concat([b'-'].into()); .concat(ArrayN::<u8, 1>::from(*b"-"));
let dst_1 = par_1.concat(par_2); let dst_1 = par_1.concat(par_2);
let dst_2 = CS::ID; let dst_2 = CS::ID;
@@ -530,7 +507,7 @@ impl<L: ArrayLength<u8>> Dst<L> {
} }
pub(crate) fn as_dst(&self) -> [&[u8]; 2] { pub(crate) fn as_dst(&self) -> [&[u8]; 2] {
[&self.dst_1, self.dst_2.as_bytes()] [&self.dst_1, self.dst_2]
} }
pub(crate) fn i2osp_2(&self) -> [u8; 2] { pub(crate) fn i2osp_2(&self) -> [u8; 2] {
@@ -568,8 +545,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()
} }
+8 -12
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,
@@ -23,12 +18,14 @@ pub enum Error {
DeriveKeyPair, DeriveKeyPair,
/// Failure to deserialize bytes /// Failure to deserialize bytes
Deserialization, Deserialization,
/// Batched items are more then [`u16::MAX`] or length don't match. /// Batched items are more than [`u16::MAX`] or length don't match.
Batch, Batch,
/// In verifiable mode, occurs when the proof failed to verify /// In verifiable mode, occurs when the proof failed to verify
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 {}
+61 -35
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::block_api::BlockSizeUser;
use digest::typenum::{IsLess, IsLessOrEqual, U256};
use digest::{FixedOutput, HashMarker}; 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, FieldBytesSize, 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>,
FieldBytesSize<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 = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize; type ElemLen = ElemLen<Self>;
type Scalar = Scalar<Self>; type Scalar = Scalar<Self>;
type ScalarLen = FieldBytesSize<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: BlockSizeUser + Default + FixedOutput + HashMarker,
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: BlockSizeUser + Default + FixedOutput + HashMarker, 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 {
@@ -95,8 +118,11 @@ where
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> {
+40 -20
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::{FixedOutput, HashMarker}; 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,7 +52,15 @@ 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
/// ///
@@ -57,7 +70,9 @@ pub trait Group {
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: BlockSizeUser + Default + FixedOutput + HashMarker, 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
/// ///
@@ -67,7 +82,9 @@ pub trait Group {
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: BlockSizeUser + Default + FixedOutput + HashMarker, 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.
+54 -32
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::{FixedOutput, HashMarker}; 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;
@@ -26,7 +27,7 @@ pub struct Ristretto255;
#[cfg(feature = "ristretto255-ciphersuite")] #[cfg(feature = "ristretto255-ciphersuite")]
impl crate::CipherSuite for Ristretto255 { impl crate::CipherSuite for Ristretto255 {
const ID: &'static str = "ristretto255-SHA512"; const ID: &'static [u8] = b"ristretto255-SHA512";
type Group = Ristretto255; type Group = Ristretto255;
@@ -42,34 +43,34 @@ impl Group for Ristretto255 {
type ScalarLen = U32; type ScalarLen = U32;
type SecurityLevel = U16;
// Implements the `hash_to_ristretto255()` function from // Implements the `hash_to_ristretto255()` function from
// https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt // https://www.rfc-editor.org/rfc/rfc9380.html#appendix-B
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: BlockSizeUser + Default + FixedOutput + HashMarker, 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 uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into())) 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: BlockSizeUser + Default + FixedOutput + HashMarker, 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 uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
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())) Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
} }
fn base_elem() -> Self::Elem { fn base_elem() -> Self::Elem {
@@ -81,7 +82,7 @@ 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()
} }
@@ -93,12 +94,14 @@ impl Group for Ristretto255 {
.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);
} }
} }
} }
@@ -116,7 +119,7 @@ impl Group for Ristretto255 {
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()
} }
@@ -129,3 +132,22 @@ impl Group for Ristretto255 {
.ok_or(Error::Deserialization) .ok_or(Error::Deserialization)
} }
} }
// HELPERS
fn expand_uniform_bytes<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<[u8; 64], InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256>
+ IsLessOrEqual<H::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<U16, 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(uniform_bytes)
}
+7 -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
@@ -16,6 +13,7 @@ use crate::{Error, Group, Result};
fn test_group_properties() -> Result<()> { fn test_group_properties() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384; use p384::NistP384;
use p521::NistP521;
#[cfg(feature = "ristretto255")] #[cfg(feature = "ristretto255")]
{ {
@@ -31,6 +29,9 @@ fn test_group_properties() -> Result<()> {
test_identity_element_error::<NistP384>()?; test_identity_element_error::<NistP384>()?;
test_zero_scalar_error::<NistP384>()?; test_zero_scalar_error::<NistP384>()?;
test_identity_element_error::<NistP521>()?;
test_zero_scalar_error::<NistP521>()?;
Ok(()) Ok(())
} }
+85 -90
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-19](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.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
@@ -539,7 +534,7 @@
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends) //! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
#![no_std] #![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(test), deny(unsafe_code))] #![cfg_attr(not(test), deny(unsafe_code))]
#![warn( #![warn(
clippy::cargo, clippy::cargo,
+46 -177
View File
@@ -1,24 +1,19 @@
// 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};
use derive_where::derive_where; use derive_where::derive_where;
use digest::core_api::BlockSizeUser; use digest::Output;
use digest::{Digest, Output, OutputSizeUser}; use hybrid_array::Array;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use rand_core::{TryCryptoRng, TryRng};
use generic_array::GenericArray;
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, derive_key_internal, deterministic_blind_unchecked,
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE, finalize_after_unblind, hash_to_group, server_evaluate_hash_input,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::serialization::serde::Scalar; use crate::serialization::serde::Scalar;
@@ -43,11 +38,7 @@ use crate::{CipherSuite, Error, Group, Result};
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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,
} }
@@ -61,11 +52,7 @@ where
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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 +62,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)
} }
@@ -136,7 +119,7 @@ where
) -> Result<Output<CS::Hash>> { ) -> Result<Output<CS::Hash>> {
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind); let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
let mut outputs = let mut outputs =
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)), &[]); finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)));
outputs.next().unwrap() outputs.next().unwrap()
} }
@@ -153,18 +136,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 +172,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,58 +209,13 @@ 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
pub message: BlindedElement<CS>, pub message: BlindedElement<CS>,
} }
/////////////////////
// Inner functions //
// =============== //
/////////////////////
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
IE,
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
>;
/// Returned values can only fail with [`Error::Input`].
fn finalize_after_unblind<
'a,
CS: CipherSuite,
I: AsRef<[u8]>,
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
>(
inputs_and_unblinded_elements: IE,
_unused: &'a [u8],
) -> FinalizeAfterUnblindResult<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)| {
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
// hashInput = I2OSP(len(input), 2) || input ||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
// "Finalize"
// return Hash(hashInput)
Ok(CS::Hash::new()
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
.chain_update(input.as_ref())
.chain_update(elem_len)
.chain_update(CS::Group::serialize_elem(unblinded_element))
.chain_update(STR_FINALIZE)
.finalize())
})
}
/////////// ///////////
// Tests // // Tests //
// ===== // // ===== //
@@ -291,65 +225,38 @@ where
mod tests { mod tests {
use core::ptr; use core::ptr;
use rand::rngs::OsRng; use rand::TryRng;
use rand::rngs::SysRng;
use super::*; use super::*;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group; use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::tests::helpers::prf;
fn prf<CS: CipherSuite>( fn base_retrieval<CS: CipherSuite>() {
input: &[u8],
key: <CS::Group as Group>::Scalar,
info: &[u8],
mode: Mode,
) -> Output<CS::Hash>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
let res = point * &key;
finalize_after_unblind::<CS, _, _>(iter::once((input, res)), info)
.next()
.unwrap()
.unwrap()
}
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);
let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap(); let client_finalize_result = client_blind_result.state.finalize(input, &message).unwrap();
let res2 = prf::<CS>(input, server.get_private_key(), &[], Mode::Oprf); let res2 = prf::<CS>(input, server.get_private_key(), Mode::Oprf);
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 = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf); let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, 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.as_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()
.unwrap(); .unwrap();
@@ -357,13 +264,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);
@@ -385,13 +288,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;
@@ -403,13 +302,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);
@@ -422,37 +317,11 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0)); assert!(message.serialize().iter().all(|&x| x == 0));
} }
#[test] crate::tests::test_all_curves!(
fn test_functionality() -> Result<()> { base_retrieval,
use p256::NistP256; base_inversion_unsalted,
use p384::NistP384; server_evaluate,
zeroize_oprf_client,
#[cfg(feature = "ristretto255")] zeroize_oprf_server,
{ );
use crate::Ristretto255;
base_retrieval::<Ristretto255>();
base_inversion_unsalted::<Ristretto255>();
server_evaluate::<Ristretto255>();
zeroize_oprf_client::<Ristretto255>();
zeroize_oprf_server::<Ristretto255>();
}
base_retrieval::<NistP256>();
base_inversion_unsalted::<NistP256>();
server_evaluate::<NistP256>();
zeroize_oprf_client::<NistP256>();
zeroize_oprf_server::<NistP256>();
base_retrieval::<NistP384>();
base_inversion_unsalted::<NistP384>();
server_evaluate::<NistP384>();
zeroize_oprf_client::<NistP384>();
zeroize_oprf_server::<NistP384>();
Ok(())
}
} }
+63 -178
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,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, OutputSizeUser}; use digest::{Digest, Output, OutputSizeUser};
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use hybrid_array::typenum::Unsigned;
use generic_array::GenericArray; use hybrid_array::{Array, ArraySize};
use rand_core::{CryptoRng, RngCore}; use rand_core::{TryCryptoRng, TryRng};
use crate::common::{ use crate::common::{
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2, BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode, STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO, hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar}; use crate::serialization::serde::{Element, Scalar};
@@ -41,11 +37,7 @@ use crate::{CipherSuite, Error, Group, Result};
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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,11 +53,7 @@ where
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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>"))]
@@ -77,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)
} }
@@ -144,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);
@@ -179,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)?;
@@ -192,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, &[])
} }
@@ -234,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
@@ -247,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>,
@@ -285,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,
@@ -358,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,
>( >(
@@ -426,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
@@ -449,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
@@ -479,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
@@ -496,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
@@ -510,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
@@ -544,19 +508,12 @@ pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
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.
@@ -575,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
@@ -597,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();
@@ -614,7 +565,7 @@ 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 = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf); let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, 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.as_dst()).unwrap(); let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
@@ -635,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();
@@ -651,7 +598,7 @@ 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 = Dst::new::<CS, _, _>(STR_HASH_TO_SCALAR, Mode::Poprf); let dst = Dst::new::<CS, _>(STR_HASH_TO_SCALAR, 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.as_dst()).unwrap(); let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
@@ -690,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>>,
@@ -719,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)))
} }
@@ -727,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
@@ -743,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);
@@ -783,30 +727,23 @@ 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 = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode); let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
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.as_dst()).unwrap();
// evaluatedElement = G.ScalarInverse(t) * blindedElement // evaluatedElement = G.ScalarInverse(t) * blindedElement
@@ -819,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
@@ -846,21 +779,17 @@ 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 = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf); let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, 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.as_dst()).unwrap() CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
}; };
@@ -874,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
@@ -911,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;
@@ -931,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
@@ -962,37 +873,11 @@ mod tests {
assert!(proof.serialize().iter().all(|&x| x == 0)); assert!(proof.serialize().iter().all(|&x| x == 0));
} }
#[test] crate::tests::test_all_curves!(
fn test_functionality() -> Result<()> { verifiable_retrieval,
use p256::NistP256; verifiable_bad_public_key,
use p384::NistP384; verifiable_server_evaluate,
zeroize_verifiable_client,
#[cfg(feature = "ristretto255")] zeroize_verifiable_server,
{ );
use crate::Ristretto255;
verifiable_retrieval::<Ristretto255>();
verifiable_bad_public_key::<Ristretto255>();
verifiable_server_evaluate::<Ristretto255>();
zeroize_verifiable_client::<Ristretto255>();
zeroize_verifiable_server::<Ristretto255>();
}
verifiable_retrieval::<NistP256>();
verifiable_bad_public_key::<NistP256>();
verifiable_server_evaluate::<NistP256>();
zeroize_verifiable_client::<NistP256>();
zeroize_verifiable_server::<NistP256>();
verifiable_retrieval::<NistP384>();
verifiable_bad_public_key::<NistP384>();
verifiable_server_evaluate::<NistP384>();
zeroize_verifiable_client::<NistP384>();
zeroize_verifiable_server::<NistP384>();
Ok(())
}
} }
+300 -289
View File
@@ -1,295 +1,22 @@
// 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,
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer, PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
}; };
////////////////////////////////////////////////////////// /////////////////////////////
// Serialization and Deserialization for High-Level API // // Deserialization Helpers //
// ==================================================== // // ======================= //
////////////////////////////////////////////////////////// /////////////////////////////
/// Length of [`OprfClient`] in bytes for serialization.
pub type OprfClientLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> OprfClient<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, OprfClientLen<CS>> {
CS::Group::serialize_scalar(self.blind)
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
Ok(Self { blind })
}
}
/// Length of [`VoprfClient`] in bytes for serialization.
pub type VoprfClientLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ElemLen,
>;
impl<CS: CipherSuite> VoprfClient<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<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)
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self {
blind,
blinded_element,
})
}
}
/// Length of [`PoprfClient`] in bytes for serialization.
pub type PoprfClientLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ElemLen,
>;
impl<CS: CipherSuite> PoprfClient<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<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)
.concat(<CS::Group as Group>::serialize_elem(self.blinded_element))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let blind = deserialize_scalar::<CS::Group>(&mut input)?;
let blinded_element = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self {
blind,
blinded_element,
})
}
}
/// Length of [`OprfServer`] in bytes for serialization.
pub type OprfServerLen<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> OprfServer<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, OprfServerLen<CS>> {
CS::Group::serialize_scalar(self.sk)
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
Ok(Self { sk })
}
}
/// Length of [`VoprfServer`] in bytes for serialization.
pub type VoprfServerLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ElemLen,
>;
impl<CS: CipherSuite> VoprfServer<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<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))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self { sk, pk })
}
}
/// Length of [`PoprfServer`] in bytes for serialization.
pub type PoprfServerLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ElemLen,
>;
impl<CS: CipherSuite> PoprfServer<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<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))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let sk = deserialize_scalar::<CS::Group>(&mut input)?;
let pk = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self { sk, pk })
}
}
/// Length of [`Proof`] in bytes for serialization.
pub type ProofLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ScalarLen,
>;
impl<CS: CipherSuite> Proof<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<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)
.concat(CS::Group::serialize_scalar(self.s_scalar))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let c_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
let s_scalar = deserialize_scalar::<CS::Group>(&mut input)?;
Ok(Proof { c_scalar, s_scalar })
}
}
/// Length of [`BlindedElement`] in bytes for serialization.
pub type BlindedElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
impl<CS: CipherSuite> BlindedElement<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, BlindedElementLen<CS>> {
CS::Group::serialize_elem(self.0)
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self(value))
}
}
/// Length of [`EvaluationElement`] in bytes for serialization.
pub type EvaluationElementLen<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
impl<CS: CipherSuite> EvaluationElement<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, EvaluationElementLen<CS>> {
CS::Group::serialize_elem(self.0)
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?;
Ok(Self(value))
}
}
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> { fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
let input = input let input = input
@@ -306,11 +33,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;
} }
@@ -321,11 +48,163 @@ impl<T> SliceExt for [T] {
} }
} }
//////////////////////////////
// Serialization Macros //
// ======================== //
//////////////////////////////
macro_rules! impl_serde_scalar {
($ty:ident, $len:ident, $field:ident) => {
/// Length in bytes for serialization.
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ScalarLen;
impl<CS: CipherSuite> $ty<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Array<u8, $len<CS>> {
CS::Group::serialize_scalar(self.$field)
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let $field = deserialize_scalar::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self { $field })
}
}
};
}
macro_rules! impl_serde_scalar_elem {
($ty:ident, $len:ident, $scalar_field:ident, $elem_field:ident) => {
/// Length in bytes for serialization.
pub type $len<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ElemLen,
>;
impl<CS: CipherSuite> $ty<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Array<u8, $len<CS>> {
<CS::Group as Group>::serialize_scalar(self.$scalar_field)
.concat(<CS::Group as Group>::serialize_elem(self.$elem_field))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let $scalar_field = deserialize_scalar::<CS::Group>(&mut input)?;
let $elem_field = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self {
$scalar_field,
$elem_field,
})
}
}
};
}
macro_rules! impl_serde_elem {
($ty:ident, $len:ident) => {
/// Length in bytes for serialization.
pub type $len<CS> = <<CS as CipherSuite>::Group as Group>::ElemLen;
impl<CS: CipherSuite> $ty<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Array<u8, $len<CS>> {
CS::Group::serialize_elem(self.0)
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let value = deserialize_elem::<CS::Group>(&mut input)?;
if !input.is_empty() {
return Err(Error::Deserialization);
}
Ok(Self(value))
}
}
};
}
//////////////////////////////////////////////////////////
// Serialization and Deserialization for High-Level API //
// ==================================================== //
//////////////////////////////////////////////////////////
impl_serde_scalar!(OprfClient, OprfClientLen, blind);
impl_serde_scalar!(OprfServer, OprfServerLen, sk);
impl_serde_elem!(BlindedElement, BlindedElementLen);
impl_serde_elem!(EvaluationElement, EvaluationElementLen);
impl_serde_scalar_elem!(VoprfClient, VoprfClientLen, blind, blinded_element);
impl_serde_scalar_elem!(PoprfClient, PoprfClientLen, blind, blinded_element);
impl_serde_scalar_elem!(VoprfServer, VoprfServerLen, sk, pk);
impl_serde_scalar_elem!(PoprfServer, PoprfServerLen, sk, pk);
/////////////////////
// Proof (One-Off) //
// =============== //
/////////////////////
/// Length of [`Proof`] in bytes for serialization.
pub type ProofLen<CS> = Sum<
<<CS as CipherSuite>::Group as Group>::ScalarLen,
<<CS as CipherSuite>::Group as Group>::ScalarLen,
>;
impl<CS: CipherSuite> Proof<CS> {
/// Serialization into bytes
pub fn serialize(&self) -> Array<u8, ProofLen<CS>> {
CS::Group::serialize_scalar(self.c_scalar)
.concat(CS::Group::serialize_scalar(self.s_scalar))
}
/// Deserialization from bytes
///
/// # Errors
/// [`Error::Deserialization`] if failed to deserialize `input`.
pub fn deserialize(mut input: &[u8]) -> Result<Self> {
let c_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 })
}
}
///////////////////////////
// Serde Support //
// ===================== //
///////////////////////////
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
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 +218,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 +237,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 +260,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,11 +268,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::<::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)) {
@@ -440,4 +352,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);
} }
+64 -177
View File
@@ -1,29 +1,23 @@
// 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.
//! The VOPRF test vectors taken from: //! The VOPRF test vectors taken from:
//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/draft-irtf-cfrg-voprf-19/draft-irtf-cfrg-voprf.md //! https://www.rfc-editor.org/rfc/rfc9497#appendix-A
pub(crate) const VECTORS: &str = r#" pub(crate) const VECTORS: &str = r#"
## ristretto255-SHA512 A.1. ristretto255-SHA512
### OPRF Mode A.1.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
skSm = 5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063 skSm = 5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063
b0e b0e
~~~
#### Test Vector 1, Batch Size 1 A.1.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706 6706
@@ -34,11 +28,9 @@ d8cc917ea0869c7e
Output = 527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb770826 Output = 527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb770826
4e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf 4e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf
6 6
~~~
#### Test Vector 2, Batch Size 1 A.1.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706 6706
@@ -49,11 +41,9 @@ EvaluationElement = b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e
Output = f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e7 Output = f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e7
50cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c7 50cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c7
3 3
~~~
### VOPRF Mode A.1.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -61,11 +51,9 @@ skSm = e6f73f344b79b379f1a0dd37e07ff62e38d9f71345ce62ae3a9bc60b04ccd
909 909
pkSm = c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476a pkSm = c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476a
d4e d4e
~~~
#### Test Vector 1, Batch Size 1 A.1.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706 6706
@@ -80,11 +68,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3 a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
c c
~~~
#### Test Vector 2, Batch Size 1 A.1.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706 6706
@@ -99,11 +85,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = 8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a Output = 8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a
6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b 6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b
6 6
~~~
#### Test Vector 3, Batch Size 2 A.1.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0 6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0
@@ -122,11 +106,9 @@ Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3 a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df6035 c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df6035
6f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6 6f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6
~~~
### POPRF Mode A.1.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -134,11 +116,9 @@ skSm = 145c79c108538421ac164ecbe131942136d5570b16d8bf41a24d4337da981
e07 e07
pkSm = c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d pkSm = c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d
631 631
~~~
#### Test Vector 1, Batch Size 1 A.1.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
@@ -154,11 +134,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15 Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22 2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
1 1
~~~
#### Test Vector 2, Batch Size 1 A.1.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
@@ -174,11 +152,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = 7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b Output = 7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b
56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae50 56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae50
7 7
~~~
#### Test Vector 3, Batch Size 2 A.1.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
@@ -198,23 +174,19 @@ Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22 2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
1,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de 1,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de
2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507 2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507
~~~
## decaf448-SHAKE256 A.2. decaf448-SHAKE256
### OPRF Mode A.2.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
skSm = e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29e skSm = e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29e
cc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e cc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e
~~~
#### Test Vector 1, Batch Size 1 A.2.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112 3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -225,11 +197,9 @@ EvaluationElement = 50ce4e60eed006e22e7027454b5a4b8319eb2bc8ced609eb
Output = 37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba0 Output = 37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba0
9e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae3 9e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae3
7 7
~~~
#### Test Vector 2, Batch Size 1 A.2.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112 3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -240,11 +210,9 @@ EvaluationElement = 162e9fa6e9d527c3cd734a31bf122a34dbd5bcb7bb23651f
Output = a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a6416 Output = a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a6416
4d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55 4d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55
e e
~~~
### VOPRF Mode A.2.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -252,11 +220,9 @@ skSm = e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104f
cc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e cc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e
pkSm = 945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce0 pkSm = 945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce0
9069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955 9069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955
~~~
#### Test Vector 1, Batch Size 1 A.2.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112 3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -273,11 +239,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853 Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c 5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
1 1
~~~
#### Test Vector 2, Batch Size 1 A.2.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112 3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -294,11 +258,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = 862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380 Output = 862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380
c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c94 c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c94
1 1
~~~
#### Test Vector 3, Batch Size 2 A.2.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce 3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce
@@ -322,11 +284,9 @@ Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c 5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa 1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa
8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941 8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941
~~~
### POPRF Mode A.2.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -334,11 +294,9 @@ skSm = 792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22d
e402990eb143fd7c67ac66be75e0609705ecea800992aac8e19 e402990eb143fd7c67ac66be75e0609705ecea800992aac8e19
pkSm = 6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf54526 pkSm = 6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf54526
47ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1 47ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1
~~~
#### Test Vector 1, Batch Size 1 A.2.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
@@ -356,11 +314,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e 971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
d d
~~~
#### Test Vector 2, Batch Size 1 A.2.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
@@ -378,11 +334,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = 8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf63 Output = 8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf63
3126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8 3126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8
d d
~~~
#### Test Vector 3, Batch Size 2 A.2.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
@@ -407,23 +361,19 @@ Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e 971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
d,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0 d,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0
c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d
~~~
## P256-SHA256 A.3. P256-SHA256
### OPRF Mode A.3.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
skSm = 159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac skSm = 159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac
0bf 0bf
~~~
#### Test Vector 1, Batch Size 1 A.3.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364 d364
@@ -433,11 +383,9 @@ EvaluationElement = 030de02ffec47a1fd53efcdd1c6faf5bdc270912b8749e78
3c7ca75bb412958832 3c7ca75bb412958832
Output = a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe Output = a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe
495dd 495dd
~~~
#### Test Vector 2, Batch Size 1 A.3.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364 d364
@@ -447,11 +395,9 @@ EvaluationElement = 03a0395fe3828f2476ffcd1f4fe540e5a8489322d398be3c
4e5a869db7fcb7c52c 4e5a869db7fcb7c52c
Output = c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece Output = c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece
3dcce 3dcce
~~~
### VOPRF Mode A.3.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -459,11 +405,9 @@ skSm = ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312f
ca6 ca6
pkSm = 03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b1 pkSm = 03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b1
02462 02462
~~~
#### Test Vector 1, Batch Size 1 A.3.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364 d364
@@ -477,11 +421,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1 e45c405d1348b7b1
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
645a1 645a1
~~~
#### Test Vector 2, Batch Size 1 A.3.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364 d364
@@ -495,11 +437,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1 e45c405d1348b7b1
Output = 771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c Output = 771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c
24f18 24f18
~~~
#### Test Vector 3, Batch Size 2 A.3.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
@@ -517,11 +457,9 @@ ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f 645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f
18 18
~~~
### POPRF Mode A.3.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -529,11 +467,9 @@ skSm = 6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4d
ae2 ae2
pkSm = 030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e pkSm = 030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e
2a3e3 2a3e3
~~~
#### Test Vector 1, Batch Size 1 A.3.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -548,11 +484,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1 e45c405d1348b7b1
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24 Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
5c592 5c592
~~~
#### Test Vector 2, Batch Size 1 A.3.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -567,11 +501,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1 e45c405d1348b7b1
Output = 1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5f Output = 1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5f
fce8c fce8c
~~~
#### Test Vector 3, Batch Size 2 A.3.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -590,23 +522,19 @@ ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24 Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
5c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce 5c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce
8c 8c
~~~
## P384-SHA384 A.4. P384-SHA384
### OPRF Mode A.4.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
skSm = dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed skSm = dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed
02ef40b8b92f60ae591bcabd72a6518f188 02ef40b8b92f60ae591bcabd72a6518f188
~~~
#### Test Vector 1, Batch Size 1 A.4.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364 889d89dbfa691d1cde91517fa222ed7ad364
@@ -616,11 +544,9 @@ EvaluationElement = 03af2a4fc94770d7a7bf3187ca9cc4faf3732049eded2442
ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30 ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30
Output = ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b Output = ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b
815a07d5813641fb1398c03ddab5763874357 815a07d5813641fb1398c03ddab5763874357
~~~
#### Test Vector 2, Batch Size 1 A.4.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364 889d89dbfa691d1cde91517fa222ed7ad364
@@ -630,11 +556,9 @@ EvaluationElement = 034e9b9a2960b536f2ef47d8608b21597ba400d5abfa1825
fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b
Output = dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b07585 Output = dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b07585
9d1c526d36597326f1b985778f781a1682e75 9d1c526d36597326f1b985778f781a1682e75
~~~
### VOPRF Mode A.4.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -642,11 +566,9 @@ skSm = 051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992
f4278f9016eafc944edaa2b43183581779d f4278f9016eafc944edaa2b43183581779d
pkSm = 031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b pkSm = 031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b
46acf59b7c0d4a9077b3da21c25dd482229a0 46acf59b7c0d4a9077b3da21c25dd482229a0
~~~
#### Test Vector 1, Batch Size 1 A.4.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364 889d89dbfa691d1cde91517fa222ed7ad364
@@ -661,11 +583,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1 c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0 Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
26b4a622beab60220bf19078bca35a529b35c 26b4a622beab60220bf19078bca35a529b35c
~~~
#### Test Vector 2, Batch Size 1 A.4.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364 889d89dbfa691d1cde91517fa222ed7ad364
@@ -680,11 +600,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1 c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213 Output = b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213
e895942cd0f8580e6d99f82221e66c40d274f e895942cd0f8580e6d99f82221e66c40d274f
~~~
#### Test Vector 3, Batch Size 2 A.4.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5 889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5
@@ -705,11 +623,9 @@ eca27405420cdf3d63cb3aef005f40ba51943c8026877963
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0 Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
26b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809 26b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809
a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f
~~~
### POPRF Mode A.4.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -717,11 +633,9 @@ skSm = 5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d72
1ff79d47d7a75906c30b7818ec0f38b7fb2 1ff79d47d7a75906c30b7818ec0f38b7fb2
pkSm = 02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45de pkSm = 02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45de
b8b951aaf1f10c82d2eaa6d02aafa3f10d2b6 b8b951aaf1f10c82d2eaa6d02aafa3f10d2b6
~~~
#### Test Vector 1, Batch Size 1 A.4.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
@@ -737,11 +651,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1 c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165 Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
27303ed449a08caf84272c3bbc972ede797df 27303ed449a08caf84272c3bbc972ede797df
~~~
#### Test Vector 2, Batch Size 1 A.4.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
@@ -757,11 +669,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1 c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba369953 Output = ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba369953
43ca3b33bb5d6cf15be4d31a7e6d75b30d3f5 43ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
~~~
#### Test Vector 3, Batch Size 2 A.4.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562 Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
@@ -783,24 +693,20 @@ eca27405420cdf3d63cb3aef005f40ba51943c8026877963
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165 Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
27303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f0 27303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f0
78c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5 78c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
~~~
## P521-SHA512 A.5. P521-SHA512
### OPRF Mode A.5.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
skSm = 0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a9 skSm = 0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a9
55d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fcc 55d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fcc
ea6 ea6
~~~
#### Test Vector 1, Batch Size 1 A.5.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a 88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -814,11 +720,9 @@ EvaluationElement = 030166371cf827cb2fb9b581f97907121a16e2dc5d8b10ce
Output = 26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb99 Output = 26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb99
81d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c80 81d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c80
9 9
~~~
#### Test Vector 2, Batch Size 1 A.5.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a 88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -832,11 +736,9 @@ af5640cbb2d9620a6e
Output = ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd32 Output = ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd32
4ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04b 4ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04b
a a
~~~
### VOPRF Mode A.5.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -846,11 +748,9 @@ skSm = 015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f2
pkSm = 0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b0 pkSm = 0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b0
7fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc06 7fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc06
78ac0 78ac0
~~~
#### Test Vector 1, Batch Size 1 A.5.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a 88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -871,11 +771,9 @@ e45c405d1348b7b1
Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1 Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954 a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
b b
~~~
#### Test Vector 2, Batch Size 1 A.5.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a 88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -896,11 +794,9 @@ e45c405d1348b7b1
Output = fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b Output = fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b
54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf47 54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf47
4 4
~~~
#### Test Vector 3, Batch Size 2 A.5.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a 88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -928,11 +824,9 @@ Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954 a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604 b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604
d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474 d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474
~~~
### POPRF Mode A.5.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3 3a3
KeyInfo = 74657374206b6579 KeyInfo = 74657374206b6579
@@ -942,11 +836,9 @@ b27
pkSm = 0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d pkSm = 0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d
692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729 692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729
f812a f812a
~~~
#### Test Vector 1, Batch Size 1 A.5.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00 Input = 00
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
@@ -968,11 +860,9 @@ e45c405d1348b7b1
Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548 Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733 82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
b b
~~~
#### Test Vector 2, Batch Size 1 A.5.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
@@ -994,11 +884,9 @@ e45c405d1348b7b1
Output = 27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af Output = 27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af
5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e 5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e
3 3
~~~
#### Test Vector 3, Batch Size 2 A.5.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f Info = 7465737420696e666f
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333 Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
@@ -1027,5 +915,4 @@ Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733 82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c36 b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c36
38afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3 38afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e3
~~~
"#; "#;
+25
View File
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
use core::iter;
use digest::Output;
use crate::common::{Dst, Mode, STR_HASH_TO_GROUP, finalize_after_unblind};
use crate::{CipherSuite, Group};
pub(crate) fn prf<CS: CipherSuite>(
input: &[u8],
key: <CS::Group as Group>::Scalar,
mode: Mode,
) -> Output<CS::Hash> {
let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, mode);
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
let res = point * &key;
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
.next()
.unwrap()
.unwrap()
}
+20
View File
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
macro_rules! test_all_curves {
($($test_fn:ident),+ $(,)?) => {
#[test]
fn test_functionality() -> $crate::Result<()> {
#[cfg(feature = "ristretto255")]
{
$( $test_fn::<$crate::Ristretto255>(); )+
}
$( $test_fn::<::p256::NistP256>(); )+
$( $test_fn::<::p384::NistP384>(); )+
$( $test_fn::<::p521::NistP521>(); )+
Ok(())
}
};
}
pub(crate) use test_all_curves;
+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());
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 {}
+8 -6
View File
@@ -1,11 +1,13 @@
// 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 macros;
mod mock_rng; mod mock_rng;
mod parser; mod parser;
mod test_cfrg_vectors; mod test_cfrg_vectors;
pub(crate) mod helpers;
pub(crate) use macros::test_all_curves;
+10 -18
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"\n## (?P<ciphersuite>.+?)\n").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")
+49 -94
View File
@@ -1,19 +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.
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 digest::OutputSizeUser;
use generic_array::typenum::{IsLess, IsLessOrEqual, Sum, U256};
use generic_array::ArrayLength;
use serde_json::Value; use serde_json::Value;
use crate::tests::mock_rng::CycleRng; use crate::tests::mock_rng::CycleRng;
@@ -88,6 +80,7 @@ macro_rules! json_to_test_vectors {
fn test_vectors() -> Result<()> { fn test_vectors() -> Result<()> {
use p256::NistP256; use p256::NistP256;
use p384::NistP384; use p384::NistP384;
use p521::NistP521;
let rfc: Value = serde_json::from_str(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");
@@ -187,14 +180,37 @@ fn test_vectors() -> Result<()> {
test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?; test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
test_poprf_evaluate::<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)?;
@@ -206,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)?;
@@ -226,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)?;
@@ -247,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])?;
@@ -272,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])?;
@@ -297,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])?;
@@ -322,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)?;
@@ -343,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)?;
@@ -370,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)?;
@@ -402,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();
@@ -411,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 =
@@ -438,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() {
@@ -477,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() {
@@ -515,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)?;
@@ -532,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)?;
@@ -549,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)?;
+60 -218
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,14 @@ 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::Output;
use digest::{Digest, Output, OutputSizeUser}; use hybrid_array::Array;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256}; use rand_core::{TryCryptoRng, TryRng};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use crate::common::{ use crate::common::{
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2, BlindedElement, EvaluationElement, FinalizeAfterUnblindResult, Mode, PreparedEvaluationElement,
server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode, Proof, derive_keypair, deterministic_blind_unchecked, finalize_after_unblind, generate_proof,
PreparedEvaluationElement, Proof, STR_FINALIZE, hash_to_group, server_evaluate_hash_input, verify_proof,
}; };
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar}; use crate::serialization::serde::{Element, Scalar};
@@ -41,11 +36,7 @@ use crate::{CipherSuite, Error, Group, Result};
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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>"))]
@@ -61,11 +52,7 @@ where
derive(serde::Deserialize, serde::Serialize), derive(serde::Deserialize, serde::Serialize),
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 +64,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 +143,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 +152,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 +179,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 +224,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 +233,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 +266,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 +317,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,
>( >(
@@ -401,11 +380,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 +397,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 +407,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 +438,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 +475,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,45 +499,10 @@ 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)))
} }
type FinalizeAfterUnblindResult<'a, C, I, IE> = Map<
IE,
fn((I, <<C as CipherSuite>::Group as Group>::Elem)) -> Result<Output<<C as CipherSuite>::Hash>>,
>;
/// Returned values can only fail with [`Error::Input`].
fn finalize_after_unblind<
'a,
CS: CipherSuite,
I: AsRef<[u8]>,
IE: 'a + Iterator<Item = (I, <CS::Group as Group>::Elem)>,
>(
inputs_and_unblinded_elements: 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)| {
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
// hashInput = I2OSP(len(input), 2) || input ||
// I2OSP(len(unblindedElement), 2) || unblindedElement ||
// "Finalize"
// return Hash(hashInput)
Ok(CS::Hash::new()
.chain_update(i2osp_2(input.as_ref().len()).map_err(|_| Error::Input)?)
.chain_update(input.as_ref())
.chain_update(elem_len)
.chain_update(CS::Group::serialize_elem(unblinded_element))
.chain_update(STR_FINALIZE)
.finalize())
})
}
/////////// ///////////
// Tests // // Tests //
// ===== // // ===== //
@@ -582,46 +510,20 @@ 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::typenum::Sum; use rand::rngs::SysRng;
use generic_array::ArrayLength;
use rand::rngs::OsRng;
use super::*; use super::*;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group; use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::tests::helpers::prf;
fn prf<CS: CipherSuite>( fn verifiable_retrieval<CS: CipherSuite>() {
input: &[u8],
key: <CS::Group as Group>::Scalar,
mode: Mode,
) -> Output<CS::Hash>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
let point = CS::Group::hash_to_curve::<CS::Hash>(&[input], &dst.as_dst()).unwrap();
let res = point * &key;
finalize_after_unblind::<CS, _, _>(iter::once((input, res)))
.next()
.unwrap()
.unwrap()
}
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);
@@ -638,19 +540,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);
@@ -686,19 +584,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);
@@ -717,7 +611,7 @@ mod tests {
.unwrap(); .unwrap();
let messages: Vec<_> = messages.collect(); let messages: Vec<_> = messages.collect();
let wrong_pk = { let wrong_pk = {
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf); let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, 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.as_dst()).unwrap() CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
}; };
@@ -726,18 +620,14 @@ mod tests {
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 = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, Mode::Oprf); let dst = Dst::new::<CS, _>(STR_HASH_TO_GROUP, 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.as_dst()).unwrap() CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
}; };
@@ -750,13 +640,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);
@@ -780,18 +666,12 @@ mod tests {
// inputs // inputs
let wrong_input = b"wrong input"; let wrong_input = b"wrong input";
let server_evaluate = server.evaluate(wrong_input).unwrap(); let server_evaluate = server.evaluate(wrong_input).unwrap();
assert!(client_finalize != server_evaluate); assert_ne!(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;
@@ -803,17 +683,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);
@@ -831,43 +703,13 @@ mod tests {
assert!(proof.serialize().iter().all(|&x| x == 0)); assert!(proof.serialize().iter().all(|&x| x == 0));
} }
#[test] crate::tests::test_all_curves!(
fn test_functionality() -> Result<()> { verifiable_retrieval,
use p256::NistP256; verifiable_batch_retrieval,
use p384::NistP384; verifiable_bad_public_key,
verifiable_batch_bad_public_key,
#[cfg(feature = "ristretto255")] verifiable_server_evaluate,
{ zeroize_voprf_client,
use crate::Ristretto255; zeroize_voprf_server,
);
verifiable_retrieval::<Ristretto255>();
verifiable_batch_retrieval::<Ristretto255>();
verifiable_bad_public_key::<Ristretto255>();
verifiable_batch_bad_public_key::<Ristretto255>();
verifiable_server_evaluate::<Ristretto255>();
zeroize_voprf_client::<Ristretto255>();
zeroize_voprf_server::<Ristretto255>();
}
verifiable_retrieval::<NistP256>();
verifiable_batch_retrieval::<NistP256>();
verifiable_bad_public_key::<NistP256>();
verifiable_batch_bad_public_key::<NistP256>();
verifiable_server_evaluate::<NistP256>();
zeroize_voprf_client::<NistP256>();
zeroize_voprf_server::<NistP256>();
verifiable_retrieval::<NistP384>();
verifiable_batch_retrieval::<NistP384>();
verifiable_bad_public_key::<NistP384>();
verifiable_batch_bad_public_key::<NistP384>();
verifiable_server_evaluate::<NistP384>();
zeroize_voprf_client::<NistP384>();
zeroize_voprf_server::<NistP384>();
Ok(())
}
} }