Compare commits

..
33 Commits
Author SHA1 Message Date
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
33 changed files with 3003 additions and 2729 deletions
+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
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
debug/
target/
pkg/
demo/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
+73
View File
@@ -1,26 +1,99 @@
# 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)
* Updated to be in sync with draft-irtf-cfrg-voprf-19
* Increased MSRV to 1.65
* Updated p256 dependency to v0.13
* Added p384 tests
## 0.5.0-pre.2 (February 3, 2023)
* Increased MSRV to 1.60
* Updated p256 dependency to v0.12
* Updated curve25519-dalek dependency to 4.0.0-rc.1
## 0.5.0-pre.1 (December 19, 2022)
* Updated curve25519-dalek dependency to 4.0.0-pre.5
## 0.4.0 (September 15, 2022)
* Updated to be in sync with draft-irtf-cfrg-voprf-11, with
the addition of the POPRF mode
* Adds the evaluate() function to the servers to calculate the output of the OPRF
-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
+1 -20
View File
@@ -2,30 +2,11 @@
We want to make contributing to this project as easy and transparent as
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
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.
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
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 both the LICENSE-MIT and LICENSE-APACHE files in the root
directory of this source tree.
Generated
+844
View File
@@ -0,0 +1,844 @@
# 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",
]
[[package]]
name = "der"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
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",
]
[[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.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[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.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
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-rc.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff42e4ace5424e3b6d7cb82514be89866b85af87015f80341e37dcc21a66ce6e"
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.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
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.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
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 = "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.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[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]
authors = ["Kevin Lewi <[email protected]>"]
authors = ["VexaHub Developers", "Kevin Lewi <[email protected]>"]
categories = ["no-std", "algorithms", "cryptography"]
description = "An implementation of a verifiable oblivious pseudorandom function (VOPRF)"
edition = "2021"
keywords = ["oprf"]
license = "MIT"
name = "voprf"
edition = "2024"
keywords = ["oprf", "voprf", "cryptography", "oblivious-prf"]
license = "MIT OR Apache-2.0"
name = "voprf-vx"
readme = "README.md"
repository = "https://github.com/facebook/voprf/"
rust-version = "1.65"
version = "0.5.0-pre.5"
repository = "https://github.com/vexahub/voprf-vx/"
rust-version = "1.87"
version = "1.0.0"
[features]
alloc = []
danger = []
doctest = ["dep:p256", "dep:sha2"]
default = ["ristretto255-ciphersuite", "dep:serde"]
ristretto255 = ["dep:curve25519-dalek", "generic-array/more_lengths"]
ristretto255 = ["dep:curve25519-dalek"]
ristretto255-ciphersuite = ["ristretto255", "dep:sha2"]
serde = ["generic-array/serde", "dep:serde"]
serde = ["curve25519-dalek?/serde", "hybrid-array/serde", "dep:serde"]
std = ["alloc"]
[dependencies]
curve25519-dalek = { version = "=4.0.0-rc.3", default-features = false, features = [
"rand_core",
"zeroize",
], optional = true }
curve25519-dalek = { version = "5", default-features = false, features = ["rand_core", "zeroize"], optional = true }
derive-where = { version = "1", features = ["zeroize-on-drop"] }
digest = "0.10"
digest = { version = "0.11", features = ["zeroize"] }
displaydoc = { version = "0.2", default-features = false }
elliptic-curve = { version = "0.13", features = [
"hash2curve",
elliptic-curve = { version = "0.14", features = [
"sec1",
"voprf",
] }
generic-array = "0.14"
rand_core = { version = "0.6", default-features = false }
hash2curve = "0.14"
hybrid-array = { version = "0.4", features = ["zeroize"] }
rand_core = { version = "0.10", default-features = false }
serde = { version = "1", default-features = false, features = [
"derive",
], optional = true }
sha2 = { version = "0.10", default-features = false, optional = true }
subtle = { version = "2.3", default-features = false }
sha2 = { version = "0.11", default-features = false, features = ["zeroize"], optional = true }
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 }
[dev-dependencies]
generic-array = { version = "0.14", features = ["more_lengths"] }
hex = "0.4"
p256 = { version = "0.13", default-features = false, features = [
p256 = { version = "0.14", default-features = false, features = [
"hash2curve",
"voprf",
"oprf",
] }
p384 = { version = "0.13", default-features = false, features = [
p384 = { version = "0.14", default-features = false, features = [
"hash2curve",
"voprf",
"oprf",
] }
p521 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve",
"oprf",
] }
proptest = "1"
rand = "0.8"
rand = "0.10"
regex = "1"
serde_json = "1"
sha2 = "0.10"
sha2 = "0.11"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
targets = []
features = ["doctest"]
+8 -6
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)
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
-------------
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
------------
@@ -16,17 +16,19 @@ Installation
Add the following line to the dependencies of your `Cargo.toml`:
```
voprf = "0.5.0-pre.4"
voprf-vx = "1.0.0"
```
### Minimum Supported Rust Version
Rust **1.65** or higher.
Rust **1.87** or higher.
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).
License
+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"
unstable_features = true
wrap_comments = true
+26 -23
View File
@@ -1,29 +1,28 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Defines the CipherSuite trait to specify the underlying primitives for VOPRF
use digest::core_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker, OutputSizeUser};
use elliptic_curve::VoprfParameters;
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
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
pub trait CipherSuite
where
<Self::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<Self::Hash as BlockSizeUser>::BlockSize>,
<Self::Hash as OutputSizeUser>::OutputSize: ArraySize
+ 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
/// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
const ID: &'static str;
/// <https://www.rfc-editor.org/rfc/rfc9497>
const ID: &'static [u8];
/// 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`].
@@ -34,16 +33,20 @@ where
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
T: Group,
T::Hash: BlockSizeUser + Default + FixedOutput + HashMarker,
<T::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<T::Hash as BlockSizeUser>::BlockSize>,
OprfHash<T>: BlockSizeUser + Default + FixedOutput + HashMarker,
<OprfHash<T> as OutputSizeUser>::OutputSize: ArraySize
+ 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 Hash = T::Hash;
type Hash = OprfHash<T>;
}
+87 -113
View File
@@ -1,23 +1,18 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Common functionality between multiple OPRF modes.
use core::convert::TryFrom;
use core::iter::Map;
use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U2, U256, U9};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
use hybrid_array::typenum::{IsLess, U2, U9, U256, Unsigned};
use hybrid_array::{Array, ArrayN, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq;
#[cfg(feature = "serde")]
@@ -30,14 +25,14 @@ use crate::{CipherSuite, Error, Group, InternalError, Result};
///////////////
pub(crate) const STR_FINALIZE: [u8; 8] = *b"Finalize";
pub(crate) const STR_SEED: [u8; 5] = *b"Seed-";
pub(crate) const STR_DERIVE_KEYPAIR: [u8; 13] = *b"DeriveKeyPair";
pub(crate) const STR_SEED: ArrayN<u8, 5> = Array(*b"Seed-");
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_CHALLENGE: [u8; 9] = *b"Challenge";
pub(crate) const STR_INFO: [u8; 4] = *b"Info";
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_GROUP: [u8; 12] = *b"HashToGroup-";
pub(crate) const STR_HASH_TO_SCALAR: ArrayN<u8, 13> = Array(*b"HashToScalar-");
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
/// is only used for custom implementations for [`Group`].
@@ -79,10 +74,7 @@ impl Mode {
pub struct BlindedElement<CS: CipherSuite>(
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
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
/// verifiable or not) to a server (either verifiable or not).
@@ -96,10 +88,7 @@ where
pub struct EvaluationElement<CS: CipherSuite>(
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
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
/// preparation.
@@ -110,10 +99,7 @@ where
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>)
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>;
pub struct PreparedEvaluationElement<CS: CipherSuite>(pub(crate) EvaluationElement<CS>);
/// A proof produced by a server that the OPRF output matches against a server
/// public key.
@@ -124,11 +110,7 @@ where
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct Proof<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct Proof<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) c_scalar: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
@@ -142,24 +124,20 @@ where
/// Can only fail with [`Error::Batch`].
#[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,
k: <CS::Group as Group>::Scalar,
a: <CS::Group as Group>::Elem,
b: <CS::Group as Group>::Elem,
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
mode: Mode,
) -> Result<Proof<CS>>
where
<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
) -> Result<Proof<CS>> {
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
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 t3 = m * &r;
@@ -196,7 +174,7 @@ where
&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.
let c_scalar = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
let s_scalar = r - &(c_scalar * &k);
@@ -209,16 +187,12 @@ where
pub(crate) fn verify_proof<CS: CipherSuite>(
a: <CS::Group as Group>::Elem,
b: <CS::Group as Group>::Elem,
cs: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
ds: impl Iterator<Item = <CS::Group as Group>::Elem> + ExactSizeIterator,
cs: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
ds: impl ExactSizeIterator<Item = <CS::Group as Group>::Elem>,
proof: &Proof<CS>,
mode: Mode,
) -> Result<()>
where
<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
) -> Result<()> {
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.2
let (m, z) = compute_composites::<CS, _, _>(None, b, cs, ds, mode)?;
let t2 = (a * &proof.s_scalar) + &(b * &proof.c_scalar);
let t3 = (m * &proof.s_scalar) + &(z * &proof.c_scalar);
@@ -256,7 +230,7 @@ where
&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.
let c = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
@@ -282,12 +256,8 @@ fn compute_composites<
c_slice: IC,
d_slice: ID,
mode: Mode,
) -> Result<ComputeCompositesResult<CS>>
where
<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
) -> Result<ComputeCompositesResult<CS>> {
// https://www.rfc-editor.org/rfc/rfc9497#section-2.2.1
let elem_len = <CS::Group as Group>::ElemLen::U16.to_be_bytes();
@@ -298,7 +268,7 @@ where
let len = u16::try_from(c_slice.len()).map_err(|_| Error::Batch)?;
// 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 ||
// I2OSP(len(seedDST), 2) || seedDST
@@ -309,7 +279,7 @@ where
.chain_update(seed_dst.i2osp_2())
.chain_update_multi(&seed_dst.as_dst())
.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 z = CS::Group::identity_elem();
@@ -334,7 +304,7 @@ where
&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.
let di = CS::Group::hash_to_scalar::<CS::Hash>(&h2_input, &dst.as_dst()).unwrap();
m = c * &di + &m;
@@ -362,12 +332,8 @@ pub(crate) fn derive_key_internal<CS: CipherSuite>(
seed: &[u8],
info: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Scalar, Error>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = Dst::new::<CS, _, _>(STR_DERIVE_KEYPAIR, mode);
) -> Result<<CS::Group as Group>::Scalar, Error> {
let dst = Dst::new::<CS, _>(STR_DERIVE_KEYPAIR, mode);
let info_len = i2osp_2(info.len()).map_err(|_| Error::DeriveKeyPair)?;
@@ -400,11 +366,7 @@ pub fn derive_key<CS: CipherSuite>(
seed: &[u8],
info: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Scalar, Error>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
) -> Result<<CS::Group as Group>::Scalar, Error> {
derive_key_internal::<CS>(seed, info, mode)
}
@@ -418,11 +380,7 @@ pub(crate) fn derive_keypair<CS: CipherSuite>(
seed: &[u8],
info: &[u8],
mode: Mode,
) -> Result<DeriveKeypairResult<CS>, Error>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
) -> Result<DeriveKeypairResult<CS>, Error> {
let sk_s = derive_key_internal::<CS>(seed, info, mode)?;
let pk_s = CS::Group::base_elem() * &sk_s;
@@ -438,12 +396,14 @@ pub(crate) fn deterministic_blind_unchecked<CS: CipherSuite>(
input: &[u8],
blind: &<CS::Group as Group>::Scalar,
mode: Mode,
) -> Result<<CS::Group as Group>::Elem>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
) -> Result<<CS::Group as Group>::Elem> {
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)
}
@@ -451,12 +411,8 @@ where
pub(crate) fn hash_to_group<CS: CipherSuite>(
input: &[u8],
mode: Mode,
) -> Result<<CS::Group as Group>::Elem>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let dst = Dst::new::<CS, _, _>(STR_HASH_TO_GROUP, mode);
) -> Result<<CS::Group as Group>::Elem> {
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)
}
@@ -465,12 +421,8 @@ where
pub(crate) fn server_evaluate_hash_input<CS: CipherSuite>(
input: &[u8],
info: Option<&[u8]>,
issued_element: GenericArray<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
) -> Result<Output<CS::Hash>>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
issued_element: Array<u8, <<CS as CipherSuite>::Group as Group>::ElemLen>,
) -> Result<Output<CS::Hash>> {
// OPRF & VOPRF
// hashInput = I2OSP(len(input), 2) || input ||
// I2OSP(len(issuedElement), 2) || issuedElement ||
@@ -492,31 +444,55 @@ where
.chain_update(info.as_ref());
}
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(STR_FINALIZE)
.finalize())
}
pub(crate) struct Dst<L: ArrayLength<u8>> {
dst_1: GenericArray<u8, L>,
dst_2: &'static str,
pub(crate) 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`].
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) fn new<CS: CipherSuite, T, TL: ArrayLength<u8>>(par_1: T, mode: Mode) -> Self
pub(crate) struct Dst<L: ArraySize> {
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
T: Into<GenericArray<u8, TL>>,
TL: Add<U9, Output = L>,
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
CS: CipherSuite,
TL: ArraySize + Add<U9, Output = L>,
{
let par_1 = par_1.into();
// Generates the contextString parameter as defined in
// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-voprf/>
let par_2 = GenericArray::from(STR_OPRF)
.concat([mode.to_u8()].into())
.concat([b'-'].into());
// <https://www.rfc-editor.org/rfc/rfc9497#section-3.1>
let par_2 = ArrayN::<u8, 7>::from(STR_OPRF)
.concat(ArrayN::<u8, 1>::from([mode.to_u8()]))
.concat(ArrayN::<u8, 1>::from([b'-']));
let dst_1 = par_1.concat(par_2);
let dst_2 = CS::ID;
@@ -531,7 +507,7 @@ impl<L: ArrayLength<u8>> Dst<L> {
}
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] {
@@ -569,8 +545,6 @@ pub(crate) fn i2osp_2(input: usize) -> Result<[u8; 2], InternalError> {
.map_err(|_| InternalError::I2osp)
}
pub(crate) fn i2osp_2_array<L: ArrayLength<u8> + IsLess<U256>>(
_: &GenericArray<u8, L>,
) -> GenericArray<u8, U2> {
pub(crate) fn i2osp_2_array<L: ArraySize + IsLess<U256>>() -> Array<u8, U2> {
L::U16.to_be_bytes().into()
}
+7 -12
View File
@@ -1,20 +1,14 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Errors which are produced during an execution of the protocol
use displaydoc::Display;
/// [`Result`](core::result::Result) shorthand that uses [`Error`].
pub type Result<T, E = Error> = core::result::Result<T, E>;
/// 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 {
/// Size of info is longer then [`u16::MAX`].
Info,
@@ -24,12 +18,14 @@ pub enum Error {
DeriveKeyPair,
/// Failure to deserialize bytes
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,
/// In verifiable mode, occurs when the proof failed to verify
ProofVerification,
/// The protocol has failed and can't be completed.
Protocol,
/// Random number generator failure.
Rng,
}
/// Only used to implement [`Group`](crate::Group).
@@ -41,5 +37,4 @@ pub enum InternalError {
I2osp,
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
impl core::error::Error for Error {}
+60 -35
View File
@@ -1,59 +1,73 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
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 elliptic_curve::group::cofactor::CofactorGroup;
use elliptic_curve::hash2curve::{ExpandMsgXmd, FromOkm, GroupDigest};
use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint};
use elliptic_curve::sec1::{FromSec1Point, ModulusSize, ToSec1Point};
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 generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use hash2curve::{ExpandMsgXmd, GroupDigest, MapToCurve, hash_to_scalar};
use hybrid_array::typenum::{IsGreaterOrEqual, Prod, Sum, True, U2};
use hybrid_array::{Array, ArraySize};
use rand_core::TryCryptoRng;
use super::Group;
use crate::{Error, InternalError, Result};
type ElemLen<C> = <ScalarLen<C> as ModulusSize>::CompressedPointSize;
type ScalarLen<C> = FieldBytesSize<C>;
impl<C> Group for C
where
C: GroupDigest,
ProjectivePoint<Self>: CofactorGroup + ToEncodedPoint<Self>,
FieldBytesSize<Self>: ModulusSize,
AffinePoint<Self>: FromEncodedPoint<Self> + ToEncodedPoint<Self>,
Scalar<Self>: FromOkm,
C::SecurityLevel: Mul<U2>,
C::SecurityLevel: ArraySize,
<C::SecurityLevel as Mul<U2>>::Output: ArraySize,
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 ElemLen = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize;
type ElemLen = ElemLen<Self>;
type Scalar = Scalar<Self>;
type ScalarLen = FieldBytesSize<Self>;
type ScalarLen = ScalarLen<Self>;
type SecurityLevel = C::SecurityLevel;
// Implements the `hash_to_curve()` function from
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-3
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>,
{
Self::hash_from_bytes::<ExpandMsgXmd<H>>(input, dst).map_err(|_| InternalError::Input)
// https://www.rfc-editor.org/rfc/rfc9380.html#section-3
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError> {
Self::hash_from_bytes(input, dst).map_err(|_| InternalError::Input)
}
// Implements the `HashToScalar()` function
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where
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)
}
@@ -65,10 +79,10 @@ where
ProjectivePoint::<Self>::identity()
}
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
let bytes = elem.to_encoded_point(true);
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
let bytes = elem.to_sec1_point(true);
let bytes = bytes.as_bytes();
let mut result = GenericArray::default();
let mut result = Array::default();
result[..bytes.len()].copy_from_slice(bytes);
result
}
@@ -79,8 +93,16 @@ where
.map_err(|_| Error::Deserialization)
}
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
*SecretKey::<Self>::random(rng).to_nonzero_scalar()
fn random_scalar<R: TryCryptoRng>(rng: &mut R) -> Result<Self::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 {
@@ -96,8 +118,11 @@ where
Scalar::<Self>::ZERO
}
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
scalar.into()
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
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> {
+39 -20
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Defines the Group trait to specify the underlying prime order group
@@ -14,11 +10,11 @@ mod ristretto;
use core::ops::{Add, Mul, Sub};
use digest::core_api::BlockSizeUser;
use digest::block_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256};
use generic_array::{ArrayLength, GenericArray};
use rand_core::{CryptoRng, RngCore};
use hybrid_array::typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, Sum, True, U2, U256};
use hybrid_array::{Array, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
#[cfg(feature = "ristretto255")]
pub use ristretto::Ristretto255;
use subtle::{Choice, ConstantTimeEq};
@@ -27,8 +23,16 @@ use zeroize::Zeroize;
use crate::{InternalError, Result};
/// 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.
pub trait Group {
/// subgroup is noted additively — as in the RFC — in this trait.
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
type Elem: ConstantTimeEq
+ Copy
@@ -37,7 +41,7 @@ pub trait Group {
+ for<'a> Mul<&'a Self::Scalar, Output = Self::Elem>;
/// The byte length necessary to represent group elements
type ElemLen: ArrayLength<u8> + 'static;
type ElemLen: ArraySize + 'static;
/// The type of base field scalars
type Scalar: ConstantTimeEq
@@ -48,7 +52,15 @@ pub trait Group {
+ for<'a> Sub<&'a Self::Scalar, Output = Self::Scalar>;
/// 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
///
@@ -58,7 +70,9 @@ pub trait Group {
fn hash_to_curve<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Elem, InternalError>
where
H: BlockSizeUser + Default + FixedOutput + HashMarker,
H::OutputSize: IsLess<U256> + IsLessOrEqual<H::BlockSize>;
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
///
@@ -68,7 +82,9 @@ pub trait Group {
fn hash_to_scalar<H>(input: &[&[u8]], dst: &[&[u8]]) -> Result<Self::Scalar, InternalError>
where
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
fn base_elem() -> Self::Elem;
@@ -82,7 +98,7 @@ pub trait Group {
}
/// 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
/// element is the identity element, return an error.
@@ -92,8 +108,11 @@ pub trait Group {
/// is not a valid point on the group or the identity element.
fn deserialize_elem(element_bits: &[u8]) -> Result<Self::Elem>;
/// picks a scalar at random
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar;
/// Picks a scalar at random.
///
/// # 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
fn invert_scalar(scalar: Self::Scalar) -> Self::Scalar;
@@ -106,7 +125,7 @@ pub trait Group {
fn zero_scalar() -> Self::Scalar;
/// 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
/// scalar is zero or invalid, then return an error.
+53 -32
View File
@@ -1,21 +1,21 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use core::num::NonZeroU16;
use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::Identity;
use digest::core_api::BlockSizeUser;
use digest::block_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker};
use elliptic_curve::hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use generic_array::typenum::{IsLess, IsLessOrEqual, U256, U32, U64};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use hash2curve::{ExpandMsg, ExpandMsgXmd, Expander};
use hybrid_array::Array;
use hybrid_array::typenum::{
IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U16, U32, U256,
};
use rand_core::{TryCryptoRng, TryRng};
use subtle::ConstantTimeEq;
use super::Group;
@@ -27,7 +27,7 @@ pub struct Ristretto255;
#[cfg(feature = "ristretto255-ciphersuite")]
impl crate::CipherSuite for Ristretto255 {
const ID: &'static str = "ristretto255-SHA512";
const ID: &'static [u8] = b"ristretto255-SHA512";
type Group = Ristretto255;
@@ -43,34 +43,34 @@ impl Group for Ristretto255 {
type ScalarLen = U32;
type SecurityLevel = U16;
// 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>
where
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();
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes.into()))
Ok(RistrettoPoint::from_uniform_bytes(&uniform_bytes))
}
// 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>
where
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();
ExpandMsgXmd::<H>::expand_message(input, dst, 64)
.map_err(|_| InternalError::Input)?
.fill_bytes(&mut uniform_bytes);
let uniform_bytes = expand_uniform_bytes::<H>(input, dst)?;
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes.into()))
Ok(Scalar::from_bytes_mod_order_wide(&uniform_bytes))
}
fn base_elem() -> Self::Elem {
@@ -82,7 +82,7 @@ impl Group for Ristretto255 {
}
// 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()
}
@@ -94,12 +94,14 @@ impl Group for Ristretto255 {
.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 {
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 {
break scalar;
if let Ok(scalar) = Self::deserialize_scalar(&scalar_bytes) {
break Ok(scalar);
}
}
}
@@ -117,7 +119,7 @@ impl Group for Ristretto255 {
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()
}
@@ -130,3 +132,22 @@ impl Group for Ristretto255 {
.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)
}
+6 -6
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Includes a series of tests for the group implementations
@@ -17,6 +13,7 @@ use crate::{Error, Group, Result};
fn test_group_properties() -> Result<()> {
use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
#[cfg(feature = "ristretto255")]
{
@@ -32,6 +29,9 @@ fn test_group_properties() -> Result<()> {
test_identity_element_error::<NistP384>()?;
test_zero_scalar_error::<NistP384>()?;
test_identity_element_error::<NistP521>()?;
test_zero_scalar_error::<NistP521>()?;
Ok(())
}
+84 -90
View File
@@ -1,17 +1,11 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! An implementation of a verifiable oblivious pseudorandom function (VOPRF)
//!
//! Note: This implementation is in sync with
//! [draft-irtf-cfrg-voprf-19](https://www.ietf.org/archive/id/draft-irtf-cfrg-voprf-19.html),
//! but this specification is subject to change, until the final version
//! published by the IETF.
//! [RFC 9497](https://www.rfc-editor.org/rfc/rfc9497).
//!
//! # Overview
//!
@@ -22,7 +16,7 @@
//! We will use the following choice in this example:
//!
//! ```ignore
//! type CipherSuite = voprf::Ristretto255;
//! type CipherSuite = voprf_vx::Ristretto255;
//! ```
//!
//! ## Modes of Operation
@@ -53,14 +47,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::OprfServer;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vx::OprfServer;
//!
//! let mut server_rng = OsRng;
//! let mut server_rng = SysRng;
//! let server = OprfServer::<CipherSuite>::new(&mut server_rng);
//! ```
//!
@@ -73,14 +67,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::OprfClient;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! 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)
//! .expect("Unable to construct client");
//! ```
@@ -94,19 +88,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let server_evaluate_result = server.blind_evaluate(&client_blind_result.message);
//! ```
@@ -119,19 +113,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message);
//! let client_finalize_result = client_blind_result
@@ -152,19 +146,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::OprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::OprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = OprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::OprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::OprfServer;
//! # let mut server_rng = SysRng;
//! # let server = OprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let message = server.blind_evaluate(&client_blind_result.message);
//! let client_finalize_result = client_blind_result
@@ -199,14 +193,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::VoprfServer;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! use voprf_vx::VoprfServer;
//!
//! let mut server_rng = OsRng;
//! let mut server_rng = SysRng;
//! let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//!
//! // To be sent to the client
@@ -226,14 +220,14 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! use rand::rngs::OsRng;
//! use rand::RngCore;
//! use voprf::VoprfClient;
//! use rand::rngs::SysRng;
//! use rand::Rng;
//! 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)
//! .expect("Unable to construct client");
//! ```
@@ -248,19 +242,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::{VoprfServerEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerEvaluateResult { message, proof } =
//! server.blind_evaluate(&mut server_rng, &client_blind_result.message);
@@ -275,19 +269,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate(
//! # &mut server_rng,
@@ -316,19 +310,19 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let client_blind_result = VoprfClient::<CipherSuite>::blind(
//! # b"input",
//! # &mut client_rng,
//! # ).expect("Unable to construct client");
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let server_evaluate_result = server.blind_evaluate(
//! # &mut server_rng,
@@ -370,13 +364,13 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::VoprfClient;
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::VoprfClient;
//! # use rand::{rngs::SysRng, Rng};
//! #
//! let mut client_rng = OsRng;
//! let mut client_rng = SysRng;
//! let mut client_states = vec![];
//! let mut client_messages = vec![];
//! for _ in 0..10 {
@@ -394,13 +388,13 @@
//!
//! ```
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::{VoprfServerBatchEvaluateFinishResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
@@ -411,8 +405,8 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VoprfServer;
//! let mut server_rng = OsRng;
//! # use voprf_vx::VoprfServer;
//! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let prepared_evaluation_elements = server.batch_blind_evaluate_prepare(client_messages.iter());
//! let prepared_elements: Vec<_> = prepared_evaluation_elements.collect();
@@ -428,13 +422,13 @@
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
@@ -445,8 +439,8 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VoprfServer;
//! let mut server_rng = OsRng;
//! # use voprf_vx::VoprfServer;
//! let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! let VoprfServerBatchEvaluateResult { messages, proof } = server
//! .batch_blind_evaluate(&mut server_rng, &client_messages)
@@ -462,13 +456,13 @@
//! ```
//! # #[cfg(feature = "alloc")] {
//! # #[cfg(feature = "ristretto255")]
//! # type CipherSuite = voprf::Ristretto255;
//! # type CipherSuite = voprf_vx::Ristretto255;
//! # #[cfg(not(feature = "ristretto255"))]
//! # type CipherSuite = p256::NistP256;
//! # use voprf::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::OsRng, RngCore};
//! # use voprf_vx::{VoprfServerBatchEvaluateResult, VoprfClient};
//! # use rand::{rngs::SysRng, Rng};
//! #
//! # let mut client_rng = OsRng;
//! # let mut client_rng = SysRng;
//! # let mut client_states = vec![];
//! # let mut client_messages = vec![];
//! # for _ in 0..10 {
@@ -479,8 +473,8 @@
//! # client_states.push(client_blind_result.state);
//! # client_messages.push(client_blind_result.message);
//! # }
//! # use voprf::VoprfServer;
//! # let mut server_rng = OsRng;
//! # use voprf_vx::VoprfServer;
//! # let mut server_rng = SysRng;
//! # let server = VoprfServer::<CipherSuite>::new(&mut server_rng).unwrap();
//! # let VoprfServerBatchEvaluateResult { messages, proof } = server
//! # .batch_blind_evaluate(&mut server_rng, &client_messages)
@@ -512,7 +506,7 @@
//! and [PoprfClient] are used, and that each of the functions accept an
//! additional (and optional) info parameter which represents the public input.
//! 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.
//!
//! # Features
@@ -540,7 +534,7 @@
//! (https://docs.rs/curve25519-dalek/4.0.0-pre.5/curve25519_dalek/index.html#backends)
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(test), deny(unsafe_code))]
#![warn(
clippy::cargo,
+45 -177
View File
@@ -1,25 +1,19 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Contains the main OPRF API
use core::iter::{self, Map};
use core::iter::{self};
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser};
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use digest::Output;
use hybrid_array::Array;
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
derive_key_internal, deterministic_blind_unchecked, hash_to_group, i2osp_2,
server_evaluate_hash_input, BlindedElement, EvaluationElement, Mode, STR_FINALIZE,
BlindedElement, EvaluationElement, Mode, derive_key_internal, deterministic_blind_unchecked,
finalize_after_unblind, hash_to_group, server_evaluate_hash_input,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::Scalar;
@@ -44,11 +38,7 @@ use crate::{CipherSuite, Error, Group, Result};
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct OprfClient<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct OprfClient<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) blind: <CS::Group as Group>::Scalar,
}
@@ -62,11 +52,7 @@ where
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct OprfServer<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct OprfServer<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) sk: <CS::Group as Group>::Scalar,
}
@@ -76,21 +62,17 @@ where
// =================== //
/////////////////////////
impl<CS: CipherSuite> OprfClient<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> OprfClient<CS> {
/// Computes the first step for the multiplicative blinding version of
/// DH-OPRF.
///
/// # Errors
/// [`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],
blinding_factor_rng: &mut R,
) -> 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)
}
@@ -137,7 +119,7 @@ where
) -> Result<Output<CS::Hash>> {
let unblinded_element = evaluation_element.0 * &CS::Group::invert_scalar(self.blind);
let mut outputs =
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)), &[]);
finalize_after_unblind::<CS, _, _>(iter::once((input, unblinded_element)));
outputs.next().unwrap()
}
@@ -154,18 +136,14 @@ where
}
}
impl<CS: CipherSuite> OprfServer<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> OprfServer<CS> {
/// Produces a new instance of a [OprfServer] using a supplied RNG
///
/// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
rng.fill_bytes(&mut seed);
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
Self::new_from_seed(&seed, &[])
}
@@ -194,7 +172,7 @@ where
Ok(Self { sk })
}
// Only used for tests
/// Only used for tests
#[cfg(test)]
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
self.sk
@@ -231,58 +209,13 @@ where
/// 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)]
pub struct OprfClientBlindResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct OprfClientBlindResult<CS: CipherSuite> {
/// The state to be persisted on the client
pub state: OprfClient<CS>,
/// The message to send to the server
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 //
// ===== //
@@ -292,65 +225,38 @@ where
mod tests {
use core::ptr;
use rand::rngs::OsRng;
use rand::TryRng;
use rand::rngs::SysRng;
use super::*;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::tests::helpers::prf;
fn prf<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>,
{
fn base_retrieval<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap();
let message = server.blind_evaluate(&client_blind_result.message);
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);
}
fn base_inversion_unsalted<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let mut rng = OsRng;
fn base_inversion_unsalted<CS: CipherSuite>() {
let mut rng = SysRng;
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_finalize_result = client_blind_result
.state
.finalize(&input, &EvaluationElement(client_blind_result.message.0))
.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 res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)), &[])
let res2 = finalize_after_unblind::<CS, _, _>(iter::once((input.as_ref(), point)))
.next()
.unwrap()
.unwrap();
@@ -358,13 +264,9 @@ mod tests {
assert_eq!(client_finalize_result, res2);
}
fn server_evaluate<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn server_evaluate<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&client_blind_result.message);
@@ -386,13 +288,9 @@ mod tests {
assert!(client_finalize != server_evaluate);
}
fn zeroize_oprf_client<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn zeroize_oprf_client<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state;
@@ -404,13 +302,9 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0));
}
fn zeroize_oprf_server<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn zeroize_oprf_server<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = OprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = OprfServer::<CS>::new(&mut rng).unwrap();
let mut message = server.blind_evaluate(&client_blind_result.message);
@@ -423,37 +317,11 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0));
}
#[test]
fn test_functionality() -> Result<()> {
use p256::NistP256;
use p384::NistP384;
#[cfg(feature = "ristretto255")]
{
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(())
}
crate::tests::test_all_curves!(
base_retrieval,
base_inversion_unsalted,
server_evaluate,
zeroize_oprf_client,
zeroize_oprf_server,
);
}
+62 -178
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Contains the main POPRF API
@@ -13,16 +9,15 @@ use alloc::vec::Vec;
use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser};
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use hybrid_array::typenum::Unsigned;
use hybrid_array::{Array, ArraySize};
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
server_evaluate_hash_input, verify_proof, BlindedElement, Dst, EvaluationElement, Mode,
PreparedEvaluationElement, Proof, STR_FINALIZE, STR_HASH_TO_SCALAR, STR_INFO,
BlindedElement, Dst, EvaluationElement, Mode, PreparedEvaluationElement, Proof, STR_FINALIZE,
STR_HASH_TO_SCALAR, STR_INFO, derive_keypair, deterministic_blind_unchecked, generate_proof,
hash_to_group, i2osp_2, server_evaluate_hash_input, verify_proof,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -42,11 +37,7 @@ use crate::{CipherSuite, Error, Group, Result};
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct PoprfClient<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct PoprfClient<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) blind: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -62,11 +53,7 @@ where
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct PoprfServer<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct PoprfServer<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) sk: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -78,21 +65,17 @@ where
// =================== //
/////////////////////////
impl<CS: CipherSuite> PoprfClient<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> PoprfClient<CS> {
/// Computes the first step for the multiplicative blinding version of
/// DH-OPRF.
///
/// # Errors
/// [`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],
blinding_factor_rng: &mut R,
) -> 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)
}
@@ -145,7 +128,10 @@ where
proof: &Proof<CS>,
pk: <CS::Group as Group>::Elem,
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 messages = core::array::from_ref(evaluation_element);
@@ -180,6 +166,7 @@ where
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
<&'a IM as IntoIterator>::IntoIter: ExactSizeIterator,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{
let unblinded_elements = poprf_unblind(clients, messages, pk, proof, info)?;
@@ -193,18 +180,14 @@ where
}
}
impl<CS: CipherSuite> PoprfServer<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> PoprfServer<CS> {
/// Produces a new instance of a [PoprfServer] using a supplied RNG
///
/// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
rng.fill_bytes(&mut seed);
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
Self::new_from_seed(&seed, &[])
}
@@ -235,7 +218,7 @@ where
Ok(Self { sk, pk })
}
// Only used for tests
/// Only used for tests
#[cfg(test)]
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
self.sk
@@ -248,7 +231,7 @@ where
/// # Errors
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
/// - [`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,
rng: &mut R,
blinded_element: &BlindedElement<CS>,
@@ -286,7 +269,7 @@ where
/// - [`Error::Info`] if the `info` is longer than `u16::MAX`.
/// - [`Error::Protocol`] if the protocol fails and can't be completed.
#[cfg(feature = "alloc")]
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, IE>(
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, IE>(
&self,
rng: &mut R,
blinded_elements: &'a IE,
@@ -359,7 +342,7 @@ where
pub fn batch_blind_evaluate_finish<
'a,
'b,
R: RngCore + CryptoRng,
R: TryRng + TryCryptoRng,
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
IE,
>(
@@ -427,11 +410,7 @@ where
}
}
impl<CS: CipherSuite> BlindedElement<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> BlindedElement<CS> {
/// Creates a [BlindedElement] from a raw group element.
///
/// # Caution
@@ -450,11 +429,7 @@ where
}
}
impl<CS: CipherSuite> EvaluationElement<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> EvaluationElement<CS> {
/// Creates an [EvaluationElement] from a raw group element.
///
/// # Caution
@@ -480,11 +455,7 @@ where
/// Contains the fields that are returned by a verifiable client blind
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct PoprfClientBlindResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct PoprfClientBlindResult<CS: CipherSuite> {
/// The state to be persisted on the client
pub state: PoprfClient<CS>,
/// The message to send to the server
@@ -497,11 +468,7 @@ pub type PoprfClientBatchFinalizeResult<'a, CS, II, IC, IM> =
/// Contains the fields that are returned by a verifiable server evaluate
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct PoprfServerEvaluateResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct PoprfServerEvaluateResult<CS: CipherSuite> {
/// The message to send to the client
pub message: EvaluationElement<CS>,
/// The proof for the client to verify
@@ -511,11 +478,7 @@ where
/// 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)]
#[cfg(feature = "alloc")]
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct PoprfServerBatchEvaluateResult<CS: CipherSuite> {
/// The messages to send to the client
pub messages: Vec<EvaluationElement<CS>>,
/// The proof for the client to verify
@@ -545,19 +508,12 @@ pub type PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I> = Map<
pub struct PoprfPreparedTweak<CS: CipherSuite>(
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
<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
/// evaluate prepare
#[derive_where(Debug; I, <CS::Group as Group>::Scalar)]
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct PoprfServerBatchEvaluatePrepareResult<CS: CipherSuite, I> {
/// Prepared [`EvaluationElement`].
pub prepared_evaluation_elements: PoprfServerBatchEvaluatePreparedEvaluationElements<CS, I>,
/// Prepared tweak.
@@ -576,8 +532,6 @@ pub type PoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
pub struct PoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
{
/// The [`EvaluationElement`]s to send to the client
@@ -598,11 +552,7 @@ where
fn compute_tweaked_key<CS: CipherSuite>(
pk: <CS::Group as Group>::Elem,
info: Option<&[u8]>,
) -> Result<<CS::Group as Group>::Elem>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
) -> Result<<CS::Group as Group>::Elem> {
// None for info is treated the same as empty bytes
let info = info.unwrap_or_default();
@@ -615,7 +565,7 @@ where
let info_len = i2osp_2(info.len()).map_err(|_| Error::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.
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
@@ -636,11 +586,7 @@ where
fn compute_tweak<CS: CipherSuite>(
sk: <CS::Group as Group>::Scalar,
info: Option<&[u8]>,
) -> Result<<CS::Group as Group>::Scalar>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
) -> Result<<CS::Group as Group>::Scalar> {
// None for info is treated the same as empty bytes
let info = info.unwrap_or_default();
@@ -652,7 +598,7 @@ where
let info_len = i2osp_2(info.len()).map_err(|_| Error::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.
let m = CS::Group::hash_to_scalar::<CS::Hash>(&framed_info, &dst.as_dst()).unwrap();
@@ -691,8 +637,6 @@ fn poprf_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
info: Option<&[u8]>,
) -> Result<PoprfUnblindResult<'a, CS, IC, IM>>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a IC: 'a + IntoIterator<Item = &'a PoprfClient<CS>>,
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
@@ -720,7 +664,7 @@ where
)?;
Ok(blinds
.zip(messages.into_iter())
.zip(messages)
.map(|(blind, x)| x.0 * &CS::Group::invert_scalar(blind)))
}
@@ -728,7 +672,7 @@ type FinalizeAfterUnblindResult<'a, CS, IE, II> = Map<
Zip<Zip<IE, II>, Repeat<&'a [u8]>>,
fn(
((<<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
@@ -744,8 +688,7 @@ fn finalize_after_unblind<
info: Option<&'a [u8]>,
) -> Result<FinalizeAfterUnblindResult<'a, CS, IE, II>>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
<<CS as CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArraySize,
{
if unblinded_elements.len() != inputs.len() {
return Err(Error::Batch);
@@ -784,30 +727,23 @@ where
#[cfg(test)]
mod tests {
use core::ops::Add;
use core::ptr;
use generic_array::typenum::Sum;
use generic_array::ArrayLength;
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use super::*;
use crate::common::STR_HASH_TO_GROUP;
use crate::Group;
use crate::common::STR_HASH_TO_GROUP;
fn prf<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>,
{
) -> Output<CS::Hash> {
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();
// evaluatedElement = G.ScalarInverse(t) * blindedElement
@@ -820,14 +756,10 @@ mod tests {
.unwrap()
}
fn verifiable_retrieval<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn verifiable_retrieval<CS: CipherSuite>() {
let input = b"input";
let info = b"info";
let mut rng = OsRng;
let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server
@@ -847,21 +779,17 @@ mod tests {
assert_eq!(client_finalize_result, res2);
}
fn verifiable_bad_public_key<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn verifiable_bad_public_key<CS: CipherSuite>() {
let input = b"input";
let info = b"info";
let mut rng = OsRng;
let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server
.blind_evaluate(&mut rng, &client_blind_result.message, Some(info))
.unwrap();
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
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
};
@@ -875,14 +803,10 @@ mod tests {
assert!(client_finalize_result.is_err());
}
fn verifiable_server_evaluate<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn verifiable_server_evaluate<CS: CipherSuite>() {
let input = b"input";
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 server = PoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server
@@ -912,15 +836,9 @@ mod tests {
assert!(client_finalize != server_evaluate);
}
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>,
{
fn zeroize_verifiable_client<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state;
@@ -932,18 +850,10 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0));
}
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>,
{
fn zeroize_verifiable_server<CS: CipherSuite>() {
let input = b"input";
let info = b"info";
let mut rng = OsRng;
let mut rng = SysRng;
let server = PoprfServer::<CS>::new(&mut rng).unwrap();
let client_blind_result = PoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server_result = server
@@ -963,37 +873,11 @@ mod tests {
assert!(proof.serialize().iter().all(|&x| x == 0));
}
#[test]
fn test_functionality() -> Result<()> {
use p256::NistP256;
use p384::NistP384;
#[cfg(feature = "ristretto255")]
{
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(())
}
crate::tests::test_all_curves!(
verifiable_retrieval,
verifiable_bad_public_key,
verifiable_server_evaluate,
zeroize_verifiable_client,
zeroize_verifiable_server,
);
}
+299 -289
View File
@@ -1,296 +1,22 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Handles the serialization of each of the components used in the VOPRF
//! protocol
use core::ops::Add;
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 hybrid_array::Array;
use hybrid_array::typenum::{Sum, Unsigned};
use crate::{
BlindedElement, CipherSuite, Error, EvaluationElement, Group, OprfClient, OprfServer,
PoprfClient, PoprfServer, Proof, Result, VoprfClient, VoprfServer,
};
//////////////////////////////////////////////////////////
// Serialization and Deserialization for High-Level API //
// ==================================================== //
//////////////////////////////////////////////////////////
/// 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))
}
}
/////////////////////////////
// Deserialization Helpers //
// ======================= //
/////////////////////////////
fn deserialize_elem<G: Group>(input: &mut &[u8]) -> Result<G::Elem> {
let input = input
@@ -307,11 +33,11 @@ fn deserialize_scalar<G: Group>(input: &mut &[u8]) -> Result<G::Scalar> {
}
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] {
fn take_ext(self: &mut &Self, take: usize) -> Option<&Self> {
fn take_ext<'a>(self: &mut &'a Self, take: usize) -> Option<&'a Self> {
if take > self.len() {
return None;
}
@@ -322,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")]
pub(crate) mod serde {
use core::marker::PhantomData;
use generic_array::GenericArray;
use hybrid_array::Array;
use serde::de::{Deserializer, Error};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
@@ -340,7 +218,7 @@ pub(crate) mod serde {
where
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))
}
@@ -359,7 +237,7 @@ pub(crate) mod serde {
where
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))
}
@@ -382,6 +260,7 @@ mod test {
VoprfClient, VoprfServer,
};
// Fuzz: no panics on arbitrary input
macro_rules! test_deserialize {
($item:ident, $bytes:ident) => {
#[cfg(feature = "ristretto255")]
@@ -389,11 +268,43 @@ mod test {
let _ = $item::<crate::Ristretto255>::deserialize(&$bytes[..]);
}
let _ = $item::<p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<p384::NistP384>::deserialize(&$bytes[..]);
let _ = $item::<::p256::NistP256>::deserialize(&$bytes[..]);
let _ = $item::<::p384::NistP384>::deserialize(&$bytes[..]);
let _ = $item::<::p521::NistP521>::deserialize(&$bytes[..]);
};
}
// Roundtrip: serialize to deserialize == original
macro_rules! test_roundtrip {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let recovered = $item::<$cs>::deserialize(&bytes).expect("roundtrip deserialize");
assert_eq!(original.serialize(), recovered.serialize());
}};
}
// Trailing bytes: valid serialization + extra byte must fail
macro_rules! test_trailing {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let mut extended = bytes.to_vec();
extended.push(0x00);
assert!($item::<$cs>::deserialize(&extended).is_err());
}};
}
// Truncated: valid serialization minus one byte must fail
macro_rules! test_truncated {
($item:ident, $cs:ty, $constructor:expr) => {{
let original = $constructor;
let bytes = original.serialize();
let truncated = &bytes[..bytes.len() - 1];
assert!($item::<$cs>::deserialize(truncated).is_err());
}};
}
proptest! {
#[test]
fn test_nocrash_oprf_client(bytes in vec(any::<u8>(), 0..200)) {
@@ -441,4 +352,103 @@ mod test {
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);
}
+63 -177
View File
@@ -1,30 +1,23 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! The VOPRF test vectors taken from:
//! https://github.com/cfrg/draft-irtf-cfrg-voprf/blob/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#"
## ristretto255-SHA512
A.1. ristretto255-SHA512
### OPRF Mode
A.1.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
skSm = 5ebcea5ee37023ccb9fc2d2019f9d7737be85591ae8652ffa9ef0f4d37063
b0e
~~~
#### Test Vector 1, Batch Size 1
A.1.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706
@@ -35,11 +28,9 @@ d8cc917ea0869c7e
Output = 527759c3d9366f277d8c6020418d96bb393ba2afb20ff90df23fb770826
4e2f3ab9135e3bd69955851de4b1f9fe8a0973396719b7912ba9ee8aa7d0b5e24bcf
6
~~~
#### Test Vector 2, Batch Size 1
A.1.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706
@@ -50,11 +41,9 @@ EvaluationElement = b4cbf5a4f1eeda5a63ce7b77c7d23f461db3fcab0dd28e4e
Output = f4a74c9c592497375e796aa837e907b1a045d34306a749db9f34221f7e7
50cb4f2a6413a6bf6fa5e19ba6348eb673934a722a7ede2e7621306d18951e7cf2c7
3
~~~
### VOPRF Mode
A.1.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -62,11 +51,9 @@ skSm = e6f73f344b79b379f1a0dd37e07ff62e38d9f71345ce62ae3a9bc60b04ccd
909
pkSm = c803e2cc6b05fc15064549b5920659ca4a77b2cca6f04f6b357009335476a
d4e
~~~
#### Test Vector 1, Batch Size 1
A.1.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706
@@ -81,11 +68,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
c
~~~
#### Test Vector 2, Batch Size 1
A.1.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706
@@ -100,11 +85,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = 8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a
6df60356f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b
6
~~~
#### Test Vector 3, Batch Size 2
A.1.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
6706,222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d9881aa6f61d645fc0
@@ -123,11 +106,9 @@ Output = b58cfbe118e0cb94d79b5fd6a6dafb98764dff49c14e1770b566e42402d
a1a7da4d8527693914139caee5bd03903af43a491351d23b430948dd50cde10d32b3
c,8a9a2f3c7f085b65933594309041fc1898d42d0858e59f90814ae90571a6df6035
6f4610bf816f27afdd84f47719e480906d27ecd994985890e5f539e7ea74b6
~~~
### POPRF Mode
A.1.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -135,11 +116,9 @@ skSm = 145c79c108538421ac164ecbe131942136d5570b16d8bf41a24d4337da981
e07
pkSm = c647bef38497bc6ec077c22af65b696efa43bff3b4a1975a3e8e0a1c5a79d
631
~~~
#### Test Vector 1, Batch Size 1
A.1.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
@@ -155,11 +134,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
1
~~~
#### Test Vector 2, Batch Size 1
A.1.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
@@ -175,11 +152,9 @@ ProofRandomScalar = 222a5e897cf59db8145db8d16e597e8facb80ae7d4e26d98
Output = 7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b
56a52de2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae50
7
~~~
#### Test Vector 3, Batch Size 2
A.1.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec4c1f
@@ -199,23 +174,19 @@ Output = ca688351e88afb1d841fde4401c79efebb2eb75e7998fa9737bd5a82a15
2406d38bd29f680504e54fd4587eddcf2f37a2617ac2fbd2993f7bdf45442ace7d22
1,7c6557b276a137922a0bcfc2aa2b35dd78322bd500235eb6d6b6f91bc5b56a52de
2d65612d503236b321f5d0bebcbc52b64b92e426f29c9b8b69f52de98ae507
~~~
## decaf448-SHAKE256
A.2. decaf448-SHAKE256
### OPRF Mode
A.2.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
skSm = e8b1375371fd11ebeb224f832dcc16d371b4188951c438f751425699ed29e
cc80c6c13e558ccd67634fd82eac94aa8d1f0d7fee990695d1e
~~~
#### Test Vector 1, Batch Size 1
A.2.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -226,11 +197,9 @@ EvaluationElement = 50ce4e60eed006e22e7027454b5a4b8319eb2bc8ced609eb
Output = 37d3f7922d9388a15b561de5829bbf654c4089ede89c0ce0f3f85bcdba0
9e382ce0ab3507e021f9e79706a1798ffeac68ebd5cf62e5eb9838c7068351d97ae3
7
~~~
#### Test Vector 2, Batch Size 1
A.2.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -241,11 +210,9 @@ EvaluationElement = 162e9fa6e9d527c3cd734a31bf122a34dbd5bcb7bb23651f
Output = a2a652290055cb0f6f8637a249ee45e32ef4667db0b4c80c0a70d2a6416
4d01525cfdad5d870a694ec77972b9b6ec5d2596a5223e5336913f945101f0137f55
e
~~~
### VOPRF Mode
A.2.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -253,11 +220,9 @@ skSm = e3c01519a076a326a0eb566343e9b21c115fa18e6e85577ddbe890b33104f
cc2835ddfb14a928dc3f5d79b936e17c76b99e0bf6a1680930e
pkSm = 945fc518c47695cf65217ace04b86ac5e4cbe26ca649d52854bb16c494ce0
9069d6add96b20d4b0ae311a87c9a73e3a146b525763ab2f955
~~~
#### Test Vector 1, Batch Size 1
A.2.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -274,11 +239,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
1
~~~
#### Test Vector 2, Batch Size 1
A.2.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112
@@ -295,11 +258,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = 862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380
c959baa8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c94
1
~~~
#### Test Vector 3, Batch Size 2
A.2.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
3833a26e9388336361686ff1f83df55046504dfecad8549ba112,b1b748135d405ce
@@ -323,11 +284,9 @@ Output = e2ac40b634f36cccd8262b285adff7c9dcc19cd308564a5f4e581d1a853
5773b86fa4fc9f2203c370763695c5093aea4a7aedec4488b1340ba3bf663a23098c
1,862952380e07ec840d9f6e6f909c5a25d16c3dacb586d89a181b4aa7380c959baa
8c480fe8e6c64e089d68ea7aeeb5817bd524d7577905b5bab487690048c941
~~~
### POPRF Mode
A.2.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -335,11 +294,9 @@ skSm = 792a10dcbd3ba4a52a054f6f39186623208695301e7adb9634b74709ab22d
e402990eb143fd7c67ac66be75e0609705ecea800992aac8e19
pkSm = 6c9d12723a5bbcf305522cc04b4a34d9ced2e12831826018ea7b5dcf54526
47ad262113059bf0f6e4354319951b9d513c74f29cb0eec38c1
~~~
#### Test Vector 1, Batch Size 1
A.2.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
@@ -357,11 +314,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
d
~~~
#### Test Vector 2, Batch Size 1
A.2.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
@@ -379,11 +334,9 @@ ProofRandomScalar = b1b748135d405ce48c6973401d9455bb8ccd18b01d0295c0
Output = 8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf63
3126de0c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8
d
~~~
#### Test Vector 3, Batch Size 2
A.2.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 64d37aed22a27f5191de1c1d69fadb899d8862b58eb4220029e036ec65fa
@@ -408,23 +361,19 @@ Output = 4423f6dcc1740688ea201de57d76824d59cd6b859e1f9884b7eebc49b0b
971358cf9cb075df1536a8ea31bcf55c3e31c2ba9cfa8efe54448d17091daeb9924e
d,8691905500510843902c44bdd9730ab9dc3925aa58ff9dd42765a2baf633126de0
c3adb93bef5652f38e5827b6396e87643960163a560fc4ac9738c8de4e4a8d
~~~
## P256-SHA256
A.3. P256-SHA256
### OPRF Mode
A.3.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
skSm = 159749d750713afe245d2d39ccfaae8381c53ce92d098a9375ee70739c7ac
0bf
~~~
#### Test Vector 1, Batch Size 1
A.3.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364
@@ -434,11 +383,9 @@ EvaluationElement = 030de02ffec47a1fd53efcdd1c6faf5bdc270912b8749e78
3c7ca75bb412958832
Output = a0b34de5fa4c5b6da07e72af73cc507cceeb48981b97b7285fc375345fe
495dd
~~~
#### Test Vector 2, Batch Size 1
A.3.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364
@@ -448,11 +395,9 @@ EvaluationElement = 03a0395fe3828f2476ffcd1f4fe540e5a8489322d398be3c
4e5a869db7fcb7c52c
Output = c748ca6dd327f0ce85f4ae3a8cd6d4d5390bbb804c9e12dcf94f853fece
3dcce
~~~
### VOPRF Mode
A.3.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -460,11 +405,9 @@ skSm = ca5d94c8807817669a51b196c34c1b7f8442fde4334a7121ae4736364312f
ca6
pkSm = 03e17e70604bcabe198882c0a1f27a92441e774224ed9c702e51dd17038b1
02462
~~~
#### Test Vector 1, Batch Size 1
A.3.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364
@@ -478,11 +421,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
645a1
~~~
#### Test Vector 2, Batch Size 1
A.3.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364
@@ -496,11 +437,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1
Output = 771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c
24f18
~~~
#### Test Vector 3, Batch Size 2
A.3.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
d364,f9db001266677f62c095021db018cd8cbb55941d4073698ce45c405d1348b7b
@@ -518,11 +457,9 @@ ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
Output = 0412e8f78b02c415ab3a288e228978376f99927767ff37c5718d420010a
645a1,771e10dcd6bcd3664e23b8f2a710cfaaa8357747c4a8cbba03133967b5c24f
18
~~~
### POPRF Mode
A.3.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -530,11 +467,9 @@ skSm = 6ad2173efa689ef2c27772566ad7ff6e2d59b3b196f00219451fb2c89ee4d
ae2
pkSm = 030d7ff077fddeec965db14b794f0cc1ba9019b04a2f4fcc1fa525dedf72e
2a3e3
~~~
#### Test Vector 1, Batch Size 1
A.3.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Info = 7465737420696e666f
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -549,11 +484,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
5c592
~~~
#### Test Vector 2, Batch Size 1
A.3.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -568,11 +501,9 @@ ProofRandomScalar = f9db001266677f62c095021db018cd8cbb55941d4073698c
e45c405d1348b7b1
Output = 1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5f
fce8c
~~~
#### Test Vector 3, Batch Size 2
A.3.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 3338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -591,23 +522,19 @@ ProofRandomScalar = 350e8040f828bf6ceca27405420cdf3d63cb3aef005f40ba
Output = 193a92520bd8fd1f37accb918040a57108daa110dc4f659abe212636d24
5c592,1e6d164cfd835d88a31401623549bf6b9b306628ef03a7962921d62bc5ffce
8c
~~~
## P384-SHA384
A.4. P384-SHA384
### OPRF Mode
A.4.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
skSm = dfe7ddc41a4646901184f2b432616c8ba6d452f9bcd0c4f75a5150ef2b2ed
02ef40b8b92f60ae591bcabd72a6518f188
~~~
#### Test Vector 1, Batch Size 1
A.4.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364
@@ -617,11 +544,9 @@ EvaluationElement = 03af2a4fc94770d7a7bf3187ca9cc4faf3732049eded2442
ee50fbddda58b70ae2999366f72498cdbc43e6f2fc184afe30
Output = ed84ad3f31a552f0456e58935fcc0a3039db42e7f356dcb32aa6d487b6b
815a07d5813641fb1398c03ddab5763874357
~~~
#### Test Vector 2, Batch Size 1
A.4.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364
@@ -631,11 +556,9 @@ EvaluationElement = 034e9b9a2960b536f2ef47d8608b21597ba400d5abfa1825
fd21c36b75f927f396bf3716c96129d1fa4a77fa1d479c8d7b
Output = dd4f29da869ab9355d60617b60da0991e22aaab243a3460601e48b07585
9d1c526d36597326f1b985778f781a1682e75
~~~
### VOPRF Mode
A.4.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -643,11 +566,9 @@ skSm = 051646b9e6e7a71ae27c1e1d0b87b4381db6d3595eeeb1adb41579adbf992
f4278f9016eafc944edaa2b43183581779d
pkSm = 031d689686c611991b55f1a1d8f4305ccd6cb719446f660a30db61b7aa87b
46acf59b7c0d4a9077b3da21c25dd482229a0
~~~
#### Test Vector 1, Batch Size 1
A.4.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364
@@ -662,11 +583,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
26b4a622beab60220bf19078bca35a529b35c
~~~
#### Test Vector 2, Batch Size 1
A.4.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364
@@ -681,11 +600,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = b91c70ea3d4d62ba922eb8a7d03809a441e1c3c7af915cbc2226f485213
e895942cd0f8580e6d99f82221e66c40d274f
~~~
#### Test Vector 3, Batch Size 2
A.4.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
889d89dbfa691d1cde91517fa222ed7ad364,803d955f0e073a04aa5d92b3fb739f5
@@ -706,11 +623,9 @@ eca27405420cdf3d63cb3aef005f40ba51943c8026877963
Output = 3333230886b562ffb8329a8be08fea8025755372817ec969d114d1203d0
26b4a622beab60220bf19078bca35a529b35c,b91c70ea3d4d62ba922eb8a7d03809
a441e1c3c7af915cbc2226f485213e895942cd0f8580e6d99f82221e66c40d274f
~~~
### POPRF Mode
A.4.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -718,11 +633,9 @@ skSm = 5b2690d6954b8fbb159f19935d64133f12770c00b68422559c65431942d72
1ff79d47d7a75906c30b7818ec0f38b7fb2
pkSm = 02f00f0f1de81e5d6cf18140d4926ffdc9b1898c48dc49657ae36eb1e45de
b8b951aaf1f10c82d2eaa6d02aafa3f10d2b6
~~~
#### Test Vector 1, Batch Size 1
A.4.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Info = 7465737420696e666f
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
@@ -738,11 +651,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
27303ed449a08caf84272c3bbc972ede797df
~~~
#### Test Vector 2, Batch Size 1
A.4.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
@@ -758,11 +669,9 @@ ProofRandomScalar = 803d955f0e073a04aa5d92b3fb739f56f9db001266677f62
c095021db018cd8cbb55941d4073698ce45c405d1348b7b1
Output = ff2a527a21cc43b251a567382677f078c6e356336aec069dea8ba369953
43ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
~~~
#### Test Vector 3, Batch Size 2
A.4.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 504650f53df8f16f6861633388936ea23338fa65ec36e0290022b48eb562
@@ -784,24 +693,20 @@ eca27405420cdf3d63cb3aef005f40ba51943c8026877963
Output = 0188653cfec38119a6c7dd7948b0f0720460b4310e40824e048bf82a165
27303ed449a08caf84272c3bbc972ede797df,ff2a527a21cc43b251a567382677f0
78c6e356336aec069dea8ba36995343ca3b33bb5d6cf15be4d31a7e6d75b30d3f5
~~~
## P521-SHA512
A.5. P521-SHA512
### OPRF Mode
A.5.1. OPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
skSm = 0153441b8faedb0340439036d6aed06d1217b34c42f17f8db4c5cc610a4a9
55d698a688831b16d0dc7713a1aa3611ec60703bffc7dc9c84e3ed673b3dbe1d5fcc
ea6
~~~
#### Test Vector 1, Batch Size 1
A.5.1.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -815,11 +720,9 @@ EvaluationElement = 030166371cf827cb2fb9b581f97907121a16e2dc5d8b10ce
Output = 26232de6fff83f812adadadb6cc05d7bbeee5dca043dbb16b03488abb99
81d0a1ef4351fad52dbd7e759649af393348f7b9717566c19a6b8856284d69375c80
9
~~~
#### Test Vector 2, Batch Size 1
A.5.1.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -833,11 +736,9 @@ af5640cbb2d9620a6e
Output = ad1f76ef939042175e007738906ac0336bbd1d51e287ebaa66901abdd32
4ea3ffa40bfc5a68e7939c2845e0fd37a5a6e76dadb9907c6cc8579629757fd4d04b
a
~~~
### VOPRF Mode
A.5.2. VOPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -847,11 +748,9 @@ skSm = 015c7fc1b4a0b1390925bae915bd9f3d72009d44d9241b962428aad5d13f2
pkSm = 0301505d646f6e4c9102451eb39730c4ba1c4087618641edbdba4a60896b0
7fd0c9414ce553cbf25b81dfcca50a8f6724ab7a2bc4d0cf736967a287bb6084cc06
78ac0
~~~
#### Test Vector 1, Batch Size 1
A.5.2.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -872,11 +771,9 @@ e45c405d1348b7b1
Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
b
~~~
#### Test Vector 2, Batch Size 1
A.5.2.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -897,11 +794,9 @@ e45c405d1348b7b1
Output = fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b
54b6604d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf47
4
~~~
#### Test Vector 3, Batch Size 2
A.5.2.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
88936ea23338fa65ec36e0290022b48eb562889d89dbfa691d1cde91517fa222ed7a
@@ -929,11 +824,9 @@ Output = 5e003d9b2fb540b3d4bab5fedd154912246da1ee5e557afd8f56415faa1
a0fadff6517da802ee254437e4f60907b4cda146e7ba19e249eef7be405549f62954
b,fa15eebba81ecf40954f7135cb76f69ef22c6bae394d1a4362f9b03066b54b6604
d39f2e53369ca6762a3d9787e230e832aa85955af40ecb8deebb009a8cf474
~~~
### POPRF Mode
A.5.3. POPRF Mode
~~~
Seed = a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a
3a3
KeyInfo = 74657374206b6579
@@ -943,11 +836,9 @@ b27
pkSm = 0301de8ceb9ffe9237b1bba87c320ea0bebcfc3447fe6f278065c6c69886d
692d1126b79b6844f829940ace9b52a5e26882cf7cbc9e57503d4cca3cd834584729
f812a
~~~
#### Test Vector 1, Batch Size 1
A.5.3.1. Test Vector 1, Batch Size 1
~~~
Input = 00
Info = 7465737420696e666f
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
@@ -969,11 +860,9 @@ e45c405d1348b7b1
Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
b
~~~
#### Test Vector 2, Batch Size 1
A.5.3.2. Test Vector 2, Batch Size 1
~~~
Input = 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
@@ -995,11 +884,9 @@ e45c405d1348b7b1
Output = 27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af
5762c3638afae7912a6c1acdb1ae2ab2fa670bd5486c645a0e55412e08d33a4a0d6e
3
~~~
#### Test Vector 3, Batch Size 2
A.5.3.3. Test Vector 3, Batch Size 2
~~~
Input = 00,5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a
Info = 7465737420696e666f
Blind = 00d1dccf7a51bafaf75d4a866d53d8cafe4d504650f53df8f16f68616333
@@ -1028,5 +915,4 @@ Output = 808ae5b87662eaaf0b39151dd85991b94c96ef214cb14a68bf5c1439548
82d330da8953a80eea20788e552bc8bbbfff3100e89f9d6e341197b122c46a208733
b,27032e24b1a52a82ab7f4646f3c5df0f070f499db98b9c5df33972bd5af5762c36
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;
+25 -22
View File
@@ -1,15 +1,12 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use alloc::vec::Vec;
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.
///
@@ -38,29 +35,35 @@ fn rotate_left<T>(data: &mut [T], steps: usize) {
data.reverse();
}
impl RngCore for CycleRng {
fn next_u32(&mut self) -> u32 {
unimplemented!()
impl TryRng for CycleRng {
type Error = Infallible;
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 next_u64(&mut self) -> u64 {
unimplemented!()
fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut buf = [0u8; 8];
self.try_fill_bytes(&mut buf)?;
Ok(u64::from_le_bytes(buf))
}
#[inline]
fn fill_bytes(&mut self, dest: &mut [u8]) {
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
let len = min(self.v.len(), dest.len());
dest[..len].copy_from_slice(&self.v[..len]);
rotate_left(&mut self.v, len);
}
#[inline]
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
self.fill_bytes(dest);
dest[..len].copy_from_slice(&self.v[..len]);
rotate_left(&mut self.v, len);
Ok(())
}
}
// This is meant for testing only
impl CryptoRng for CycleRng {}
impl TryCryptoRng for CycleRng {}
+7 -6
View File
@@ -1,12 +1,13 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
mod cfrg_vectors;
mod macros;
mod mock_rng;
mod parser;
mod test_cfrg_vectors;
pub(crate) mod helpers;
pub(crate) use macros::test_all_curves;
+9 -18
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use alloc::string::{String, ToString};
use alloc::vec::Vec;
@@ -15,55 +11,50 @@ pub(crate) fn rfc_to_json(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 chunks: Vec<&str> = re.split(input).collect();
let mut count = 1;
for caps in re.captures_iter(input) {
for (count, caps) in (1..).zip(re.captures_iter(input)) {
let ciphersuite = format!(
"\"{}\": {{ {} }}",
&caps["ciphersuite"],
parse_modes(chunks[count])
);
ciphersuites.push(ciphersuite);
count += 1;
}
ciphersuites.join(",\n")
}
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 chunks: Vec<&str> = re.split(input).collect();
let mut count = 1;
for caps in re.captures_iter(input) {
for (count, caps) in (1..).zip(re.captures_iter(input)) {
let mode = format!(
"\"{}\": [\n {} \n]",
&caps["mode"],
parse_vectors(chunks[count])
);
modes.push(mode);
count += 1;
}
modes.join(",\n")
}
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 chunks: Vec<&str> = re.split(input).collect();
let init_params = parse_params(chunks[0]);
let mut count = 1;
for _ in re.captures_iter(input) {
for (count, _) in (1..).zip(re.captures_iter(input)) {
let params = format!("{{\n{},\n{}\n}}", init_params, parse_params(chunks[count]));
vectors.push(params);
count += 1;
}
vectors.join(",\n")
+48 -94
View File
@@ -1,20 +1,11 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use alloc::string::String;
use alloc::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 crate::tests::mock_rng::CycleRng;
@@ -89,6 +80,7 @@ macro_rules! json_to_test_vectors {
fn test_vectors() -> Result<()> {
use p256::NistP256;
use p384::NistP384;
use p521::NistP521;
let rfc: Value = serde_json::from_str(rfc_to_json(super::cfrg_vectors::VECTORS).as_str())
.expect("Could not parse json");
@@ -188,14 +180,37 @@ fn test_vectors() -> Result<()> {
test_poprf_finalize::<NistP384>(&p384_poprf_tvs)?;
test_poprf_evaluate::<NistP384>(&p384_poprf_tvs)?;
let p521_oprf_tvs =
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("OPRF"));
assert_ne!(p521_oprf_tvs.len(), 0);
test_oprf_seed_to_key::<NistP521>(&p521_oprf_tvs)?;
test_oprf_blind::<NistP521>(&p521_oprf_tvs)?;
test_oprf_blind_evaluate::<NistP521>(&p521_oprf_tvs)?;
test_oprf_finalize::<NistP521>(&p521_oprf_tvs)?;
test_oprf_evaluate::<NistP521>(&p521_oprf_tvs)?;
let p521_voprf_tvs =
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("VOPRF"));
assert_ne!(p521_voprf_tvs.len(), 0);
test_voprf_seed_to_key::<NistP521>(&p521_voprf_tvs)?;
test_voprf_blind::<NistP521>(&p521_voprf_tvs)?;
test_voprf_blind_evaluate::<NistP521>(&p521_voprf_tvs)?;
test_voprf_finalize::<NistP521>(&p521_voprf_tvs)?;
test_voprf_evaluate::<NistP521>(&p521_voprf_tvs)?;
let p521_poprf_tvs =
json_to_test_vectors!(rfc, String::from("P521-SHA512"), String::from("POPRF"));
assert_ne!(p521_poprf_tvs.len(), 0);
test_poprf_seed_to_key::<NistP521>(&p521_poprf_tvs)?;
test_poprf_blind::<NistP521>(&p521_poprf_tvs)?;
test_poprf_blind_evaluate::<NistP521>(&p521_poprf_tvs)?;
test_poprf_finalize::<NistP521>(&p521_poprf_tvs)?;
test_poprf_evaluate::<NistP521>(&p521_poprf_tvs)?;
Ok(())
}
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>,
{
fn test_oprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let server = OprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;
@@ -207,11 +222,7 @@ where
Ok(())
}
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>,
{
fn test_voprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let server = VoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;
@@ -227,11 +238,7 @@ where
Ok(())
}
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>,
{
fn test_poprf_seed_to_key<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let server = PoprfServer::<CS>::new_from_seed(&parameters.seed, &parameters.key_info)?;
@@ -248,11 +255,7 @@ where
}
// Tests input -> blind, blinded_element
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_oprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
@@ -273,11 +276,7 @@ where
}
// Tests input -> blind, blinded_element
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_voprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
@@ -298,11 +297,7 @@ where
}
// Tests input -> blind, blinded_element
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_poprf_blind<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let blind = CS::Group::deserialize_scalar(&parameters.blind[i])?;
@@ -323,11 +318,7 @@ where
}
// Tests sksm, blinded_element -> evaluation_element
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_oprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -344,13 +335,7 @@ where
Ok(())
}
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>,
{
fn test_voprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -371,18 +356,12 @@ where
assert_eq!(&parameter, &message.serialize().as_slice());
}
assert_eq!(&parameters.proof, &proof.serialize().as_slice());
assert_eq!(&parameters.proof, &proof.serialize().to_vec());
}
Ok(())
}
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>,
{
fn test_poprf_blind_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let mut rng = CycleRng::new(parameters.proof_random_scalar.clone());
let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -403,8 +382,7 @@ where
blinded_elements.iter(),
&prepared_evaluation_elements,
&prepared_tweak,
)
.unwrap();
)?;
let messages: Vec<_> = messages.collect();
@@ -412,17 +390,13 @@ where
assert_eq!(&parameter, &message.serialize().as_slice());
}
assert_eq!(&parameters.proof, &proof.serialize().as_slice());
assert_eq!(&parameters.proof, &proof.serialize().to_vec());
}
Ok(())
}
// Tests input, blind, evaluation_element -> output
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_oprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let client =
@@ -439,11 +413,7 @@ where
Ok(())
}
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_voprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let mut clients = vec![];
for i in 0..parameters.input.len() {
@@ -478,11 +448,7 @@ where
Ok(())
}
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_poprf_finalize<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
let mut clients = vec![];
for i in 0..parameters.input.len() {
@@ -516,11 +482,7 @@ where
}
// Tests input, sksm -> output
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_oprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = OprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -533,11 +495,7 @@ where
Ok(())
}
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_voprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = VoprfServer::<CS>::new_with_key(&parameters.sksm)?;
@@ -550,11 +508,7 @@ where
Ok(())
}
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn test_poprf_evaluate<CS: CipherSuite>(tvs: &[VOPRFTestVectorParameters]) -> Result<()> {
for parameters in tvs {
for i in 0..parameters.input.len() {
let server = PoprfServer::<CS>::new_with_key(&parameters.sksm)?;
+59 -218
View File
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) VexaHub and contributors.
// Copyright (c) Meta Platforms, Inc. and affiliates.
//
// This source code is dual-licensed under either the MIT license found in the
// LICENSE-MIT file in the root directory of this source tree or the Apache
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
//! Contains the main VOPRF API
@@ -13,16 +9,14 @@ use alloc::vec::Vec;
use core::iter::{self, Map, Repeat, Zip};
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output, OutputSizeUser};
use generic_array::typenum::{IsLess, IsLessOrEqual, Unsigned, U256};
use generic_array::GenericArray;
use rand_core::{CryptoRng, RngCore};
use digest::Output;
use hybrid_array::Array;
use rand_core::{TryCryptoRng, TryRng};
use crate::common::{
derive_keypair, deterministic_blind_unchecked, generate_proof, hash_to_group, i2osp_2,
server_evaluate_hash_input, verify_proof, BlindedElement, EvaluationElement, Mode,
PreparedEvaluationElement, Proof, STR_FINALIZE,
BlindedElement, EvaluationElement, FinalizeAfterUnblindResult, Mode, PreparedEvaluationElement,
Proof, derive_keypair, deterministic_blind_unchecked, finalize_after_unblind, generate_proof,
hash_to_group, server_evaluate_hash_input, verify_proof,
};
#[cfg(feature = "serde")]
use crate::serialization::serde::{Element, Scalar};
@@ -42,11 +36,7 @@ use crate::{CipherSuite, Error, Group, Result};
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct VoprfClient<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct VoprfClient<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) blind: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -62,11 +52,7 @@ where
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
pub struct VoprfServer<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct VoprfServer<CS: CipherSuite> {
#[cfg_attr(feature = "serde", serde(with = "Scalar::<CS::Group>"))]
pub(crate) sk: <CS::Group as Group>::Scalar,
#[cfg_attr(feature = "serde", serde(with = "Element::<CS::Group>"))]
@@ -78,21 +64,17 @@ where
// =================== //
/////////////////////////
impl<CS: CipherSuite> VoprfClient<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> VoprfClient<CS> {
/// Computes the first step for the multiplicative blinding version of
/// DH-OPRF.
///
/// # Errors
/// [`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],
blinding_factor_rng: &mut R,
) -> 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)
}
@@ -161,7 +143,7 @@ where
///
/// The resulting messages can each fail individually with [`Error::Input`]
/// if the `input` is empty or longer then [`u16::MAX`].
pub fn batch_finalize<'a, I: 'a, II, IC, IM>(
pub fn batch_finalize<'a, I, II, IC, IM>(
inputs: &'a II,
clients: &'a IC,
messages: &'a IM,
@@ -170,7 +152,7 @@ where
) -> Result<VoprfClientBatchFinalizeResult<'a, CS, I, II, IC, IM>>
where
CS: 'a,
I: AsRef<[u8]>,
I: 'a + AsRef<[u8]>,
&'a II: 'a + IntoIterator<Item = I>,
<&'a II as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
@@ -197,25 +179,21 @@ where
}
}
// Only used for test functions
/// Only used for test functions
#[cfg(test)]
pub fn get_blind(&self) -> <CS::Group as Group>::Scalar {
self.blind
}
}
impl<CS: CipherSuite> VoprfServer<CS>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
impl<CS: CipherSuite> VoprfServer<CS> {
/// Produces a new instance of a [VoprfServer] using a supplied RNG
///
/// # Errors
/// [`Error::Protocol`] if the protocol fails and can't be completed.
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = GenericArray::<_, <CS::Group as Group>::ScalarLen>::default();
rng.fill_bytes(&mut seed);
pub fn new<R: TryRng + TryCryptoRng>(rng: &mut R) -> Result<Self> {
let mut seed = Array::<_, <CS::Group as Group>::ScalarLen>::default();
rng.try_fill_bytes(&mut seed).map_err(|_| Error::Protocol)?;
// This can't fail as the hash output is type constrained.
Self::new_from_seed(&seed, &[])
}
@@ -246,7 +224,7 @@ where
Ok(Self { sk, pk })
}
// Only used for tests
/// Only used for tests
#[cfg(test)]
pub fn get_private_key(&self) -> <CS::Group as Group>::Scalar {
self.sk
@@ -255,7 +233,7 @@ where
/// Computes the second step for the multiplicative blinding version of
/// DH-OPRF. This message is sent from the server (who holds the OPRF key)
/// to the client.
pub fn blind_evaluate<R: RngCore + CryptoRng>(
pub fn blind_evaluate<R: TryRng + TryCryptoRng>(
&self,
rng: &mut R,
blinded_element: &BlindedElement<CS>,
@@ -288,7 +266,7 @@ where
/// [`Error::Batch`] if the number of `blinded_elements` and
/// `evaluation_elements` don't match or is longer then [`u16::MAX`]
#[cfg(feature = "alloc")]
pub fn batch_blind_evaluate<'a, R: RngCore + CryptoRng, I>(
pub fn batch_blind_evaluate<'a, R: TryRng + TryCryptoRng, I>(
&self,
rng: &mut R,
blinded_elements: &'a I,
@@ -339,7 +317,7 @@ where
pub fn batch_blind_evaluate_finish<
'a,
'b,
R: RngCore + CryptoRng,
R: TryRng + TryCryptoRng,
IB: Iterator<Item = &'a BlindedElement<CS>> + ExactSizeIterator,
IE,
>(
@@ -402,11 +380,7 @@ where
/// Contains the fields that are returned by a verifiable client blind
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct VoprfClientBlindResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct VoprfClientBlindResult<CS: CipherSuite> {
/// The state to be persisted on the client
pub state: VoprfClient<CS>,
/// The message to send to the server
@@ -423,11 +397,7 @@ pub type VoprfClientBatchFinalizeResult<'a, C, I, II, IC, IM> = FinalizeAfterUnb
/// Contains the fields that are returned by a verifiable server evaluate
#[derive_where(Debug; <CS::Group as Group>::Scalar, <CS::Group as Group>::Elem)]
pub struct VoprfServerEvaluateResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct VoprfServerEvaluateResult<CS: CipherSuite> {
/// The message to send to the client
pub message: EvaluationElement<CS>,
/// The proof for the client to verify
@@ -437,11 +407,7 @@ where
/// 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)]
#[cfg(feature = "alloc")]
pub struct VoprfServerBatchEvaluateResult<CS: CipherSuite>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
pub struct VoprfServerBatchEvaluateResult<CS: CipherSuite> {
/// The messages to send to the client
pub messages: Vec<EvaluationElement<CS>>,
/// The proof for the client to verify
@@ -472,8 +438,6 @@ pub type VoprfServerBatchEvaluateFinishedMessages<'a, CS, I> = Map<
#[derive_where(Debug; <&'a I as IntoIterator>::IntoIter, <CS::Group as Group>::Scalar)]
pub struct VoprfServerBatchEvaluateFinishResult<'a, CS: 'a + CipherSuite, I>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a I: IntoIterator<Item = &'a PreparedEvaluationElement<CS>>,
{
/// The [`EvaluationElement`]s to send to the client
@@ -511,8 +475,6 @@ fn verifiable_unblind<'a, CS: 'a + CipherSuite, IC, IM>(
proof: &Proof<CS>,
) -> Result<VoprfUnblindResult<'a, CS, IC, IM>>
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
&'a IC: 'a + IntoIterator<Item = &'a VoprfClient<CS>>,
<&'a IC as IntoIterator>::IntoIter: ExactSizeIterator,
&'a IM: 'a + IntoIterator<Item = &'a EvaluationElement<CS>>,
@@ -537,45 +499,10 @@ where
)?;
Ok(blinds
.zip(messages.into_iter())
.zip(messages)
.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 //
// ===== //
@@ -583,46 +510,20 @@ where
#[cfg(test)]
mod tests {
use core::ops::Add;
use core::ptr;
use ::alloc::vec;
use ::alloc::vec::Vec;
use generic_array::typenum::Sum;
use generic_array::ArrayLength;
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use super::*;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::Group;
use crate::common::{Dst, STR_HASH_TO_GROUP};
use crate::tests::helpers::prf;
fn prf<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>,
{
fn verifiable_retrieval<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
@@ -639,19 +540,15 @@ mod tests {
assert_eq!(client_finalize_result, res2);
}
fn verifiable_batch_retrieval<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let mut rng = OsRng;
fn verifiable_batch_retrieval<CS: CipherSuite>() {
let mut rng = SysRng;
let mut inputs = vec![];
let mut client_states = vec![];
let mut client_messages = vec![];
let num_iterations = 10;
for _ in 0..num_iterations {
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();
inputs.push(input);
client_states.push(client_blind_result.state);
@@ -687,19 +584,15 @@ mod tests {
assert_eq!(client_finalize_result, res2);
}
fn verifiable_batch_bad_public_key<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
let mut rng = OsRng;
fn verifiable_batch_bad_public_key<CS: CipherSuite>() {
let mut rng = SysRng;
let mut inputs = vec![];
let mut client_states = vec![];
let mut client_messages = vec![];
let num_iterations = 10;
for _ in 0..num_iterations {
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();
inputs.push(input);
client_states.push(client_blind_result.state);
@@ -718,7 +611,7 @@ mod tests {
.unwrap();
let messages: Vec<_> = messages.collect();
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
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
};
@@ -727,18 +620,14 @@ mod tests {
assert!(client_finalize_result.is_err());
}
fn verifiable_bad_public_key<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn verifiable_bad_public_key<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
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
CS::Group::hash_to_curve::<CS::Hash>(&[b"msg"], &dst.as_dst()).unwrap()
};
@@ -751,13 +640,9 @@ mod tests {
assert!(client_finalize_result.is_err());
}
fn verifiable_server_evaluate<CS: CipherSuite>()
where
<CS::Hash as OutputSizeUser>::OutputSize:
IsLess<U256> + IsLessOrEqual<<CS::Hash as BlockSizeUser>::BlockSize>,
{
fn verifiable_server_evaluate<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
@@ -781,18 +666,12 @@ mod tests {
// inputs
let wrong_input = b"wrong input";
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>()
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>,
{
fn zeroize_voprf_client<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let mut state = client_blind_result.state;
@@ -804,17 +683,9 @@ mod tests {
assert!(message.serialize().iter().all(|&x| x == 0));
}
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>,
{
fn zeroize_voprf_server<CS: CipherSuite>() {
let input = b"input";
let mut rng = OsRng;
let mut rng = SysRng;
let client_blind_result = VoprfClient::<CS>::blind(input, &mut rng).unwrap();
let server = VoprfServer::<CS>::new(&mut rng).unwrap();
let server_result = server.blind_evaluate(&mut rng, &client_blind_result.message);
@@ -832,43 +703,13 @@ mod tests {
assert!(proof.serialize().iter().all(|&x| x == 0));
}
#[test]
fn test_functionality() -> Result<()> {
use p256::NistP256;
use p384::NistP384;
#[cfg(feature = "ristretto255")]
{
use crate::Ristretto255;
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(())
}
crate::tests::test_all_curves!(
verifiable_retrieval,
verifiable_batch_retrieval,
verifiable_bad_public_key,
verifiable_batch_bad_public_key,
verifiable_server_evaluate,
zeroize_voprf_client,
zeroize_voprf_server,
);
}