feat: upgrade crypto ecosystem to latest RustCrypto stack
Rust CI / cargo fmt (pull_request) Successful in 13s
Rust CI / test (1.88.0 / no backend / no frontend) (pull_request) Failing after 13s
Rust CI / cargo clippy (pull_request) Successful in 1m23s
Rust CI / test (stable / no backend / no frontend) (pull_request) Successful in 2m18s
Rust CI / test (1.88.0 / no backend / --features argon2) (pull_request) Failing after 3s
Rust CI / test (stable / no backend / --features argon2) (pull_request) Successful in 2m27s
Rust CI / test (stable / no backend / --features serde) (pull_request) Successful in 2m48s
Rust CI / test (1.88.0 / no backend / --features serde) (pull_request) Failing after 4s
Rust CI / test (1.88.0 / --features curve25519 / no frontend) (pull_request) Failing after 4s
Rust CI / test (stable / --features curve25519 / no frontend) (pull_request) Successful in 2m23s
Rust CI / test (1.88.0 / --features curve25519 / --features argon2) (pull_request) Failing after 4s
Rust CI / test (stable / --features curve25519 / --features argon2) (pull_request) Successful in 2m32s
Rust CI / test (1.88.0 / --features curve25519 / --features serde) (pull_request) Failing after 4s
Rust CI / test (stable / --features curve25519 / --features serde) (pull_request) Successful in 2m50s
Rust CI / test (stable / --features ecdsa / no frontend) (pull_request) Successful in 2m44s
Rust CI / test (1.88.0 / --features ecdsa / no frontend) (pull_request) Failing after 4s
Rust CI / test (1.88.0 / --features ecdsa / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ecdsa / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255 / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255 / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255 / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255 / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ecdsa / --features argon2) (pull_request) Failing after 3s
Rust CI / test (1.88.0 / --features ristretto255,curve25519,ecdsa,ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,curve25519,ecdsa,ed25519 / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,curve25519,ecdsa,ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,curve25519,ecdsa,ed25519 / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,curve25519,ecdsa,ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,curve25519,ecdsa,ed25519 / --features serde) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,kem / no frontend) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,kem / no frontend) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,kem / --features argon2) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,kem / --features argon2) (pull_request) Has been cancelled
Rust CI / test (1.88.0 / --features ristretto255,kem / --features serde) (pull_request) Has been cancelled
Rust CI / test (stable / --features ristretto255,kem / --features serde) (pull_request) Has been cancelled
Rust CI / test simple_login example (pull_request) Has been cancelled
Rust CI / test digital_locker example (pull_request) Has been cancelled
Rust CI / cargo bench compilation () (pull_request) Has been cancelled
Rust CI / cargo bench compilation (--features ristretto255) (pull_request) Has been cancelled
Rust CI / cargo bench compilation (--features ristretto255,kem) (pull_request) Has been cancelled
Rust CI / cargo audit (pull_request) Has been cancelled
Rust CI / test (stable / --features ecdsa / --features argon2) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ristretto255,curve25519,ecdsa,ed25519) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / no backend) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / no backend) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / curve25519) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / curve25519) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ecdsa) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ecdsa) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ed25519) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ed25519) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ristretto255) (pull_request) Has been cancelled
Rust CI / no-std (wasm32-unknown-unknown / ristretto255) (pull_request) Has been cancelled
Rust CI / no-std (thumbv6m-none-eabi / ristretto255,curve25519,ecdsa,ed25519) (pull_request) Has been cancelled

Upgrade all core cryptographic dependencies to their latest versions:

Dependencies:
- digest: 0.10 to 0.11
- elliptic-curve: 0.13 to 0.14
- hkdf: 0.12 to 0.13
- hmac: 0.12 to 0.13
- rand: 0.8 to 0.10
- rand_chacha: 0.3 to 0.10
- sha2: 0.10 to 0.11
- getrandom: 0.2 to 0.4 (WASM)
- ml-kem: 0.3.0-rc.0 to 0.3
- ecdsa: 0.16 to 0.17.0-rc.23
- rfc6979: 0.4 to 0.6 (now internal to ecdsa)
- p256/p384/p521: 0.13 to 0.14.0-rc.15
- curve25519-dalek: 4 to 5.0.0-rc
- ed25519-dalek: 2 to 3.0.0-rc
- cryptoki: 0.9 to 0.12
- rustyline: 17 to 18
- scrypt: 0.11 to 0.12
- voprf replaced by voprf-vx 1.0.0-pre.0

Migration changes:
- generic-array 0.14 to 1.4 with hybrid-array 0.4 interop
- ArrayLength<u8> to ArrayLength (generic-array 1.x)
- Added ConcatExt trait to disambiguate from [T]::concat
- Replaced Hmac with SimpleHmac for digest 0.11 compatibility
- Added OutputSize<H>: ArrayLength bounds throughout Hash trait
- Converted hybrid_array::Array between GenericArray at API boundaries
- Updated GroupEncoding Repr bound to hybrid_array::Array
- ECDSA sign now uses ecdsa::hazmat::sign_prehashed_rfc6979
- Removed direct rfc6979 dependency (handled by ecdsa internally)
- Replaced bincode with postcard for no_std serialization
- Re-exported hybrid_array from crate root

Other changes:
- Renamed crate to opaque-vx
- Increased MSRV to 1.88
- Added cryptography to Cargo.toml categories
- Removed Facebook-specific contributions from CONTRIBUTING.md
- Removed v3 to v4 migration test
- Removed unstable rustfmt configurations for stable compatibility
This commit is contained in:
2026-06-30 17:56:08 +02:00
parent 5e2ba86643
commit 76ff117112
45 changed files with 1666 additions and 1477 deletions
+140
View File
@@ -0,0 +1,140 @@
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@nightly
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-targets --features argon2,std,curve25519,ecdsa,ed25519,kem -- -D warnings
- name: Run cargo doc
run: cargo doc --no-deps --document-private-items --features argon2,std,curve25519,ecdsa,ed25519,kem
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
- --features ristretto255,kem
- --features curve25519
- --features ecdsa
- --features ed25519
- --features ristretto255,curve25519,ecdsa,ed25519
frontend_feature:
- ""
- --features argon2
- --features serde
toolchain:
- stable
- "1.88.0"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@${{ matrix.toolchain }}
- name: Run cargo test
run: cargo test --no-default-features ${{ matrix.backend_feature }} ${{ matrix.frontend_feature }}
- name: Run cargo test with std
run: cargo test --no-default-features --features std ${{ matrix.backend_feature }} ${{ matrix.frontend_feature }}
simple-login-test:
name: test simple_login example
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Run expect
run: expect -f scripts/simple_login.exp
digital-locker-test:
name: test digital_locker example
runs-on: linux_amd64
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Run expect
run: expect -f scripts/digital_locker.exp
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:
- ""
- ristretto255
- curve25519
- ecdsa
- ed25519
- ristretto255,curve25519,ecdsa,ed25519
frontend_feature:
- argon2
- 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 --features ${{ matrix.frontend_feature }},${{ matrix.backend_feature }}
benches:
name: cargo bench compilation
runs-on: linux_amd64
strategy:
fail-fast: false
matrix:
backend_feature:
- --features ristretto255
- --features ristretto255,kem
- ""
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dtolnay/rust-toolchain@stable
- name: Run cargo bench --no-run
run: cargo bench --no-default-features ${{ matrix.backend_feature }} --no-run
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 }}
+81 -35
View File
@@ -1,67 +1,112 @@
# Changelog
## 1.0.0-pre.0 (June 29, 2026)
Forked from [facebook/opaque-ke](https://github.com/facebook/opaque-ke/) at `4.1.0-pre.2`.
* Upgraded dependencies:
* `ml-kem`: `0.3.0-rc.0` to `0.3`
* `digest`: `0.10` to `0.11`
* `elliptic-curve`: `0.13` to `0.14`
* `curve25519-dalek`: `4` to `5.0.0-rc`
* `ed25519-dalek`: `2` to `3.0.0-rc`
* `ecdsa`: `0.16` to `0.17.0-rc.23`
* `hkdf`: `0.12` to `0.13`
* `hmac`: `0.12` to `0.13`
* `rand`: `0.8` to `0.10`
* `rand_chacha`: `0.3` to `0.10`
* `rfc6979`: `0.4` to `0.6` (now internal to `ecdsa`)
* `sha2`: `0.10` to `0.11`
* `getrandom`: `0.2` to `0.4` (WASM target)
* `p256`/`p384`/`p521`: `0.13` to `0.14.0-rc.15` (dev-dependency)
* `cryptoki`: `0.9` to `0.12` (dev-dependency)
* `rustyline`: `17` to `18` (dev-dependency)
* `scrypt`: `0.11` to `0.12` (dev-dependency)
* `voprf` replaced by `voprf-vx 1.0.0-pre.0`
* Bump `generic-array 0.14` to `generic-array 1.4` with `hybrid-array 0.4` interop
* Added `hybrid-array 0.4` for interop
* Added `ConcatExt` trait to disambiguate from `[T]::concat`
* Added **`cryptography`** to `categories` in `Cargo.toml`
* Replaced `Hmac` with `SimpleHmac` throughout for `digest 0.11` compatibility
* Replaced `bincode` with `postcard` for `no_std` serialization
* Re-exported `hybrid_array` from crate root
* Updated `Hash` trait to remove `BlockSizeUser` bounds incompatible with `digest 0.11`
* Updated `GroupEncoding Repr` bound to `hybrid_array::Array`
* Fixed `MaskedResponse::serialize` field ordering to match deserialization
* Increased **MSRV** to **1.88**
* Renamed crate to `opaque-vx`
* Removed direct `rfc6979` dependency (handled by `ecdsa` internally)
* Removed unstable `rustfmt` configurations for **Rust stable** compatibility
* Removed Facebook-specific contributions (CLA, bounty program) from `CONTRIBUTING.md`
* Removed `v3` to `v4` migration test (no longer relevant for fork)
## 4.1.0-pre.2 (March 26, 2026)
* Upgraded ml-kem from 0.2 to 0.3.0-rc.0
* Increased MSRV to 1.87
## 4.1.0-pre.1 (November 17, 2025)
* Added ml-kem re-export behind the kem feature
## 4.1.0-pre.0 (November 11, 2025)
* Fixed dependency exporting for the rand crate
* Added TripleDhKem key exchange protocol
## 4.0.1 (October 30, 2025)
* Fixing docs building issue
## 4.0.0 (October 23, 2025)
* Increased MSRV to 1.83
* Synced implementation with RFC 9807 (no core protocol changes)
* Added a SIGMA-I key exchange implementation
* Removed KeGroup type from the Ciphersuite trait (now part of KeyExchange type)
* **Breaking: existing Ciphersuite trait definitions need to be updated**
* **Breaking: existing Ciphersuite trait definitions need to be updated**
* Ensured that dummy record is always created to avoid timing attack issues
* Modified the dummy registration file to only contain the public key
instead of the keypair
* **Breaking: existing `ServerSetup`s need to be updated**
```rust
// Given `old` is a `ServerSetup` from `opaque-ke` v3.
let old_serialized = old.serialize();
type OldSeedLen = <<<OldCipherSuite as opaque_ke_3::CipherSuite>::OprfCs as voprf::CipherSuite>::Hash as OutputSizeUser>::OutputSize;
type OldSkLen = <<OldCipherSuite as opaque_ke_3::CipherSuite>::KeGroup as opaque_ke_3::key_exchange::group::KeGroup>::SkLen;
let (old_serialied_rest, old_fake_keypair_serialized): (
GenericArray<u8, Sum<OldSeedLen, OldSkLen>>,
_,
) = old_serialized.split();
let old_fake_keypair =
KeyPair::<<OldCipherSuite as opaque_ke_3::CipherSuite>::KeGroup>::from_private_key_slice(
&old_fake_keypair_serialized,
)
.unwrap();
let old_fake_pk_serialized = old_fake_keypair.public().serialize();
let new_serialized = old_serialied_rest.concat(old_fake_pk_serialized);
// Given `NewCipherSuite` is a `CipherSuite` implementation equivalent to `OldCipherSuite`.
ServerSetup::<NewCipherSuite>::deserialize(&new_serialized).unwrap()
```
* **Breaking: existing `ServerSetup`s need to be updated**
```rust
// Given `old` is a `ServerSetup` from `opaque-ke` v3.
let old_serialized = old.serialize();
type OldSeedLen = <<<OldCipherSuite as opaque_ke_3::CipherSuite>::OprfCs as voprf::CipherSuite>::Hash as OutputSizeUser>::OutputSize;
type OldSkLen = <<OldCipherSuite as opaque_ke_3::CipherSuite>::KeGroup as opaque_ke_3::key_exchange::group::KeGroup>::SkLen;
let (old_serialied_rest, old_fake_keypair_serialized): (
GenericArray<u8, Sum<OldSeedLen, OldSkLen>>,
_,
) = old_serialized.split();
let old_fake_keypair =
KeyPair::<<OldCipherSuite as opaque_ke_3::CipherSuite>::KeGroup>::from_private_key_slice(
&old_fake_keypair_serialized,
)
.unwrap();
let old_fake_pk_serialized = old_fake_keypair.public().serialize();
let new_serialized = old_serialied_rest.concat(old_fake_pk_serialized);
// Given `NewCipherSuite` is a `CipherSuite` implementation equivalent to `OldCipherSuite`.
ServerSetup::<NewCipherSuite>::deserialize(&new_serialized).unwrap()
```
* Added remote OPRF seed support
* Replace remote private key trait with a state machine, facilitating async support.
* Serde de/serialization formats have been simplified
* **Breaking: existing `ServerRegistration`s may need to be updated**
```rust
// Given `old` is a `ServerRegistration` from `opaque-ke` v3.
let old_serialized = old.serialize();
// Given `NewCipherSuite` is a `CipherSuite` implementation equivalent to the old cipher suite.
ServerRegistration::<NewCipherSuite>::deserialize(&old_serialized).unwrap()
```
* **Breaking: existing `ServerRegistration`s may need to be updated**
```rust
// Given `old` is a `ServerRegistration` from `opaque-ke` v3.
let old_serialized = old.serialize();
// Given `NewCipherSuite` is a `CipherSuite` implementation equivalent to the old cipher suite.
ServerRegistration::<NewCipherSuite>::deserialize(&old_serialized).unwrap()
```
## 3.0.0 (October 10, 2024)
* Synced implementation with draft-irtf-cfrg-opaque-16
* **Breaking: protocol context string changed from `RFCXXXX` to `OPAQUEv1-`**
* **Breaking: protocol context string changed from `RFCXXXX` to `OPAQUEv1-`**
* Dropped unmaintained json crate in favor of serde_json
* Updated dependencies
* Increased MSRV to 1.74
@@ -69,11 +114,12 @@
* Adjusted key generation logic to be in line with commit 727b9ac of
https://github.com/cfrg/draft-irtf-cfrg-opaque
* Updated VOPRF to draft 19
* **Breaking: backwards-incompatible changes introduced in OPRF protocol**
* **Breaking: backwards-incompatible changes introduced in OPRF protocol**
* Added P384 testing support
* Renaming of X25519 to Curve25519
## 2.0.0 (September 21, 2022)
* Synced implementation with draft-irtf-cfrg-opaque-10
* Changed argon2 salt length to recommended value (16 bytes)
* Fixed issue from 2.0.0-pre.2 not pinning voprf dependency correctly
-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
-19
View File
@@ -2,29 +2,10 @@
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 opaque-ke, you agree that your contributions will be
licensed under both the LICENSE-MIT and LICENSE-APACHE files in the root
+74 -65
View File
@@ -1,110 +1,119 @@
[package]
authors = ["Kevin Lewi <[email protected]>", "François Garillot <[email protected]>"]
categories = ["no-std"]
authors = [
"VexaHub Developers",
"Kevin Lewi <[email protected]>",
"François Garillot <[email protected]>",
]
categories = ["no-std", "cryptography"]
description = "An implementation of the OPAQUE password-authenticated key exchange protocol"
edition = "2024"
exclude = ["/src/tests/"]
keywords = ["cryptography", "crypto", "opaque", "passwords", "authentication"]
keywords = ["cryptography", "opaque", "passwords", "authentication", "pake"]
license = "Apache-2.0 OR MIT"
name = "opaque-ke"
name = "opaque-vx"
readme = "README.md"
repository = "https://github.com/facebook/opaque-ke"
rust-version = "1.87"
version = "4.1.0-pre.2"
repository = "https://github.com/vexahub/opaque-vx"
rust-version = "1.88"
version = "1.0.0-pre.0"
[features]
argon2 = ["dep:argon2"]
curve25519 = ["dep:curve25519-dalek"]
default = ["ristretto255", "serde"]
ecdsa = ["dep:ecdsa", "dep:rfc6979"]
ecdsa = ["dep:ecdsa"]
ed25519 = ["dep:curve25519-dalek", "dep:ed25519-dalek"]
kem = ["dep:ml-kem", "dep:rand_core_10"]
kem = ["dep:ml-kem", "dep:rand_core"]
ristretto255 = ["dep:curve25519-dalek", "voprf/ristretto255-ciphersuite"]
serde = [
"dep:serde",
"curve25519-dalek?/serde",
"ecdsa?/serde",
"ed25519-dalek?/serde",
"elliptic-curve/serde",
"generic-array/serde",
"voprf/serde",
"zeroize/serde",
"dep:serde",
"curve25519-dalek?/serde",
"ecdsa?/serde",
"ed25519-dalek?/serde",
"elliptic-curve/serde",
"generic-array/serde",
"hybrid-array/serde",
"voprf/serde",
"zeroize/serde",
]
std = ["dep:getrandom", "rand/std"]
[dependencies]
argon2 = { version = "0.5", default-features = false, features = [
"alloc",
argon2 = { version = "0.6.0-rc", default-features = false, features = [
"alloc",
], optional = true }
curve25519-dalek = { version = "4", default-features = false, features = [
"zeroize",
curve25519-dalek = { version = "5.0.0-rc", default-features = false, features = [
"zeroize",
], optional = true }
derive-where = { version = "1.4", features = ["zeroize-on-drop"] }
digest = "0.10"
derive-where = { version = "1.6", features = ["zeroize-on-drop"] }
digest = { version = "0.11", features = ["zeroize"] }
displaydoc = { version = "0.2", default-features = false }
ecdsa = { version = "0.16", default-features = false, features = [
"arithmetic",
"hazmat",
ecdsa = { version = "0.17.0-rc.23", default-features = false, features = [
"algorithm",
], optional = true }
ed25519-dalek = { version = "2", default-features = false, features = [
"digest",
"hazmat",
ed25519-dalek = { version = "3.0.0-rc", default-features = false, features = [
"digest",
"hazmat",
], optional = true }
elliptic-curve = { version = "0.13", features = ["hash2curve", "sec1"] }
generic-array = "=0.14.7" # pinned to avoid deprecation warnings
hkdf = "0.12"
hmac = "0.12"
ml-kem = { version = "0.3.0-rc.0", default-features = false, features = [
"zeroize",
elliptic-curve = { version = "0.14", features = ["sec1"] }
generic-array = { version = "1.4", features = ["hybrid-array-0_4", "zeroize"] }
hybrid-array = { version = "0.4", features = ["extra-sizes", "zeroize"] }
hkdf = "0.13"
hmac = "0.13"
ml-kem = { version = "0.3", default-features = false, features = [
"zeroize",
], optional = true }
rand = { version = "0.8", default-features = false }
rand_core_10 = { package = "rand_core", version = "0.10", default-features = false, optional = true }
rfc6979 = { version = "0.4", optional = true }
rand = { version = "0.10", default-features = false }
rand_core = { version = "0.10", default-features = false, optional = true }
serde = { version = "1", default-features = false, features = [
"derive",
"derive",
], optional = true }
subtle = { version = "2.6", default-features = false }
voprf = { version = "0.5", default-features = false, features = ["danger"] }
zeroize = { version = "1.8", features = ["zeroize_derive"] }
voprf = {
package = "voprf-vx",
version = "1.0.0-pre.0",
default-features = false,
features = ["danger"]
}
zeroize = { version = "1.9", features = ["zeroize_derive"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2", features = ["js"], optional = true }
getrandom = { version = "0.4", features = ["wasm_js"], optional = true }
[dev-dependencies]
anyhow = "1"
bincode = "1"
chacha20poly1305 = "0.10"
postcard = { version = "1", features = ["alloc"] }
chacha20poly1305 = "0.11"
criterion = "0.8"
cryptoki = "0.9"
elliptic-curve = { version = "0.13", features = ["alloc", "pkcs8"] }
cryptoki = "0.12"
elliptic-curve = { version = "0.14", features = ["alloc", "pkcs8"] }
rand_core = { version = "0.10", default-features = false }
hex = "0.4"
opaque-ke-3 = { package = "opaque-ke", version = "=3.0.0" }
p256 = { version = "0.13", default-features = false, features = [
"ecdsa",
"hash2curve",
"pkcs8",
"voprf",
p256 = { version = "0.14.0-rc.15", default-features = false, features = [
"ecdsa",
"hash2curve",
"pkcs8",
"oprf",
] }
p384 = { version = "0.13", default-features = false, features = [
"hash2curve",
"pkcs8",
"voprf",
p384 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve",
"pkcs8",
"oprf",
] }
p521 = { version = "0.13.3", default-features = false, features = [
"hash2curve",
"pkcs8",
"voprf",
p521 = { version = "0.14.0-rc.15", default-features = false, features = [
"hash2curve",
"pkcs8",
"oprf",
] }
paste = "1"
proptest = "1"
rand = "0.8"
rand_chacha = "0.3"
rand = "0.10"
rand_chacha = "0.10"
regex = "1"
sha2 = { version = "0.10", default-features = false }
sha2 = { version = "0.11", default-features = false }
thiserror = "2"
# MSRV
rustyline = "17"
scrypt = "0.11"
rustyline = "18"
scrypt = "0.12"
serde_json = "1"
[[bench]]
+35 -16
View File
@@ -1,20 +1,28 @@
## The OPAQUE key exchange protocol ![Build Status](https://github.com/facebook/opaque-ke/workflows/Rust%20CI/badge.svg)
## The OPAQUE key exchange protocol
[OPAQUE](https://eprint.iacr.org/2018/163.pdf) is an augmented password-authenticated key exchange protocol. It allows a client to authenticate to a server using a password, without ever having to expose the plaintext password to the server.
[OPAQUE](https://eprint.iacr.org/2018/163.pdf) is an augmented password-authenticated key exchange protocol. It allows a
client to authenticate to a server using a password, without ever having to expose the plaintext password to the server.
This implementation is based on [RFC 9807](https://datatracker.ietf.org/doc/rfc9807/).
This is a fork of [facebook/opaque-ke](https://github.com/facebook/opaque-ke) maintained
by [VexaHub](https://github.com/vexahub), targeting the latest **RustCrypto ecosystem**.
Background
----------
Augmented Password Authenticated Key Exchange (aPAKE) protocols are designed to provide password authentication and mutually authenticated key exchange without relying on PKI (except during user/password registration) and without disclosing passwords to servers or other entities other than the client machine.
Augmented Password Authenticated Key Exchange (aPAKE) protocols are designed to provide password authentication and
mutually authenticated key exchange without relying on PKI (except during user/password registration) and without
disclosing passwords to servers or other entities other than the client machine.
OPAQUE is a PKI-free aPAKE that is secure against pre-computation attacks and capable of using a secret salt.
Documentation
-------------
The API can be found [here](https://docs.rs/opaque-ke/) along with an example for usage. More examples can be found in the [examples](./examples) directory.
The API can be found [here](https://docs.rs/opaque-ke-vx/) along with an example for usage. More examples can be found
in
the [examples](./examples) directory.
Installation
------------
@@ -22,41 +30,52 @@ Installation
Add the following line to the dependencies of your `Cargo.toml`:
```
opaque-ke = "4.1.0-pre.2"
opaque-ke = { package = "opaque-ke-vx", version = "1.0.0-pre.0" }
```
### Minimum Supported Rust Version
Rust **1.87** or higher.
Rust **1.88** or higher.
Audit
-----
This library was audited by NCC Group in June of 2021. The audit was sponsored by WhatsApp for its use in [enabling end-to-end encrypted backups](https://engineering.fb.com/2021/09/10/security/whatsapp-e2ee-backups/).
This library was audited by NCC Group in June of 2021. The audit was sponsored by WhatsApp for its use
in [enabling end-to-end encrypted backups](https://engineering.fb.com/2021/09/10/security/whatsapp-e2ee-backups/).
The audit found issues in release `v0.5.0`, and the fixes were subsequently incorporated into release `v1.2.0`. See the [full audit report here](https://research.nccgroup.com/2021/12/13/public-report-whatsapp-opaque-ke-cryptographic-implementation-review/).
The audit found issues in release `v0.5.0`, and the fixes were subsequently incorporated into release `v1.2.0`. See
the [full audit report here](https://research.nccgroup.com/2021/12/13/public-report-whatsapp-opaque-ke-cryptographic-implementation-review/).
Resources
---------
- [OPAQUE academic publication](https://eprint.iacr.org/2018/163.pdf), including formal definitions and a proof of security
- [OPAQUE academic publication](https://eprint.iacr.org/2018/163.pdf), including formal definitions and a proof of
security
- [RFC 9807](https://datatracker.ietf.org/doc/rfc9807/), containing a detailed (byte-level) specification for OPAQUE
- ["Let's talk about PAKE"](https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/), an introductory blog post written by Matthew Green that covers OPAQUE
- ["Let's talk about PAKE"](https://blog.cryptographyengineering.com/2018/10/19/lets-talk-about-pake/), an introductory
blog post written by Matthew Green that covers OPAQUE
- [@serenity-kit/opaque](https://github.com/serenity-kit/opaque), a WebAssembly package for this library
- [opaque-wasm](https://github.com/marucjmar/opaque-wasm), a WebAssembly package for this library. A comparison between `@serenity-kit/opaque` and `opaque-wasm` can be found [here](https://opaque-documentation.netlify.app/docs/faq#how-does-it-compare-to-opaque-wasm)
- [react-native-opaque](https://github.com/serenity-kit/react-native-opaque), a React Native package for this library matching the API of `@serenity-kit/opaque`
- [opaque-wasm](https://github.com/marucjmar/opaque-wasm), a WebAssembly package for this library. A comparison between
`@serenity-kit/opaque` and `opaque-wasm` can be
found [here](https://opaque-documentation.netlify.app/docs/faq#how-does-it-compare-to-opaque-wasm)
- [react-native-opaque](https://github.com/serenity-kit/react-native-opaque), a React Native package for this library
matching the API of `@serenity-kit/opaque`
Contributors
------------
The authors of this code are Kevin Lewi
([@kevinlewi](https://github.com/kevinlewi)) and François Garillot ([@huitseeker](https://github.com/huitseeker)).
This fork is maintained by [VexaHub](https://github.com/vexahub).
The original authors are Kevin Lewi ([@kevinlewi](https://github.com/kevinlewi)) and François
Garillot ([@huitseeker](https://github.com/huitseeker)).
To learn more about contributing to this project, [see this document](./CONTRIBUTING.md).
#### Acknowledgments
Special thanks go to Hugo Krawczyk and Chris Wood for helping to clarify discrepancies and making suggestions for improving
this implementation. Additional credit goes to @daxpedda for adding no_std support, p256 support, and making other general
Special thanks go to Hugo Krawczyk and Chris Wood for helping to clarify discrepancies and making suggestions for
improving
this implementation. Additional credit goes to @daxpedda for adding no_std support, p256 support, and making other
general
improvements to the library.
License
+18 -17
View File
@@ -10,8 +10,9 @@
extern crate criterion;
use criterion::Criterion;
use opaque_ke::*;
use rand::rngs::OsRng;
use opaque_vx::*;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
#[cfg(feature = "ristretto255")]
static SUFFIX: &str = "ristretto255";
@@ -22,20 +23,20 @@ struct Default;
#[cfg(feature = "ristretto255")]
impl CipherSuite for Default {
type OprfCs = opaque_ke::Ristretto255;
type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
type Ksf = opaque_ke::ksf::Identity;
type OprfCs = Ristretto255;
type KeyExchange = TripleDh<Ristretto255, sha2::Sha512>;
type Ksf = ksf::Identity;
}
#[cfg(not(feature = "ristretto255"))]
impl CipherSuite for Default {
type OprfCs = p256::NistP256;
type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
type Ksf = opaque_ke::ksf::Identity;
type KeyExchange = TripleDh<p256::NistP256, sha2::Sha256>;
type Ksf = ksf::Identity;
}
fn server_setup(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
c.bench_function(&format!("server setup ({SUFFIX})"), move |b| {
b.iter(|| {
@@ -45,7 +46,7 @@ fn server_setup(c: &mut Criterion) {
}
fn client_registration_start(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let password = b"password";
c.bench_function(&format!("client registration start ({SUFFIX})"), move |b| {
@@ -56,7 +57,7 @@ fn client_registration_start(c: &mut Criterion) {
}
fn server_registration_start(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
@@ -76,7 +77,7 @@ fn server_registration_start(c: &mut Criterion) {
}
fn client_registration_finish(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
@@ -109,7 +110,7 @@ fn client_registration_finish(c: &mut Criterion) {
}
fn server_registration_finish(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
@@ -142,7 +143,7 @@ fn server_registration_finish(c: &mut Criterion) {
}
fn client_login_start(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let password = b"password";
c.bench_function(&format!("client login start ({SUFFIX})"), move |b| {
@@ -153,7 +154,7 @@ fn client_login_start(c: &mut Criterion) {
}
fn server_login_start_real(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
@@ -193,7 +194,7 @@ fn server_login_start_real(c: &mut Criterion) {
}
fn server_login_start_fake(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
@@ -215,7 +216,7 @@ fn server_login_start_fake(c: &mut Criterion) {
}
fn client_login_finish(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
@@ -265,7 +266,7 @@ fn client_login_finish(c: &mut Criterion) {
}
fn server_login_finish(c: &mut Criterion) {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let username = b"username";
let password = b"password";
let server_setup = ServerSetup::<Default>::new(&mut rng);
+23 -23
View File
@@ -30,16 +30,16 @@ use std::process::exit;
use chacha20poly1305::aead::{Aead, KeyInit};
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};
use opaque_ke::ciphersuite::CipherSuite;
use opaque_ke::generic_array::GenericArray;
use opaque_ke::rand::RngCore;
use opaque_ke::rand::rngs::OsRng;
use opaque_ke::{
use opaque_vx::ciphersuite::CipherSuite;
use opaque_vx::rand::Rng;
use opaque_vx::rand::rngs::SysRng;
use opaque_vx::{
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
ServerLoginParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
ServerLoginParameters, ServerRegistration, ServerSetup,
};
use rand_core::UnwrapErr;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use rustyline::history::DefaultHistory;
@@ -51,43 +51,43 @@ struct DefaultCipherSuite;
#[cfg(feature = "ristretto255")]
impl CipherSuite for DefaultCipherSuite {
type OprfCs = opaque_ke::Ristretto255;
type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
type Ksf = opaque_ke::ksf::Identity;
type OprfCs = opaque_vx::Ristretto255;
type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
type Ksf = opaque_vx::ksf::Identity;
}
#[cfg(not(feature = "ristretto255"))]
impl CipherSuite for DefaultCipherSuite {
type OprfCs = p256::NistP256;
type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
type Ksf = opaque_ke::ksf::Identity;
type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
type Ksf = opaque_vx::ksf::Identity;
}
struct Locker {
contents: Vec<u8>,
password_file: GenericArray<u8, ServerRegistrationLen<DefaultCipherSuite>>,
password_file: Vec<u8>,
}
// Given a key and plaintext, produce an AEAD ciphertext along with a nonce
fn encrypt(key: &[u8], plaintext: &[u8]) -> Vec<u8> {
let cipher = ChaCha20Poly1305::new(Key::from_slice(&key[..32]));
let cipher = ChaCha20Poly1305::new(&Key::try_from(&key[..32]).unwrap());
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let mut nonce_bytes = [0u8; 12];
rng.fill_bytes(&mut nonce_bytes);
let nonce = Nonce::from_slice(&nonce_bytes);
let nonce = Nonce::try_from(&nonce_bytes[..]).unwrap();
let ciphertext = cipher.encrypt(nonce, plaintext.as_ref()).unwrap();
let ciphertext = cipher.encrypt(&nonce, plaintext.as_ref()).unwrap();
[nonce_bytes.to_vec(), ciphertext].concat()
}
// Decrypt using a key and a ciphertext (nonce included) to recover the original
// plaintext
fn decrypt(key: &[u8], ciphertext: &[u8]) -> Vec<u8> {
let cipher = ChaCha20Poly1305::new(Key::from_slice(&key[..32]));
let cipher = ChaCha20Poly1305::new(&Key::try_from(&key[..32]).unwrap());
cipher
.decrypt(
Nonce::from_slice(&ciphertext[..12]),
&Nonce::try_from(&ciphertext[..12]).unwrap(),
ciphertext[12..].as_ref(),
)
.unwrap()
@@ -101,7 +101,7 @@ fn register_locker(
password: String,
secret_message: String,
) -> Locker {
let mut client_rng = OsRng;
let mut client_rng = UnwrapErr(SysRng);
let client_registration_start_result =
ClientRegistration::<DefaultCipherSuite>::start(&mut client_rng, password.as_bytes())
.unwrap();
@@ -143,7 +143,7 @@ fn register_locker(
Locker {
contents: ciphertext,
password_file: password_file.serialize(),
password_file: password_file.serialize().to_vec(),
}
}
@@ -154,7 +154,7 @@ fn open_locker(
password: String,
locker: &Locker,
) -> Result<String, String> {
let mut client_rng = OsRng;
let mut client_rng = UnwrapErr(SysRng);
let client_login_start_result =
ClientLogin::<DefaultCipherSuite>::start(&mut client_rng, password.as_bytes()).unwrap();
let credential_request_bytes = client_login_start_result.message.serialize();
@@ -163,7 +163,7 @@ fn open_locker(
let password_file =
ServerRegistration::<DefaultCipherSuite>::deserialize(&locker.password_file).unwrap();
let mut server_rng = OsRng;
let mut server_rng = UnwrapErr(SysRng);
let server_login_start_result = ServerLogin::start(
&mut server_rng,
server_setup,
@@ -217,7 +217,7 @@ fn open_locker(
}
fn main() {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let server_setup = ServerSetup::<DefaultCipherSuite>::new(&mut rng);
let mut rl = Editor::<(), _>::new().unwrap();
+18 -18
View File
@@ -22,22 +22,22 @@
//! over "the wire" to the server. These bytes are serialized and explicitly
//! annotated in the below functions.
use std::collections::HashMap;
use std::process::exit;
use opaque_ke::argon2::Argon2;
use opaque_ke::ciphersuite::CipherSuite;
use opaque_ke::generic_array::GenericArray;
use opaque_ke::rand::rngs::OsRng;
use opaque_ke::{
use opaque_vx::argon2::Argon2;
use opaque_vx::ciphersuite::CipherSuite;
use opaque_vx::hybrid_array::Array;
use opaque_vx::rand::rngs::SysRng;
use opaque_vx::{
ClientLogin, ClientLoginFinishParameters, ClientRegistration,
ClientRegistrationFinishParameters, CredentialFinalization, CredentialRequest,
CredentialResponse, RegistrationRequest, RegistrationResponse, RegistrationUpload, ServerLogin,
ServerLoginParameters, ServerRegistration, ServerRegistrationLen, ServerSetup,
};
use rand_core::UnwrapErr;
use rustyline::Editor;
use rustyline::error::ReadlineError;
use rustyline::history::DefaultHistory;
use std::collections::HashMap;
use std::process::exit;
// The ciphersuite trait allows to specify the underlying primitives that will
// be used in the OPAQUE protocol
@@ -46,8 +46,8 @@ struct DefaultCipherSuite;
#[cfg(feature = "ristretto255")]
impl CipherSuite for DefaultCipherSuite {
type OprfCs = opaque_ke::Ristretto255;
type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
type OprfCs = opaque_vx::Ristretto255;
type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
type Ksf = Argon2<'static>;
}
@@ -55,7 +55,7 @@ impl CipherSuite for DefaultCipherSuite {
#[cfg(not(feature = "ristretto255"))]
impl CipherSuite for DefaultCipherSuite {
type OprfCs = p256::NistP256;
type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
type Ksf = Argon2<'static>;
}
@@ -65,8 +65,8 @@ fn account_registration(
server_setup: &ServerSetup<DefaultCipherSuite>,
username: String,
password: String,
) -> GenericArray<u8, ServerRegistrationLen<DefaultCipherSuite>> {
let mut client_rng = OsRng;
) -> Array<u8, ServerRegistrationLen<DefaultCipherSuite>> {
let mut client_rng = UnwrapErr(SysRng);
let client_registration_start_result =
ClientRegistration::<DefaultCipherSuite>::start(&mut client_rng, password.as_bytes())
.unwrap();
@@ -100,7 +100,7 @@ fn account_registration(
let password_file = ServerRegistration::finish(
RegistrationUpload::<DefaultCipherSuite>::deserialize(&message_bytes).unwrap(),
);
password_file.serialize()
password_file.serialize().into_ha0_4()
}
// Password-based login between a client and server
@@ -110,7 +110,7 @@ fn account_login(
password: String,
password_file_bytes: &[u8],
) -> bool {
let mut client_rng = OsRng;
let mut client_rng = UnwrapErr(SysRng);
let client_login_start_result =
ClientLogin::<DefaultCipherSuite>::start(&mut client_rng, password.as_bytes()).unwrap();
let credential_request_bytes = client_login_start_result.message.serialize();
@@ -119,7 +119,7 @@ fn account_login(
let password_file =
ServerRegistration::<DefaultCipherSuite>::deserialize(password_file_bytes).unwrap();
let mut server_rng = OsRng;
let mut server_rng = UnwrapErr(SysRng);
let server_login_start_result = ServerLogin::start(
&mut server_rng,
server_setup,
@@ -161,12 +161,12 @@ fn account_login(
}
fn main() {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let server_setup = ServerSetup::<DefaultCipherSuite>::new(&mut rng);
let mut rl = Editor::<(), _>::new().unwrap();
let mut registered_users =
HashMap::<String, GenericArray<u8, ServerRegistrationLen<DefaultCipherSuite>>>::new();
HashMap::<String, Array<u8, ServerRegistrationLen<DefaultCipherSuite>>>::new();
loop {
println!(
"\nCurrently registered usernames: {:?}\n",
+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
+10 -8
View File
@@ -11,7 +11,7 @@
use core::ops::Add;
use digest::core_api::{BlockSizeUser, CoreProxy};
use digest::block_api::{CoreProxy, EagerHash, SmallBlockSizeUser};
use generic_array::ArrayLength;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
@@ -30,16 +30,18 @@ use crate::opaque::MaskedResponseLen;
/// * `Ksf`: A key stretching function, typically used for password hashing
pub trait CipherSuite
where
OprfHash<Self>: Hash,
OprfHash<Self>: Hash + EagerHash,
<OprfHash<Self> as CoreProxy>::Core: ProxyHash,
<<OprfHash<Self> as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<<OprfHash<Self> as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<OprfHash<Self> as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<OprfHash<Self> as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
// Envelope: Nonce + Hash
// MaskedResponse: (Nonce + Hash) + KePk
OutputSize<OprfHash<Self>>: Add<NonceLen>,
Sum<OutputSize<OprfHash<Self>>, NonceLen>:
ArrayLength<u8> + Add<<KeGroup<Self> as Group>::PkLen>,
MaskedResponseLen<Self>: ArrayLength<u8>,
OutputSize<OprfHash<Self>>: Add<NonceLen> + ArrayLength,
Sum<OutputSize<OprfHash<Self>>, NonceLen>: ArrayLength + Add<<KeGroup<Self> as Group>::PkLen>,
MaskedResponseLen<Self>: ArrayLength,
// hybrid-array interop bounds
<OprfGroup<Self> as voprf::Group>::ScalarLen: ArrayLength,
<OprfGroup<Self> as voprf::Group>::ElemLen: ArrayLength,
{
/// A VOPRF ciphersuite, see [`voprf::CipherSuite`].
type OprfCs: voprf::CipherSuite;
+32 -23
View File
@@ -11,11 +11,10 @@ use core::convert::TryFrom;
use derive_where::derive_where;
use digest::Output;
use generic_array::GenericArray;
use generic_array::sequence::Concat;
use generic_array::typenum::{Sum, U32};
use hkdf::Hkdf;
use hmac::{Hmac, Mac};
use rand::{CryptoRng, RngCore};
use hkdf::SimpleHkdf as Hkdf;
use hmac::{KeyInit, Mac, SimpleHmac};
use rand::{CryptoRng, Rng};
use zeroize::Zeroize;
use crate::ciphersuite::{CipherSuite, KeGroup, OprfHash};
@@ -25,7 +24,7 @@ use crate::key_exchange::SerializedIdentifiers;
use crate::key_exchange::group::Group;
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
use crate::opaque::Identifiers;
use crate::serialization::{GenericArrayExt, SliceExt, UpdateExt};
use crate::serialization::{SliceExt, UpdateExt};
// Constant string used as salt for HKDF computation
const STR_AUTH_KEY: [u8; 7] = *b"AuthKey";
@@ -108,9 +107,9 @@ pub(crate) type EnvelopeLen<CS: CipherSuite> = Sum<OutputSize<OprfHash<CS>>, Non
impl<CS: CipherSuite> Envelope<CS> {
#[allow(clippy::type_complexity)]
pub(crate) fn seal<R: RngCore + CryptoRng>(
pub(crate) fn seal<R: Rng + CryptoRng>(
rng: &mut R,
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
randomized_pwd_hasher: &Hkdf<OprfHash<CS>>,
server_s_pk: &PublicKey<KeGroup<CS>>,
ids: Identifiers,
) -> Result<SealResult<CS>, ProtocolError> {
@@ -119,7 +118,7 @@ impl<CS: CipherSuite> Envelope<CS> {
let (mode, client_s_pk) = (
InnerEnvelopeMode::Internal,
build_inner_envelope_internal::<CS>(randomized_pwd_hasher.clone(), nonce)?,
build_inner_envelope_internal::<CS>(randomized_pwd_hasher, nonce)?,
);
let server_s_pk_bytes = server_s_pk.serialize();
@@ -148,7 +147,7 @@ impl<CS: CipherSuite> Envelope<CS> {
/// the aad field. Note that a new nonce is sampled for each call to seal.
#[allow(clippy::type_complexity)]
pub(crate) fn seal_raw<'a>(
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
randomized_pwd_hasher: &Hkdf<OprfHash<CS>>,
nonce: GenericArray<u8, NonceLen>,
aad: impl Iterator<Item = &'a [u8]>,
mode: InnerEnvelopeMode,
@@ -163,7 +162,7 @@ impl<CS: CipherSuite> Envelope<CS> {
.expand_multi_info(&[&nonce, &STR_EXPORT_KEY], &mut export_key)
.map_err(|_| InternalError::HkdfError)?;
let mut hmac = Hmac::<OprfHash<CS>>::new_from_slice(&hmac_key)
let mut hmac = SimpleHmac::<OprfHash<CS>>::new_from_slice(&hmac_key)
.map_err(|_| InternalError::HmacError)?;
hmac.update(&nonce);
hmac.update_iter(aad);
@@ -184,7 +183,7 @@ impl<CS: CipherSuite> Envelope<CS> {
pub(crate) fn open<'a>(
&self,
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
randomized_pwd_hasher: &Hkdf<OprfHash<CS>>,
server_s_pk: PublicKey<KeGroup<CS>>,
optional_ids: Identifiers<'a>,
) -> Result<OpenedEnvelope<'a, CS>, ProtocolError> {
@@ -193,7 +192,7 @@ impl<CS: CipherSuite> Envelope<CS> {
return Err(InternalError::IncompatibleEnvelopeModeError.into());
}
InnerEnvelopeMode::Internal => {
recover_keys_internal::<CS>(randomized_pwd_hasher.clone(), self.nonce)?
recover_keys_internal::<CS>(randomized_pwd_hasher, self.nonce)?
}
};
@@ -222,20 +221,20 @@ impl<CS: CipherSuite> Envelope<CS> {
/// if the key and aad used to construct the envelope are the same.
pub(crate) fn open_raw<'a>(
&self,
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
randomized_pwd_hasher: &Hkdf<OprfHash<CS>>,
aad: impl Iterator<Item = &'a [u8]>,
) -> Result<OpenedInnerEnvelope<CS>, InternalError> {
let mut hmac_key = Output::<OprfHash<CS>>::default();
let mut export_key = Output::<OprfHash<CS>>::default();
randomized_pwd_hasher
.expand(&self.nonce.concat(STR_AUTH_KEY.into()), &mut hmac_key)
.expand_multi_info(&[&self.nonce, &STR_AUTH_KEY], &mut hmac_key)
.map_err(|_| InternalError::HkdfError)?;
randomized_pwd_hasher
.expand(&self.nonce.concat(STR_EXPORT_KEY.into()), &mut export_key)
.expand_multi_info(&[&self.nonce, &STR_EXPORT_KEY], &mut export_key)
.map_err(|_| InternalError::HkdfError)?;
let mut hmac = Hmac::<OprfHash<CS>>::new_from_slice(&hmac_key)
let mut hmac = SimpleHmac::<OprfHash<CS>>::new_from_slice(&hmac_key)
.map_err(|_| InternalError::HmacError)?;
hmac.update(&self.nonce);
hmac.update_iter(aad);
@@ -250,7 +249,7 @@ impl<CS: CipherSuite> Envelope<CS> {
Self {
mode: InnerEnvelopeMode::Zero,
nonce: GenericArray::default(),
hmac: GenericArray::default(),
hmac: GenericArray::default().into_ha0_4(),
}
}
@@ -261,15 +260,25 @@ impl<CS: CipherSuite> Envelope<CS> {
OutputSize::<OprfHash<CS>>::USIZE + NonceLen::USIZE
}
// TODO: check if ok
pub(crate) fn serialize(&self) -> GenericArray<u8, EnvelopeLen<CS>> {
self.nonce.concat_ext(&self.hmac)
use generic_array::typenum::Unsigned;
let mut buf = GenericArray::<u8, EnvelopeLen<CS>>::default();
buf[..NonceLen::USIZE].copy_from_slice(&self.nonce);
buf[NonceLen::USIZE..].copy_from_slice(&self.hmac);
buf
}
pub(crate) fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
mode: InnerEnvelopeMode::Internal,
nonce: bytes.take_array("nonce")?,
hmac: bytes.take_array("hmac")?,
hmac: bytes
.take_array::<OutputSize<OprfHash<CS>>>("hmac")?
.into_ha0_4(),
})
}
}
@@ -277,12 +286,12 @@ impl<CS: CipherSuite> Envelope<CS> {
// Helper functions
fn build_inner_envelope_internal<CS: CipherSuite>(
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
randomized_pwd_hasher: &Hkdf<OprfHash<CS>>,
nonce: GenericArray<u8, NonceLen>,
) -> Result<PublicKey<KeGroup<CS>>, ProtocolError> {
let mut keypair_seed = GenericArray::<_, <KeGroup<CS> as Group>::SkLen>::default();
randomized_pwd_hasher
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &mut keypair_seed)
.expand_multi_info(&[&nonce, &STR_PRIVATE_KEY], &mut keypair_seed)
.map_err(|_| InternalError::HkdfError)?;
let client_s_sk = PrivateKey::new(KeGroup::<CS>::derive_scalar(keypair_seed)?);
@@ -290,12 +299,12 @@ fn build_inner_envelope_internal<CS: CipherSuite>(
}
fn recover_keys_internal<CS: CipherSuite>(
randomized_pwd_hasher: Hkdf<OprfHash<CS>>,
randomized_pwd_hasher: &Hkdf<OprfHash<CS>>,
nonce: GenericArray<u8, NonceLen>,
) -> Result<KeyPair<KeGroup<CS>>, ProtocolError> {
let mut keypair_seed = GenericArray::<_, <KeGroup<CS> as Group>::SkLen>::default();
randomized_pwd_hasher
.expand(&nonce.concat(STR_PRIVATE_KEY.into()), &mut keypair_seed)
.expand_multi_info(&[&nonce, &STR_PRIVATE_KEY], &mut keypair_seed)
.map_err(|_| InternalError::HkdfError)?;
let client_s_sk = PrivateKey::new(KeGroup::<CS>::derive_scalar(keypair_seed)?);
let client_s_pk = client_s_sk.public_key();
+3 -2
View File
@@ -141,8 +141,8 @@ impl<T> From<InternalError> for ProtocolError<T> {
// See https://github.com/rust-lang/rust/issues/64715 and remove this when merged,
// and https://github.com/dtolnay/thiserror/issues/62 for why this comes up in our
// doc tests.
impl<T> From<::core::convert::Infallible> for ProtocolError<T> {
fn from(_: ::core::convert::Infallible) -> Self {
impl<T> From<Infallible> for ProtocolError<T> {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
@@ -164,6 +164,7 @@ impl ProtocolError {
actual_len,
},
Self::ReflectedValueError => ProtocolError::ReflectedValueError,
Self::Custom(infallible) => match infallible {},
}
}
}
+27 -15
View File
@@ -8,36 +8,45 @@
//! A convenience trait for digest bounds used throughout the library
use digest::block_api::{
BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, SmallBlockSizeUser,
};
use digest::block_buffer::Eager;
use digest::core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore};
use digest::{FixedOutputReset, HashMarker, OutputSizeUser};
use digest::{Digest, FixedOutputReset, HashMarker, OutputSizeUser};
use generic_array::typenum::{IsLess, Le, NonZero, U256};
pub(crate) type OutputSize<H> = <<H as CoreProxy>::Core as OutputSizeUser>::OutputSize;
/// Trait to simplify requirements for [`Hash`].
pub trait ProxyHash:
HashMarker + FixedOutputCore + BufferKindUser<BufferKind = Eager> + Default + Clone
HashMarker + FixedOutputCore + BufferKindUser<BufferKind = Eager> + OutputSizeUser + Default + Clone
where
<Self as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<Self as BlockSizeUser>::BlockSize, U256>: NonZero,
<Self as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<Self as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
{
}
impl<T: HashMarker + FixedOutputCore + BufferKindUser<BufferKind = Eager> + Default + Clone>
ProxyHash for T
impl<
T: HashMarker
+ FixedOutputCore
+ BufferKindUser<BufferKind = Eager>
+ OutputSizeUser
+ Default
+ Clone,
> ProxyHash for T
where
<Self as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<Self as BlockSizeUser>::BlockSize, U256>: NonZero,
<T as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<T as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
{
}
/// Trait inheriting the requirements from [`digest::Digest`] for compatibility
/// Trait inheriting the requirements from [`Digest`] for compatibility
/// with HKDF and HMAC Associated types could be simplified when they are made
/// as defaults: <https://github.com/rust-lang/rust/issues/29661>
pub trait Hash:
Default
+ HashMarker
+ Digest
+ OutputSizeUser<OutputSize = OutputSize<Self>>
+ BlockSizeUser
+ FixedOutputReset
@@ -45,14 +54,16 @@ pub trait Hash:
+ Clone
where
<Self as CoreProxy>::Core: ProxyHash,
<<Self as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<<Self as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<Self as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<Self as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<Self>: generic_array::ArrayLength,
{
}
impl<
T: Default
+ HashMarker
+ Digest
+ OutputSizeUser<OutputSize = OutputSize<Self>>
+ BlockSizeUser
+ FixedOutputReset
@@ -60,8 +71,9 @@ impl<
+ Clone,
> Hash for T
where
<Self as CoreProxy>::Core: ProxyHash,
<<Self as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<<Self as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<T as CoreProxy>::Core: ProxyHash,
<<T as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<T as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<T>: generic_array::ArrayLength,
{
}
+2 -2
View File
@@ -14,7 +14,7 @@ use curve25519_dalek::scalar;
use curve25519_dalek::traits::IsIdentity;
use generic_array::GenericArray;
use generic_array::typenum::U32;
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use subtle::ConstantTimeEq;
use zeroize::ZeroizeOnDrop;
@@ -43,7 +43,7 @@ impl Group for Curve25519 {
.and_then(|bytes| NonIdentity::from_bytes(bytes.into()))
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
fn random_sk<R: Rng + CryptoRng>(rng: &mut R) -> Self::Sk {
// Sample 32 random bytes and then clamp, as described in https://cr.yp.to/ecdh.html
let mut scalar_bytes = [0u8; 32];
rng.fill_bytes(&mut scalar_bytes);
+16 -15
View File
@@ -18,9 +18,8 @@ pub use ed25519_dalek;
use ed25519_dalek::hazmat::ExpandedSecretKey;
use ed25519_dalek::{SecretKey, Sha512};
use generic_array::GenericArray;
use generic_array::sequence::Concat;
use generic_array::typenum::{U32, U64};
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use zeroize::{Zeroize, ZeroizeOnDrop};
use super::Group;
@@ -30,7 +29,7 @@ use crate::key_exchange::sigma_i::hash_eddsa::implementation::HashEddsaImpl;
use crate::key_exchange::sigma_i::pure_eddsa::implementation::PureEddsaImpl;
pub use crate::key_exchange::sigma_i::shared::PreHash;
use crate::key_exchange::sigma_i::{CachedMessage, Message, MessageBuilder};
use crate::serialization::{SliceExt, UpdateExt};
use crate::serialization::{ConcatExt, SliceExt, UpdateExt};
/// Implementation for Ed25519.
pub struct Ed25519;
@@ -46,12 +45,12 @@ impl Group for Ed25519 {
}
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
let bytes = bytes.take_array("public key")?;
let bytes = bytes.take_array::<U32>("public key")?;
VerifyingKey::from_bytes(bytes.into())
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
fn random_sk<R: Rng + CryptoRng>(rng: &mut R) -> Self::Sk {
let mut sk = <[u8; 32]>::default();
rng.fill_bytes(&mut sk);
@@ -72,7 +71,7 @@ impl Group for Ed25519 {
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
Ok(SigningKey::from_bytes(
bytes.take_array("secret key")?.into(),
bytes.take_array::<U32>("secret key")?.into(),
))
}
}
@@ -399,7 +398,7 @@ pub struct Signature {
}
impl Signature {
/// Expects the `R` and `s` components of a Ed25519 signature with no added
/// Expects the `R` and `s` components of an Ed25519 signature with no added
/// framing.
pub fn from_slice(mut bytes: &[u8]) -> Result<Self, ProtocolError> {
Self::deserialize_take(&mut bytes)
@@ -407,9 +406,9 @@ impl Signature {
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
#[allow(non_snake_case)]
let R = CompressedEdwardsY(bytes.take_array("signature R")?.into());
let R = CompressedEdwardsY(bytes.take_array::<U32>("signature R")?.into());
let s = Scalar::from_canonical_bytes(bytes.take_array("signature s")?.into())
let s = Scalar::from_canonical_bytes(bytes.take_array::<U32>("signature s")?.into())
.into_option()
.ok_or(ProtocolError::SerializationError)?;
@@ -417,7 +416,8 @@ impl Signature {
}
fn serialize(&self) -> GenericArray<u8, U64> {
GenericArray::from(self.R.0).concat(GenericArray::from(self.s.to_bytes()))
GenericArray::<u8, U32>::from(self.R.0)
.cat(GenericArray::<u8, U32>::from(self.s.to_bytes()))
}
}
@@ -433,17 +433,18 @@ mod test {
use std::iter;
use ed25519_dalek::{Signer, SigningKey, Verifier, VerifyingKey};
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
use super::*;
#[test]
fn pure_eddsa() {
let mut message = [0; 1024];
OsRng.fill_bytes(&mut message);
UnwrapErr(SysRng).fill_bytes(&mut message);
let mut sk = SecretKey::default();
OsRng.fill_bytes(&mut sk);
UnwrapErr(SysRng).fill_bytes(&mut sk);
let signing_key = SigningKey::from_bytes(&sk);
let signature = signing_key.sign(&message);
@@ -472,12 +473,12 @@ mod test {
#[test]
fn hash_eddsa() {
let mut message = [0; 1024];
OsRng.fill_bytes(&mut message);
UnwrapErr(SysRng).fill_bytes(&mut message);
let message = Sha512::new_with_prefix(message);
let pre_hash = message.clone().finalize();
let mut sk = SecretKey::default();
OsRng.fill_bytes(&mut sk);
UnwrapErr(SysRng).fill_bytes(&mut sk);
let signing_key = SigningKey::from_bytes(&sk);
let signature = signing_key.sign_prehashed(message.clone(), None).unwrap();
+50 -67
View File
@@ -8,17 +8,18 @@
//! Implementation for EC curves via [`elliptic_curve`] traits.
use core::fmt::{self, Debug, Formatter};
use derive_where::derive_where;
use core::ops::Mul;
use digest::OutputSizeUser;
use digest::block_api::BlockSizeUser;
use elliptic_curve::group::GroupEncoding;
use elliptic_curve::ops::MulByGenerator;
use elliptic_curve::sec1::{ModulusSize, ToEncodedPoint};
use elliptic_curve::point::NonIdentity;
use elliptic_curve::sec1::{ModulusSize, ToSec1Point};
use elliptic_curve::{
CurveArithmetic, FieldBytesSize, NonZeroScalar, ProjectivePoint, Scalar, SecretKey, point,
CurveArithmetic, FieldBytesSize, Generate, NonZeroScalar, ProjectivePoint, Scalar, SecretKey,
};
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
use generic_array::typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U256};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, Rng};
use voprf::Mode;
use super::{Group, STR_OPAQUE_DERIVE_AUTH_KEY_PAIR};
@@ -29,15 +30,27 @@ use crate::serialization::SliceExt;
impl<G> Group for G
where
Self: CurveArithmetic + voprf::CipherSuite<Group = Self> + voprf::Group<Scalar = Scalar<Self>>,
FieldBytesSize<Self>: ModulusSize,
FieldBytesSize<Self>: ModulusSize + ArrayLength,
<FieldBytesSize<Self> as ModulusSize>::CompressedPointSize: ArrayLength,
ProjectivePoint<Self>: GroupEncoding<
Repr = GenericArray<u8, <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize>,
> + ToEncodedPoint<Self>,
Repr = hybrid_array::Array<
u8,
<FieldBytesSize<Self> as ModulusSize>::CompressedPointSize,
>,
> + ToSec1Point<Self>,
// Bounds required by voprf::CipherSuite
<Self as voprf::Group>::SecurityLevel: Mul<U2>,
<<Self as voprf::CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength
+ IsLess<U256>
+ IsLessOrEqual<
<<Self as voprf::CipherSuite>::Hash as BlockSizeUser>::BlockSize,
Output = True,
> + IsGreaterOrEqual<Prod<<Self as voprf::Group>::SecurityLevel, U2>, Output = True>,
{
// We don't use `elliptic_curve::PublicKey` because it stores its internals in a
// format ideal for serialization and not computation. This is inconsistent with
// our other implementations.
type Pk = NonIdentity<Self>;
type Pk = NonIdentity<ProjectivePoint<Self>>;
type PkLen = <FieldBytesSize<Self> as ModulusSize>::CompressedPointSize;
@@ -46,18 +59,19 @@ where
type SkLen = FieldBytesSize<Self>;
fn serialize_pk(pk: &Self::Pk) -> GenericArray<u8, Self::PkLen> {
GenericArray::clone_from_slice(pk.0.to_encoded_point(true).as_bytes())
GenericArray::from_slice(pk.to_sec1_point(true).as_bytes()).clone()
}
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
point::NonIdentity::<ProjectivePoint<Self>>::from_bytes(&bytes.take_array("public key")?)
.into_option()
.map(NonIdentity)
.ok_or(ProtocolError::SerializationError)
NonIdentity::<ProjectivePoint<Self>>::from_bytes(
&bytes.take_array("public key")?.into_ha0_4(),
)
.into_option()
.ok_or(ProtocolError::SerializationError)
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
SecretKey::<Self>::random(rng)
fn random_sk<R: Rng + CryptoRng>(rng: &mut R) -> Self::Sk {
SecretKey::<Self>::generate_from_rng(rng)
}
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError> {
@@ -70,21 +84,15 @@ where
}
fn public_key(sk: &Self::Sk) -> Self::Pk {
// Non-panicking version in https://github.com/RustCrypto/traits/pull/1833.
NonIdentity(
point::NonIdentity::new(ProjectivePoint::<Self>::mul_by_generator(
&sk.to_nonzero_scalar(),
))
.expect("multiplying with a non-zero scalar can never yield the identity element"),
)
NonIdentity::<ProjectivePoint<Self>>::mul_by_generator(&sk.to_nonzero_scalar())
}
fn serialize_sk(sk: &Self::Sk) -> GenericArray<u8, Self::SkLen> {
sk.to_bytes()
GenericArray::from(sk.to_bytes())
}
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
SecretKey::<Self>::from_bytes(&bytes.take_array("secret key")?)
SecretKey::<Self>::from_bytes(&bytes.take_array("secret key")?.into_ha0_4())
.map_err(|_| ProtocolError::SerializationError)
}
}
@@ -92,51 +100,26 @@ where
impl<G> DiffieHellman<G> for SecretKey<G>
where
G: CurveArithmetic + voprf::CipherSuite<Group = G> + voprf::Group<Scalar = Scalar<G>>,
FieldBytesSize<G>: ModulusSize,
FieldBytesSize<G>: ModulusSize + ArrayLength,
<FieldBytesSize<G> as ModulusSize>::CompressedPointSize: ArrayLength,
ProjectivePoint<G>: GroupEncoding<
Repr = GenericArray<u8, <FieldBytesSize<G> as ModulusSize>::CompressedPointSize>,
> + ToEncodedPoint<G>,
Repr = hybrid_array::Array<u8, <FieldBytesSize<G> as ModulusSize>::CompressedPointSize>,
> + ToSec1Point<G>,
<G as voprf::Group>::SecurityLevel: Mul<U2>,
<<G as voprf::CipherSuite>::Hash as OutputSizeUser>::OutputSize: ArrayLength
+ IsLess<U256>
+ IsLessOrEqual<<<G as voprf::CipherSuite>::Hash as BlockSizeUser>::BlockSize, Output = True>
+ IsGreaterOrEqual<Prod<<G as voprf::Group>::SecurityLevel, U2>, Output = True>,
{
fn diffie_hellman(
&self,
pk: &NonIdentity<G>,
pk: &NonIdentity<ProjectivePoint<G>>,
) -> GenericArray<u8, <FieldBytesSize<G> as ModulusSize>::CompressedPointSize> {
GenericArray::clone_from_slice(
(pk.0 * self.to_nonzero_scalar())
.to_encoded_point(true)
GenericArray::from_slice(
(pk * self.to_nonzero_scalar())
.to_sec1_point(true)
.as_bytes(),
)
.clone()
}
}
/// Wrapper around [`NonIdentity`](point::NonIdentity) to [`Eq`].
// TODO: remove after https://github.com/RustCrypto/traits/pull/1834.
#[derive_where(Clone, Copy)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(
bound(
deserialize = "point::NonIdentity<ProjectivePoint<G>>: serde::Deserialize<'de>",
serialize = "point::NonIdentity<ProjectivePoint<G>>: serde::Serialize"
),
transparent
)
)]
pub struct NonIdentity<G: CurveArithmetic>(pub point::NonIdentity<ProjectivePoint<G>>);
impl<G: CurveArithmetic> Debug for NonIdentity<G> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_tuple("NonIdentity")
.field(&self.0.to_point())
.finish()
}
}
impl<G: CurveArithmetic> PartialEq for NonIdentity<G> {
fn eq(&self, other: &Self) -> bool {
self.0.to_point().eq(&other.0.to_point())
}
}
impl<G: CurveArithmetic> Eq for NonIdentity<G> {}
+5 -4
View File
@@ -17,7 +17,8 @@ pub mod elliptic_curve;
pub mod ristretto255;
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use hybrid_array::ArraySize;
use rand::{CryptoRng, Rng};
use zeroize::ZeroizeOnDrop;
use crate::errors::{InternalError, ProtocolError};
@@ -29,11 +30,11 @@ pub trait Group {
/// Public key
type Pk: Clone;
/// Length of the public key
type PkLen: ArrayLength<u8>;
type PkLen: ArrayLength + ArraySize;
/// Secret key
type Sk: Clone + ZeroizeOnDrop;
/// Length of the secret key
type SkLen: ArrayLength<u8>;
type SkLen: ArrayLength + ArraySize;
/// Serializes `self`
fn serialize_pk(pk: &Self::Pk) -> GenericArray<u8, Self::PkLen>;
@@ -44,7 +45,7 @@ pub trait Group {
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError>;
/// Generate a random secret key
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk;
fn random_sk<R: Rng + CryptoRng>(rng: &mut R) -> Self::Sk;
/// Deterministically derive a [`Self::Sk`] from `seed`.
fn derive_scalar(seed: GenericArray<u8, Self::SkLen>) -> Result<Self::Sk, InternalError>;
+24 -13
View File
@@ -13,11 +13,12 @@ use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use curve25519_dalek::traits::IsIdentity;
use digest::core_api::BlockSizeUser;
use digest::block_api::BlockSizeUser;
use digest::{FixedOutput, HashMarker};
use generic_array::GenericArray;
use generic_array::typenum::{IsLess, IsLessOrEqual, U32, U256};
use rand::{CryptoRng, RngCore};
use generic_array::typenum::{IsGreaterOrEqual, IsLess, IsLessOrEqual, Prod, True, U2, U32, U256};
use hybrid_array::Array;
use rand::{CryptoRng, Rng, TryCryptoRng, TryRng};
use voprf::Mode;
use zeroize::ZeroizeOnDrop;
@@ -42,15 +43,19 @@ impl Group for Ristretto255 {
}
fn deserialize_take_pk(bytes: &mut &[u8]) -> Result<Self::Pk, ProtocolError> {
CompressedRistretto(bytes.take_array("public key")?.into())
CompressedRistretto(bytes.take_array::<U32>("public key")?.into())
.decompress()
.ok_or(ProtocolError::SerializationError)
.and_then(NonIdentity::from_point)
}
fn random_sk<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Sk {
fn random_sk<R: Rng + CryptoRng>(rng: &mut R) -> Self::Sk {
loop {
let scalar = Scalar::random(rng);
let mut bytes = [0u8; 64];
rng.fill_bytes(&mut bytes);
let scalar = Scalar::from_bytes_mod_order_wide(&bytes);
if scalar != Scalar::ZERO {
break NonZeroScalar(scalar);
@@ -73,7 +78,7 @@ impl Group for Ristretto255 {
}
fn deserialize_take_sk(bytes: &mut &[u8]) -> Result<Self::Sk, ProtocolError> {
Scalar::from_canonical_bytes(bytes.take_array("secret key")?.into())
Scalar::from_canonical_bytes(bytes.take_array::<U32>("secret key")?.into())
.into_option()
.ok_or(ProtocolError::SerializationError)
.and_then(NonZeroScalar::from_scalar)
@@ -149,7 +154,7 @@ where
}
impl voprf::CipherSuite for Ristretto255 {
const ID: &'static str = voprf::Ristretto255::ID;
const ID: &'static [u8] = voprf::Ristretto255::ID;
type Group = <voprf::Ristretto255 as voprf::CipherSuite>::Group;
@@ -165,13 +170,17 @@ impl voprf::Group for Ristretto255 {
type ScalarLen = <voprf::Ristretto255 as voprf::Group>::ScalarLen;
type SecurityLevel = <voprf::Ristretto255 as voprf::Group>::SecurityLevel;
fn hash_to_curve<H>(
input: &[&[u8]],
dst: &[&[u8]],
) -> voprf::Result<Self::Elem, voprf::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 as voprf::Group>::SecurityLevel, U2>, Output = True>,
{
<voprf::Ristretto255 as voprf::Group>::hash_to_curve::<H>(input, dst)
}
@@ -182,7 +191,9 @@ impl voprf::Group for Ristretto255 {
) -> voprf::Result<Self::Scalar, voprf::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 as voprf::Group>::SecurityLevel, U2>, Output = True>,
{
<voprf::Ristretto255 as voprf::Group>::hash_to_scalar::<H>(input, dst)
}
@@ -195,7 +206,7 @@ impl voprf::Group for Ristretto255 {
<voprf::Ristretto255 as voprf::Group>::identity_elem()
}
fn serialize_elem(elem: Self::Elem) -> GenericArray<u8, Self::ElemLen> {
fn serialize_elem(elem: Self::Elem) -> Array<u8, Self::ElemLen> {
<voprf::Ristretto255 as voprf::Group>::serialize_elem(elem)
}
@@ -203,7 +214,7 @@ impl voprf::Group for Ristretto255 {
<voprf::Ristretto255 as voprf::Group>::deserialize_elem(element_bits)
}
fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Scalar {
fn random_scalar<R: TryRng + TryCryptoRng>(rng: &mut R) -> voprf::Result<Self::Scalar> {
<voprf::Ristretto255 as voprf::Group>::random_scalar(rng)
}
@@ -215,7 +226,7 @@ impl voprf::Group for Ristretto255 {
<voprf::Ristretto255 as voprf::Group>::is_zero_scalar(scalar)
}
fn serialize_scalar(scalar: Self::Scalar) -> GenericArray<u8, Self::ScalarLen> {
fn serialize_scalar(scalar: Self::Scalar) -> Array<u8, Self::ScalarLen> {
<voprf::Ristretto255 as voprf::Group>::serialize_scalar(scalar)
}
+32 -22
View File
@@ -21,11 +21,12 @@ use core::ops::Add;
use derive_where::derive_where;
use digest::Output;
use digest::core_api::{BlockSizeUser, CoreProxy};
use digest::block_api::{CoreProxy, SmallBlockSizeUser};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U2, U256};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use hybrid_array::Array;
use rand::{CryptoRng, Rng};
use voprf::{BlindedElement, EvaluationElement};
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -33,7 +34,7 @@ use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::ciphersuite::KeHash;
use crate::ciphersuite::{CipherSuite, OprfGroup};
use crate::errors::ProtocolError;
use crate::hash::{Hash, ProxyHash};
use crate::hash::{Hash, OutputSize, ProxyHash};
use crate::key_exchange::group::Group;
use crate::key_exchange::shared::{NonceLen, STR_CONTEXT};
use crate::keypair::{PrivateKey, PublicKey};
@@ -44,8 +45,9 @@ use crate::serialization::{SliceExt, i2osp};
pub trait KeyExchange
where
<Self::Hash as CoreProxy>::Core: ProxyHash,
<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<<Self::Hash as CoreProxy>::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<Self::Hash as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<Self::Hash as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<Self::Hash>: ArrayLength,
{
/// The group used for the key exchange.
type Group: Group;
@@ -71,12 +73,12 @@ where
/// Client generates [`KE1Message`](Self::KE1Message) and
/// [`KE1State`](Self::KE1State).
fn generate_ke1<R: RngCore + CryptoRng>(
fn generate_ke1<R: Rng + CryptoRng>(
rng: &mut R,
) -> Result<GenerateKe1Result<Self>, ProtocolError>;
/// Server generates [`KE2Builder`](Self::KE2Builder).
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: Rng + CryptoRng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -92,7 +94,7 @@ where
) -> Self::KE2BuilderData<'a, CS>;
/// Server generates the input without a remote key.
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
builder: &Self::KE2Builder<'_, CS>,
rng: &mut R,
server_s_sk: &PrivateKey<Self::Group>,
@@ -107,7 +109,7 @@ where
/// Client generates [`KE3Message`](Self::KE3Message) and the session key.
#[allow(clippy::too_many_arguments)]
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -137,7 +139,7 @@ where
)]
#[derive_where(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Zeroize)]
pub struct SerializedCredentialRequest<CS: CipherSuite>(
GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
Array<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
);
impl<CS: CipherSuite> SerializedCredentialRequest<CS> {
@@ -154,17 +156,20 @@ impl<CS: CipherSuite> SerializedCredentialRequest<CS> {
/// Returns a [`SerializedCredentialRequest`] deserialized from the given
/// `bytes`.
pub fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self(bytes.take_array("blinded element")?))
Ok(Self(bytes.take_array("blinded element")?.into_ha0_4()))
}
}
type SerializedCredentialRequestLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Group>::ElemLen;
impl<CS: CipherSuite> Serialize for SerializedCredentialRequest<CS> {
impl<CS: CipherSuite> Serialize for SerializedCredentialRequest<CS>
where
<OprfGroup<CS> as voprf::Group>::ElemLen: ArrayLength,
{
type Len = SerializedCredentialRequestLen<CS>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.0.clone()
GenericArray::from_slice(self.0.as_slice()).clone()
}
}
@@ -176,7 +181,7 @@ impl<CS: CipherSuite> Serialize for SerializedCredentialRequest<CS> {
)]
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
pub struct SerializedCredentialResponse<CS: CipherSuite> {
evaluation_element: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
evaluation_element: Array<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>,
masking_nonce: GenericArray<u8, NonceLen>,
masked_response: MaskedResponse<CS>,
}
@@ -207,7 +212,7 @@ impl<CS: CipherSuite> SerializedCredentialResponse<CS> {
/// `bytes`.
pub fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
evaluation_element: input.take_array("evaluation element")?,
evaluation_element: input.take_array("evaluation element")?.into_ha0_4(),
masking_nonce: input.take_array("masking nonce")?,
masked_response: MaskedResponse::deserialize_take(input)?,
})
@@ -221,16 +226,21 @@ impl<CS: CipherSuite> Serialize for SerializedCredentialResponse<CS>
where
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
SerializedCredentialResponseLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
SerializedCredentialResponseLen<CS>: ArrayLength,
{
type Len = SerializedCredentialResponseLen<CS>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.evaluation_element
.clone()
.concat(self.masking_nonce)
.concat(self.masked_response.serialize())
let elem = GenericArray::<u8, <OprfGroup<CS> as voprf::Group>::ElemLen>::from_slice(
self.evaluation_element.as_slice(),
)
.clone();
Concat::concat(
Concat::concat(elem, self.masking_nonce),
self.masked_response.serialize(),
)
}
}
@@ -359,7 +369,7 @@ pub trait Deserialize: Sized {
/// Serialization trait for key exchange types.
pub trait Serialize {
/// The length of the serialized types.
type Len: ArrayLength<u8>;
type Len: ArrayLength;
/// Serialize [`Self`] to a fixed-length byte array.
fn serialize(&self) -> GenericArray<u8, Self::Len>;
+45 -34
View File
@@ -9,14 +9,15 @@
use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::block_api::{CoreProxy, SmallBlockSizeUser};
use digest::{Digest, Mac, Output, OutputSizeUser, Update};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U1, U2, U32, U256, Unsigned};
use generic_array::{ArrayLength, GenericArray};
use hkdf::{Hkdf, HkdfExtract};
use hmac::Hmac;
use rand::{CryptoRng, RngCore};
use hkdf::SimpleHkdf as Hkdf;
use hkdf::SimpleHkdfExtract as HkdfExtract;
use hmac::{KeyInit, SimpleHmac};
use rand::{CryptoRng, Rng};
use super::{
Deserialize, GenerateKe1Result, KeyExchange, Serialize, SerializedContext,
@@ -106,8 +107,9 @@ pub(super) struct DerivedKeys<H: OutputSizeUser> {
pub(super) struct Ke2BuilderCommon<G: Group, H: Hash>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
G::Sk: DiffieHellman<G>,
{
pub(super) server_nonce: GenericArray<u8, NonceLen>,
@@ -126,7 +128,7 @@ where
// Helper functions
pub(super) fn generate_ke1<
R: RngCore + CryptoRng,
R: Rng + CryptoRng,
KE: KeyExchange<KE1State = Ke1State<G>, KE1Message = Ke1Message<G>>,
G: Group,
>(
@@ -150,7 +152,7 @@ pub(super) fn generate_ke1<
}
// Generate a random nonce up to NonceLen::USIZE bytes.
pub(super) fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
pub(super) fn generate_nonce<R: Rng + CryptoRng>(rng: &mut R) -> GenericArray<u8, NonceLen> {
let mut nonce_bytes = GenericArray::default();
rng.fill_bytes(&mut nonce_bytes);
nonce_bytes
@@ -190,11 +192,12 @@ pub(super) fn ke2_builder_common<'a, G, H, CS, R>(
where
G: Group,
H: Hash,
R: RngCore + CryptoRng,
R: Rng + CryptoRng,
CS: CipherSuite,
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
G::Sk: DiffieHellman<G>,
CS::KeyExchange: KeyExchange<Group = G, Hash = H>,
{
@@ -238,8 +241,9 @@ pub(super) fn derive_keys<'a, H: Hash>(
) -> Result<DerivedKeys<H>, ProtocolError>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
let mut hkdf = HkdfExtract::<H>::new(None);
@@ -280,19 +284,20 @@ pub(super) fn compute_ke2_macs<H: Hash>(
) -> Result<(Output<H>, Output<H>), ProtocolError>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
let mut mac_hasher =
Hmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
SimpleHmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
Mac::update(&mut mac_hasher, transcript_digest);
let mac = mac_hasher.finalize().into_bytes();
transcript_hasher.update(&mac);
Update::update(transcript_hasher, &mac);
let finalized_transcript = transcript_hasher.clone().finalize();
let mut expected_mac_hasher =
Hmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
SimpleHmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
Mac::update(&mut expected_mac_hasher, &finalized_transcript);
let expected_mac = expected_mac_hasher.finalize().into_bytes();
@@ -311,23 +316,24 @@ pub(super) fn finalize_ke3_transcript<'a, H: Hash>(
) -> Result<(DerivedKeys<H>, Output<H>), ProtocolError>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
let transcript_digest = transcript_hasher.clone().finalize();
let derived_keys = derive_keys::<H>(shared_secrets, &transcript_digest)?;
let mut server_mac_hasher =
Hmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
SimpleHmac::<H>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
Mac::update(&mut server_mac_hasher, &transcript_digest);
server_mac_hasher
.verify(server_mac)
.map_err(|_| ProtocolError::InvalidLoginError)?;
transcript_hasher.update(server_mac.as_slice());
Update::update(transcript_hasher, server_mac);
let finalized_transcript = transcript_hasher.clone().finalize();
let mut client_mac_hasher =
Hmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
SimpleHmac::<H>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
Mac::update(&mut client_mac_hasher, &finalized_transcript);
let client_mac = client_mac_hasher.finalize().into_bytes();
@@ -342,8 +348,9 @@ fn hkdf_expand_label<H: Hash>(
) -> Result<Output<H>, ProtocolError>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
let h = Hkdf::<H>::from_prk(secret).map_err(|_| InternalError::HkdfError)?;
hkdf_expand_label_extracted(&h, label, context)
@@ -356,10 +363,11 @@ fn hkdf_expand_label_extracted<H: Hash>(
) -> Result<Output<H>, ProtocolError>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
let mut okm = GenericArray::default();
let mut okm = GenericArray::default().into_ha0_4();
let length = i2osp::<U2>(OutputSize::<H>::USIZE)?;
let label_length = i2osp::<U1>(STR_OPAQUE.len() + label.len())?;
@@ -386,8 +394,9 @@ fn derive_secrets<H: Hash>(
) -> Result<Output<H>, ProtocolError>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
hkdf_expand_label_extracted::<H>(hkdf, label, hashed_derivation_transcript)
}
@@ -407,12 +416,14 @@ impl<G: Group> Serialize for Ke1State<G>
where
// Ke1State: KeSk + Nonce
G::SkLen: Add<NonceLen>,
Sum<G::SkLen, NonceLen>: ArrayLength<u8>,
Sum<G::SkLen, NonceLen>: ArrayLength,
{
type Len = Sum<G::SkLen, NonceLen>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.client_e_sk.serialize().concat(self.client_nonce)
let a = self.client_e_sk.serialize();
GenericArray::concat(a, self.client_nonce)
}
}
@@ -429,7 +440,7 @@ impl<G: Group> Serialize for Ke1Message<G>
where
// Ke1Message: Nonce + KePk
NonceLen: Add<G::PkLen>,
Sum<NonceLen, G::PkLen>: ArrayLength<u8>,
Sum<NonceLen, G::PkLen>: ArrayLength,
{
type Len = Sum<NonceLen, G::PkLen>;
@@ -476,7 +487,7 @@ impl<G: Group> Ke1MessageIter<G> {
impl<G: Group> Ke1MessageIter<G>
where
NonceLen: Add<G::PkLen>,
Ke1MessageIterLen<G>: ArrayLength<u8>,
Ke1MessageIterLen<G>: ArrayLength,
{
pub(crate) fn serialize(&self) -> GenericArray<u8, Ke1MessageIterLen<G>> {
self.client_nonce.concat(self.client_e_pk.clone())
+40 -85
View File
@@ -11,23 +11,19 @@
use core::marker::PhantomData;
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{FixedOutputReset, HashMarker};
use ecdsa::{PrimeCurve, SignatureSize, hazmat};
use elliptic_curve::{
CurveArithmetic, Field, FieldBytes, FieldBytesEncoding, FieldBytesSize, PrimeField, Scalar,
SecretKey,
};
use digest::block_api::{BlockSizeUser, EagerHash};
use digest::{Digest, FixedOutputReset, HashMarker};
use ecdsa::{EcdsaCurve, SignatureSize};
use elliptic_curve::point::NonIdentity;
use elliptic_curve::{CurveArithmetic, FieldBytes, ProjectivePoint, SecretKey};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use zeroize::Zeroize;
use hybrid_array::ArraySize;
use rand::{CryptoRng, Rng};
use super::{Message, MessageBuilder, SignatureProtocol};
use crate::ciphersuite::CipherSuite;
use crate::errors::ProtocolError;
use crate::key_exchange::group::Group;
use crate::key_exchange::group::elliptic_curve::NonIdentity;
pub use crate::key_exchange::sigma_i::shared::PreHash;
use crate::serialization::SliceExt;
@@ -39,23 +35,21 @@ pub struct Ecdsa<G, H>(PhantomData<(G, H)>);
impl<G, H> SignatureProtocol for Ecdsa<G, H>
where
G: CurveArithmetic + Group<Sk = SecretKey<G>, Pk = NonIdentity<G>> + PrimeCurve,
SignatureSize<G>: ArrayLength<u8>,
H: Clone
+ Default
+ BlockSizeUser
+ FixedOutputReset<OutputSize = FieldBytesSize<G>>
+ HashMarker,
G: CurveArithmetic
+ Group<Sk = SecretKey<G>, Pk = NonIdentity<ProjectivePoint<G>>>
+ EcdsaCurve,
SignatureSize<G>: ArrayLength + ArraySize,
H: EagerHash + FixedOutputReset + BlockSizeUser + HashMarker + Digest + Clone + Default,
{
type Group = G;
type Signature = Signature<G>;
type Signature = ecdsa::Signature<G>;
type SignatureLen = SignatureSize<G>;
type VerifyState<CS: CipherSuite, KE: Group> = PreHash<H>;
// We use a manual implementation of `RandomizedPrehashSigner` to use the same
// hash for the message as for generating `k`. See
// https://github.com/RustCrypto/signatures/issues/949.
fn sign<'a, R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
fn sign<'a, R: CryptoRng + Rng, CS: CipherSuite, KE: Group>(
sk: &<Self::Group as Group>::Sk,
rng: &mut R,
message: &Message<CS, KE>,
@@ -63,7 +57,7 @@ where
let hash = message.hash::<H>();
(
Signature(sign::<_, G, H>(sk, rng, &hash.sign.finalize_fixed())),
sign::<_, G, H>(sk, rng, &hash.sign.finalize_fixed()),
PreHash(hash.verify.finalize_fixed()),
)
}
@@ -74,96 +68,55 @@ where
state: Self::VerifyState<CS, KE>,
signature: &Self::Signature,
) -> Result<(), ProtocolError> {
verify(pk, &state.0, &signature.0)
verify(pk, &state.0, signature)
}
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
signature.0.to_bytes()
GenericArray::from_slice(signature.to_bytes().as_slice()).clone()
}
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
ecdsa::Signature::from_bytes(&bytes.take_array("signature")?)
.map(Signature)
ecdsa::Signature::from_bytes(&bytes.take_array("signature")?.into_ha0_4())
.map_err(|_| ProtocolError::SerializationError)
}
}
fn sign<R, C, H>(sk: &SecretKey<C>, rng: &mut R, pre_hash: &[u8]) -> ecdsa::Signature<C>
where
R: CryptoRng + RngCore,
C: CurveArithmetic + PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
H: Default + BlockSizeUser + FixedOutputReset<OutputSize = FieldBytesSize<C>> + HashMarker,
R: CryptoRng + Rng,
C: CurveArithmetic + EcdsaCurve,
SignatureSize<C>: ArraySize,
H: Digest + BlockSizeUser + FixedOutputReset,
{
let repr = sk.to_bytes();
let order = C::ORDER.encode_field_bytes();
let z =
hazmat::bits2field::<C>(pre_hash).expect("hash output can not be shorter than a scalar");
// This can only fail if the computed `r` or `s` are zero, in which case we just
// retry with a new `k`. See https://github.com/RustCrypto/signatures/pull/951.
loop {
let mut ad = FieldBytes::<C>::default();
rng.fill_bytes(&mut ad);
let k =
Scalar::<C>::from_repr(rfc6979::generate_k::<H, _>(&repr, &order, &z, &ad)).unwrap();
if let Ok((signature, _)) = hazmat::sign_prehashed::<C, _>(&sk.to_nonzero_scalar(), k, &z) {
break signature;
}
}
let mut ad = FieldBytes::<C>::default();
rng.fill_bytes(&mut ad);
ecdsa::hazmat::sign_prehashed_rfc6979::<C, H>(&sk.to_nonzero_scalar(), pre_hash, &ad).0
}
fn verify<C>(
pk: &NonIdentity<C>,
pk: &NonIdentity<ProjectivePoint<C>>,
pre_hash: &[u8],
signature: &ecdsa::Signature<C>,
) -> Result<(), ProtocolError>
where
C: CurveArithmetic + PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
C: CurveArithmetic + EcdsaCurve,
SignatureSize<C>: ArraySize,
{
let z =
hazmat::bits2field::<C>(pre_hash).expect("hash output can not be shorter than a scalar");
hazmat::verify_prehashed(&pk.0.to_point(), &z, signature)
ecdsa::hazmat::verify_prehashed(&pk.to_point(), pre_hash, signature)
.map_err(|_| ProtocolError::InvalidLoginError)
}
/// Wrapper around [`ecdsa::Signature`] to implement [`Zeroize`].
// TODO: remove after https://github.com/RustCrypto/signatures/pull/948.
#[derive_where(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "", transparent)
)]
pub struct Signature<G: CurveArithmetic + PrimeCurve>(pub ecdsa::Signature<G>)
where
SignatureSize<G>: ArrayLength<u8>;
impl<G: CurveArithmetic + PrimeCurve> Zeroize for Signature<G>
where
SignatureSize<G>: ArrayLength<u8>,
{
fn zeroize(&mut self) {
self.0 = ecdsa::Signature::from_scalars(
Into::<FieldBytes<G>>::into(Scalar::<G>::ONE),
Into::<FieldBytes<G>>::into(Scalar::<G>::ONE),
)
.expect("failed to create `Signature` with non-zero `Scalar`s");
}
}
#[test]
fn ecdsa() {
use std::vec;
use digest::Digest;
use p256::ecdsa::signature::{DigestVerifier, RandomizedDigestSigner};
use ecdsa::signature::hazmat::PrehashVerifier;
use p256::ecdsa::signature::RandomizedDigestSigner;
use p256::ecdsa::{Signature, SigningKey, VerifyingKey};
use p256::{NistP256, PublicKey};
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
use sha2::Sha256;
use crate::tests::mock_rng::CycleRng;
@@ -171,22 +124,24 @@ fn ecdsa() {
let mut rng = CycleRng::new(vec![1; 32]);
let mut message = [0; 1024];
OsRng.fill_bytes(&mut message);
UnwrapErr(SysRng).fill_bytes(&mut message);
let hash = Sha256::new_with_prefix(message);
let sk = NistP256::random_sk(&mut OsRng);
let sk = NistP256::random_sk(&mut UnwrapErr(SysRng));
let signing_key = SigningKey::from(sk.clone());
let signature: Signature = signing_key.sign_digest_with_rng(&mut rng, hash.clone());
let signature: Signature = signing_key.sign_digest_with_rng(&mut rng, |d: &mut Sha256| {
d.update(message);
});
let custom_signature = sign::<_, _, Sha256>(&sk, &mut rng, &hash.clone().finalize());
assert_eq!(signature, custom_signature);
let pk = NistP256::public_key(&sk);
let verifying_key = VerifyingKey::from(PublicKey::from(pk.0));
let verifying_key = VerifyingKey::from(PublicKey::from(&pk));
verifying_key
.verify_digest(hash.clone(), &signature)
.verify_prehash(&hash.clone().finalize(), &signature)
.unwrap();
verify(&pk, &hash.finalize(), &custom_signature).unwrap();
}
+3 -3
View File
@@ -12,7 +12,7 @@
use core::marker::PhantomData;
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use zeroize::Zeroize;
use self::implementation::HashEddsaImpl;
@@ -33,7 +33,7 @@ impl<G: HashEddsaImpl> SignatureProtocol for HashEddsa<G> {
type SignatureLen = G::SignatureLen;
type VerifyState<CS: CipherSuite, KE: Group> = G::VerifyState<CS, KE>;
fn sign<'a, R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
fn sign<'a, R: CryptoRng + Rng, CS: CipherSuite, KE: Group>(
sk: &<Self::Group as Group>::Sk,
_: &mut R,
message: &Message<CS, KE>,
@@ -66,7 +66,7 @@ pub(in super::super) mod implementation {
pub trait HashEddsaImpl: Group {
type Signature: Clone + Zeroize;
type SignatureLen: ArrayLength<u8>;
type SignatureLen: ArrayLength;
type VerifyState<CS: CipherSuite, KE: Group>: Clone + Zeroize;
fn sign<CS: CipherSuite, KE: Group>(
+16 -17
View File
@@ -10,7 +10,6 @@ use core::ops::Add;
use derive_where::derive_where;
use digest::{FixedOutput, Output, Update};
use generic_array::sequence::Concat;
use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray};
use zeroize::Zeroize;
@@ -26,7 +25,7 @@ use crate::key_exchange::{
SerializedIdentifier, SerializedIdentifiers,
};
use crate::opaque::MaskedResponseLen;
use crate::serialization::{SliceExt, UpdateExt};
use crate::serialization::{ConcatExt, SliceExt, UpdateExt};
/// This holds the message to be signed and the message to be verified.
///
@@ -242,7 +241,7 @@ impl<CS: CipherSuite, KE: Group> Deserialize for CachedMessage<CS, KE> {
credential_response: SerializedCredentialResponse::deserialize_take(input)?,
server_nonce: input.take_array("server nonce")?,
server_e_pk: input.take_array("serialized server ephemeral key")?,
server_mac: input.take_array("server mac")?,
server_mac: input.take_array("server mac")?.into_ha0_4(),
})
}
}
@@ -264,20 +263,20 @@ type CachedMessageLen<CS: CipherSuite, KE: Group> = Sum<
impl<CS: CipherSuite, KE: Group> Serialize for CachedMessage<CS, KE>
where
SerializedCredentialRequestLen<CS>: ArrayLength<u8> + Add<Ke1MessageIterLen<KE>>,
SerializedCredentialRequestLen<CS>: ArrayLength + Add<Ke1MessageIterLen<KE>>,
Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>:
ArrayLength<u8> + Add<SerializedCredentialResponseLen<CS>>,
ArrayLength + Add<SerializedCredentialResponseLen<CS>>,
Sum<
Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>,
SerializedCredentialResponseLen<CS>,
>: ArrayLength<u8> + Add<NonceLen>,
>: ArrayLength + Add<NonceLen>,
Sum<
Sum<
Sum<SerializedCredentialRequestLen<CS>, Ke1MessageIterLen<KE>>,
SerializedCredentialResponseLen<CS>,
>,
NonceLen,
>: ArrayLength<u8> + Add<KE::PkLen>,
>: ArrayLength + Add<KE::PkLen>,
Sum<
Sum<
Sum<
@@ -287,26 +286,26 @@ where
NonceLen,
>,
KE::PkLen,
>: ArrayLength<u8> + Add<OutputSize<KeHash<CS>>>,
CachedMessageLen<CS, KE>: ArrayLength<u8>,
>: ArrayLength + Add<OutputSize<KeHash<CS>>>,
CachedMessageLen<CS, KE>: ArrayLength,
// Ke1MessageIter
NonceLen: Add<KE::PkLen>,
Ke1MessageIterLen<KE>: ArrayLength<u8>,
Ke1MessageIterLen<KE>: ArrayLength,
// CredentialResponseParts
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
SerializedCredentialResponseLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
SerializedCredentialResponseLen<CS>: ArrayLength,
{
type Len = CachedMessageLen<CS, KE>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.credential_request
.serialize()
.concat(self.ke1_message.serialize())
.concat(self.credential_response.serialize())
.concat(self.server_nonce)
.concat(self.server_e_pk.clone())
.concat(self.server_mac.clone())
.cat(self.ke1_message.serialize())
.cat(self.credential_response.serialize())
.cat(self.server_nonce)
.cat(self.server_e_pk.clone())
.cat(GenericArray::from_slice(self.server_mac.as_slice()).clone())
}
}
+75 -55
View File
@@ -23,13 +23,13 @@ use core::marker::PhantomData;
use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Mac, Output, OutputSizeUser};
use digest::block_api::{BlockSizeUser, CoreProxy, SmallBlockSizeUser};
use digest::{Mac, Output, OutputSizeUser};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
use generic_array::{ArrayLength, GenericArray};
use hmac::Hmac;
use rand::{CryptoRng, RngCore};
use hmac::{KeyInit, SimpleHmac};
use rand::{CryptoRng, Rng};
use subtle::{ConstantTimeEq, CtOption};
use zeroize::Zeroize;
@@ -49,7 +49,7 @@ pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State};
use crate::key_exchange::shared::{derive_keys, generate_ke1, generate_nonce, transcript};
use crate::keypair::{KeyPair, PrivateKey, PublicKey};
use crate::opaque::Identifiers;
use crate::serialization::{SliceExt, UpdateExt};
use crate::serialization::{ConcatExt, SliceExt, UpdateExt};
/// The SIGMA-I key exchange implementation
///
@@ -95,7 +95,7 @@ pub trait SignatureProtocol {
/// The signature.
type Signature: Clone + Zeroize;
/// Length of a serialized [`Signature`](Self::Signature).
type SignatureLen: ArrayLength<u8>;
type SignatureLen: ArrayLength;
/// The state required to run the verification. This is used to cache the
/// pre-hash for curves that support that, otherwise the [`Message`] to
/// verify is stored via [`CachedMessage`].
@@ -111,7 +111,7 @@ pub trait SignatureProtocol {
/// The returned [`VerifyState`](Self::VerifyState) will be passed to
/// [`verify()`](Self::verify) and must contain the necessary
/// information to verify the incoming signature.
fn sign<R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
fn sign<R: CryptoRng + Rng, CS: CipherSuite, KE: Group>(
sk: &<Self::Group as Group>::Sk,
rng: &mut R,
message: &Message<CS, KE>,
@@ -202,8 +202,9 @@ pub struct Ke2State<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> {
pub struct Ke2Message<SIG: SignatureProtocol, KE: Group, KEH: Hash>
where
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<KEH>: ArrayLength,
{
server_nonce: GenericArray<u8, NonceLen>,
#[derive_where(skip(Zeroize))]
@@ -223,37 +224,42 @@ where
)]
#[derive_where(Clone, ZeroizeOnDrop)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; SIG::Signature)]
pub struct Ke3Message<SIG: SignatureProtocol, KEH: OutputSizeUser> {
pub struct Ke3Message<SIG: SignatureProtocol, KEH: OutputSizeUser>
where
<KEH as OutputSizeUser>::OutputSize: ArrayLength,
{
signature: SIG::Signature,
mac: Output<KEH>,
}
impl<SIG: SignatureProtocol, KE: 'static + Group, KEH: Hash> KeyExchange for SigmaI<SIG, KE, KEH>
impl<SIG: SignatureProtocol, KE: 'static + Group, KEH: Hash + BlockSizeUser> KeyExchange
for SigmaI<SIG, KE, KEH>
where
KE::Sk: DiffieHellman<KE>,
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<KEH>: ArrayLength,
{
type Group = SIG::Group;
type Hash = KEH;
type KE1State = Ke1State<KE>;
type KE2State<CS: CipherSuite> = Ke2State<CS, SIG, KE>;
type KE1Message = Ke1Message<KE>;
type KE2Builder<'a, CS: CipherSuite<KeyExchange = Self>> = Ke2Builder<'a, CS, KE>;
type KE2BuilderData<'a, CS: 'static + CipherSuite> = &'a Message<'a, CS, KE>;
type KE2BuilderInput<CS: CipherSuite> = (SIG::Signature, SIG::VerifyState<CS, KE>);
type KE2State<CS: CipherSuite> = Ke2State<CS, SIG, KE>;
type KE2Message = Ke2Message<SIG, KE, KEH>;
type KE3Message = Ke3Message<SIG, KEH>;
fn generate_ke1<R: RngCore + CryptoRng>(
fn generate_ke1<R: Rng + CryptoRng>(
rng: &mut R,
) -> Result<GenerateKe1Result<Self>, ProtocolError> {
generate_ke1(rng)
}
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: Rng + CryptoRng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -287,13 +293,13 @@ where
&transcript_hasher.finalize(),
)?;
let mut server_mac =
Hmac::<KEH>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
let mut server_mac = SimpleHmac::<KEH>::new_from_slice(&derived_keys.km2)
.map_err(|_| InternalError::HmacError)?;
server_mac.update_iter(identifiers.server.iter());
let server_mac = server_mac.finalize().into_bytes();
let mut client_mac =
Hmac::<KEH>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
let mut client_mac = SimpleHmac::<KEH>::new_from_slice(&derived_keys.km3)
.map_err(|_| InternalError::HmacError)?;
client_mac.update_iter(identifiers.client.iter());
let client_mac = client_mac.finalize().into_bytes();
@@ -331,7 +337,7 @@ where
&builder.transcript
}
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
builder: &Self::KE2Builder<'_, CS>,
rng: &mut R,
server_s_sk: &PrivateKey<Self::Group>,
@@ -363,7 +369,7 @@ where
})
}
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -397,8 +403,8 @@ where
&transcript_hasher.finalize(),
)?;
let mut server_mac =
Hmac::<KEH>::new_from_slice(&derived_keys.km2).map_err(|_| InternalError::HmacError)?;
let mut server_mac = SimpleHmac::<KEH>::new_from_slice(&derived_keys.km2)
.map_err(|_| InternalError::HmacError)?;
server_mac.update_iter(identifiers.server.iter());
let server_mac = server_mac.finalize().into_bytes();
@@ -406,8 +412,8 @@ where
.then_some(())
.ok_or(ProtocolError::InvalidLoginError)?;
let mut client_mac =
Hmac::<KEH>::new_from_slice(&derived_keys.km3).map_err(|_| InternalError::HmacError)?;
let mut client_mac = SimpleHmac::<KEH>::new_from_slice(&derived_keys.km3)
.map_err(|_| InternalError::HmacError)?;
client_mac.update_iter(identifiers.client.iter());
let client_mac = client_mac.finalize().into_bytes();
@@ -481,13 +487,14 @@ where
impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> Deserialize for Ke2State<CS, SIG, KE>
where
SIG::VerifyState<CS, KE>: Deserialize,
OutputSize<KeHash<CS>>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
client_s_pk: PublicKey::deserialize_take(input)?,
session_key: input.take_array("session key")?,
verify_state: SIG::VerifyState::deserialize_take(input)?,
expected_mac: input.take_array("expected mac")?,
session_key: input.take_array("session key")?.into_ha0_4(),
verify_state: SIG::VerifyState::<CS, KE>::deserialize_take(input)?,
expected_mac: input.take_array("expected mac")?.into_ha0_4(),
})
}
}
@@ -502,37 +509,44 @@ type VerifyStateLen<CS, SIG: SignatureProtocol, KE> = <SIG::VerifyState<CS, KE>
impl<CS: CipherSuite, SIG: SignatureProtocol, KE: Group> Serialize for Ke2State<CS, SIG, KE>
where
SIG::VerifyState<CS, KE>: Serialize,
OutputSize<KeHash<CS>>: ArrayLength,
// Ke2State: ((SigPk + Hash) + VerifyState) + Hash
<SIG::Group as Group>::PkLen: Add<OutputSize<KeHash<CS>>>,
Sum<<SIG::Group as Group>::PkLen, OutputSize<KeHash<CS>>>:
ArrayLength<u8> + Add<VerifyStateLen<CS, SIG, KE>>,
ArrayLength + Add<VerifyStateLen<CS, SIG, KE>>,
Sum<Sum<<SIG::Group as Group>::PkLen, OutputSize<KeHash<CS>>>, VerifyStateLen<CS, SIG, KE>>:
ArrayLength<u8> + Add<OutputSize<KeHash<CS>>>,
Ke2StateLen<CS, SIG, KE>: ArrayLength<u8>,
ArrayLength + Add<OutputSize<KeHash<CS>>>,
Ke2StateLen<CS, SIG, KE>: ArrayLength,
{
type Len = Ke2StateLen<CS, SIG, KE>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.client_s_pk
.serialize()
.concat(self.session_key.clone())
.concat(self.verify_state.serialize())
.concat(self.expected_mac.clone())
Concat::concat(
Concat::concat(
Concat::concat(
self.client_s_pk.serialize(),
GenericArray::from_slice(self.session_key.as_slice()).clone(),
),
self.verify_state.serialize(),
),
GenericArray::from_slice(self.expected_mac.as_slice()).clone(),
)
}
}
impl<SIG: SignatureProtocol, KE: Group, KEH: Hash> Deserialize for Ke2Message<SIG, KE, KEH>
where
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<KEH>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
server_nonce: input.take_array("server nonce")?,
server_e_pk: PublicKey::deserialize_take(input)?,
signature: SIG::deserialize_take_signature(input)?,
mac: input.take_array("mac")?,
mac: input.take_array("mac")?.into_ha0_4(),
})
}
}
@@ -540,34 +554,36 @@ where
impl<SIG: SignatureProtocol, KE: Group, KEH: Hash> Serialize for Ke2Message<SIG, KE, KEH>
where
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<KEH>: ArrayLength,
// Ke2Message: ((Nonce + KePk) + Signature) + Hash
NonceLen: Add<KE::PkLen>,
Sum<NonceLen, KE::PkLen>: ArrayLength<u8> + Add<SIG::SignatureLen>,
Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>: ArrayLength<u8> + Add<OutputSize<KEH>>,
Sum<Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>, OutputSize<KEH>>: ArrayLength<u8>,
Sum<NonceLen, KE::PkLen>: ArrayLength + Add<SIG::SignatureLen>,
Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>: ArrayLength + Add<OutputSize<KEH>>,
Sum<Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>, OutputSize<KEH>>: ArrayLength,
{
type Len = Sum<Sum<Sum<NonceLen, KE::PkLen>, SIG::SignatureLen>, OutputSize<KEH>>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.server_nonce
.concat(self.server_e_pk.serialize())
.concat(SIG::serialize_signature(&self.signature))
.concat(self.mac.clone())
.cat(self.server_e_pk.serialize())
.cat(SIG::serialize_signature(&self.signature))
.cat(GenericArray::from_slice(self.mac.as_slice()).clone())
}
}
impl<SIG: SignatureProtocol, KEH: Hash> Deserialize for Ke3Message<SIG, KEH>
where
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<KEH>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
signature: SIG::deserialize_take_signature(input)?,
mac: input.take_array("mac")?,
mac: input.take_array("mac")?.into_ha0_4(),
})
}
}
@@ -575,15 +591,19 @@ where
impl<SIG: SignatureProtocol, KEH: Hash> Serialize for Ke3Message<SIG, KEH>
where
KEH::Core: ProxyHash,
<KEH::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<KEH::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<KEH as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<KEH>: ArrayLength,
// Ke2Message: Signature + Hash
SIG::SignatureLen: Add<OutputSize<KEH>>,
Sum<SIG::SignatureLen, OutputSize<KEH>>: ArrayLength<u8>,
Sum<SIG::SignatureLen, OutputSize<KEH>>: ArrayLength,
{
type Len = Sum<SIG::SignatureLen, OutputSize<KEH>>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
SIG::serialize_signature(&self.signature).concat(self.mac.clone())
Concat::concat(
SIG::serialize_signature(&self.signature),
GenericArray::from_slice(self.mac.as_slice()).clone(),
)
}
}
+7 -7
View File
@@ -12,7 +12,7 @@
use core::marker::PhantomData;
use generic_array::GenericArray;
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use zeroize::Zeroize;
use self::implementation::PureEddsaImpl;
@@ -34,7 +34,7 @@ impl<G: PureEddsaImpl> SignatureProtocol for PureEddsa<G> {
type SignatureLen = G::SignatureLen;
type VerifyState<CS: CipherSuite, KE: Group> = CachedMessage<CS, KE>;
fn sign<'a, R: CryptoRng + RngCore, CS: CipherSuite, KE: Group>(
fn sign<'a, R: CryptoRng + Rng, CS: CipherSuite, KE: Group>(
sk: &G::Sk,
_: &mut R,
message: &Message<CS, KE>,
@@ -51,13 +51,13 @@ impl<G: PureEddsaImpl> SignatureProtocol for PureEddsa<G> {
G::verify(pk, message_builder, state, signature)
}
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
G::deserialize_take_signature(bytes)
}
fn serialize_signature(signature: &Self::Signature) -> GenericArray<u8, Self::SignatureLen> {
G::serialize_signature(signature)
}
fn deserialize_take_signature(bytes: &mut &[u8]) -> Result<Self::Signature, ProtocolError> {
G::deserialize_take_signature(bytes)
}
}
pub(in super::super) mod implementation {
@@ -67,7 +67,7 @@ pub(in super::super) mod implementation {
pub trait PureEddsaImpl: Group {
type Signature: Clone + Zeroize;
type SignatureLen: ArrayLength<u8>;
type SignatureLen: ArrayLength;
fn sign<CS: CipherSuite, KE: Group>(
sk: &Self::Sk,
+11 -5
View File
@@ -16,24 +16,30 @@ use crate::serialization::SliceExt;
/// Pre-hash of the message to be verified.
#[derive_where(Clone, Debug, Eq, Hash, PartialEq, Zeroize)]
#[derive_where(Copy; <H::OutputSize as ArrayLength<u8>>::ArrayType)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(bound = "")
)]
#[allow(dead_code)]
pub struct PreHash<H: OutputSizeUser>(pub Output<H>);
impl<H: OutputSizeUser> Deserialize for PreHash<H> {
impl<H: OutputSizeUser> Deserialize for PreHash<H>
where
H::OutputSize: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self(input.take_array("pre-hash")?))
Ok(Self(input.take_array("pre-hash")?.into_ha0_4()))
}
}
impl<H: OutputSizeUser> Serialize for PreHash<H> {
impl<H: OutputSizeUser> Serialize for PreHash<H>
where
H::OutputSize: ArrayLength,
{
type Len = H::OutputSize;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.0.clone()
GenericArray::from_slice(self.0.as_slice()).clone()
}
}
+62 -47
View File
@@ -12,12 +12,11 @@ use core::marker::PhantomData;
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 digest::block_api::{CoreProxy, SmallBlockSizeUser};
use digest::{Output, OutputSizeUser};
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use subtle::{ConstantTimeEq, CtOption};
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -34,7 +33,7 @@ use crate::key_exchange::shared::{self, NonceLen};
pub use crate::key_exchange::shared::{DiffieHellman, Ke1Message, Ke1State};
use crate::keypair::{PrivateKey, PublicKey};
use crate::opaque::Identifiers;
use crate::serialization::SliceExt;
use crate::serialization::{ConcatExt, SliceExt};
////////////////////////////
// High-level API Structs //
@@ -79,8 +78,9 @@ pub struct Ke2State<H: OutputSizeUser> {
pub struct Ke2Builder<G: Group, H: Hash>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
server_nonce: GenericArray<u8, NonceLen>,
transcript_hasher: H,
@@ -104,8 +104,9 @@ where
pub struct Ke2Message<G: Group, H: Hash>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
pub(super) server_nonce: GenericArray<u8, NonceLen>,
#[derive_where(skip(Zeroize))]
@@ -123,8 +124,9 @@ where
pub struct Ke3Message<H: Hash>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
pub(super) mac: Output<H>,
}
@@ -138,8 +140,9 @@ impl<G: Group + 'static, H: Hash> KeyExchange for TripleDh<G, H>
where
G::Sk: DiffieHellman<G>,
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
type Group = G;
type Hash = H;
@@ -153,13 +156,13 @@ where
type KE2Message = Ke2Message<G, H>;
type KE3Message = Ke3Message<H>;
fn generate_ke1<R: RngCore + CryptoRng>(
fn generate_ke1<R: Rng + CryptoRng>(
rng: &mut R,
) -> Result<GenerateKe1Result<Self>, ProtocolError> {
shared::generate_ke1(rng)
}
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: Rng + CryptoRng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -201,7 +204,7 @@ where
&builder.client_e_pk
}
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
builder: &Self::KE2Builder<'_, CS>,
_: &mut R,
server_s_sk: &PrivateKey<G>,
@@ -247,7 +250,7 @@ where
})
}
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
_: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -319,13 +322,14 @@ where
impl<H: Hash> Deserialize for Ke2State<H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
session_key: input.take_array("session key")?,
expected_mac: input.take_array("expected mac")?,
session_key: input.take_array("session key")?.into_ha0_4(),
expected_mac: input.take_array("expected mac")?.into_ha0_4(),
})
}
}
@@ -333,26 +337,32 @@ where
impl<H: Hash> Serialize for Ke2State<H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
// Ke2State: Hash + Hash
OutputSize<H>: Add<OutputSize<H>>,
Sum<OutputSize<H>, OutputSize<H>>: ArrayLength<u8>,
Sum<OutputSize<H>, OutputSize<H>>: ArrayLength,
{
type Len = Sum<OutputSize<H>, OutputSize<H>>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.session_key.clone().concat(self.expected_mac.clone())
let sk: GenericArray<u8, OutputSize<H>> =
GenericArray::from_slice(self.session_key.as_slice()).clone();
let mac: GenericArray<u8, OutputSize<H>> =
GenericArray::from_slice(self.expected_mac.as_slice()).clone();
sk.cat(mac)
}
}
/// TODO: implement via derive after hash crates get `Zeroize` support in
/// `digest` v11.
/// TODO: implement via derive after `Hash` gets `Zeroize` support.
impl<G: Group, H: Hash> Drop for Ke2Builder<G, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
fn drop(&mut self) {
let Self {
@@ -365,7 +375,7 @@ where
} = self;
server_nonce.zeroize();
transcript_hasher.reset();
digest::Reset::reset(transcript_hasher);
shared_secret_1.zeroize();
shared_secret_3.zeroize();
}
@@ -374,22 +384,24 @@ where
impl<G: Group, H: Hash> ZeroizeOnDrop for Ke2Builder<G, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
}
impl<G: Group, H: Hash> Deserialize for Ke2Message<G, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
server_nonce: input.take_array("server nonce")?,
server_e_pk: PublicKey::deserialize_take(input)?,
mac: input.take_array("mac")?,
mac: input.take_array("mac")?.into_ha0_4(),
})
}
}
@@ -397,31 +409,33 @@ where
impl<H: Hash, G: Group> Serialize for Ke2Message<G, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
// Ke2Message: (Nonce + KePk) + Hash
NonceLen: Add<G::PkLen>,
Sum<NonceLen, G::PkLen>: ArrayLength<u8> + Add<OutputSize<H>>,
Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>: ArrayLength<u8>,
Sum<NonceLen, G::PkLen>: ArrayLength + Add<OutputSize<H>>,
Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>: ArrayLength,
{
type Len = Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.server_nonce
.concat(self.server_e_pk.serialize())
.concat(self.mac.clone())
.cat(self.server_e_pk.serialize())
.cat(GenericArray::from_slice(self.mac.as_slice()).clone())
}
}
impl<H: Hash> Deserialize for Ke3Message<H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
mac: bytes.take_array("mac")?,
mac: bytes.take_array("mac")?.into_ha0_4(),
})
}
}
@@ -429,12 +443,13 @@ where
impl<H: Hash> Serialize for Ke3Message<H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
OutputSize<H>: ArrayLength,
{
type Len = OutputSize<H>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.mac.clone()
GenericArray::from_slice(self.mac.as_slice()).clone()
}
}
+96 -69
View File
@@ -23,18 +23,18 @@ use core::marker::PhantomData;
use core::ops::Add;
use derive_where::derive_where;
use digest::core_api::BlockSizeUser;
use digest::{Digest, Output};
use generic_array::sequence::Concat;
use generic_array::typenum::{IsLess, Le, NonZero, Sum, U256};
use digest::Output;
use digest::block_api::{CoreProxy, SmallBlockSizeUser};
use generic_array::typenum::{Cmp, IsLess, Le, NonZero, Sum, U256};
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::ArraySize;
#[allow(deprecated)]
use ml_kem::ExpandedKeyEncoding;
use ml_kem::kem::{
Ciphertext as MlKemCiphertext, Decapsulate, Encapsulate, Kem as MlKemTrait, KeyExport,
KeySizeUser, TryKeyInit,
};
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use subtle::{ConstantTimeEq, CtOption};
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -50,7 +50,7 @@ use crate::hash::{Hash, OutputSize, ProxyHash};
use crate::key_exchange::group::Group;
use crate::keypair::{PrivateKey, PublicKey};
use crate::opaque::Identifiers;
use crate::serialization::SliceExt;
use crate::serialization::{ConcatExt, SliceExt};
/// Adapter trait that augments the `ml-kem` core traits with the metadata
/// required by OPAQUE (e.g. fixed lengths and serialization hooks).
@@ -62,16 +62,16 @@ pub trait KemCoreWrapper {
type DecapsulationKey: Clone + ZeroizeOnDrop;
/// Length (in bytes) of the serialized public key.
type EncapsulationKeyLen: ArrayLength<u8>;
type EncapsulationKeyLen: ArrayLength + ArraySize;
/// Length (in bytes) of the serialized secret key.
type DecapsulationKeyLen: ArrayLength<u8>;
type DecapsulationKeyLen: ArrayLength + ArraySize;
/// Length (in bytes) of the encapsulated ciphertext.
type CiphertextLen: ArrayLength<u8>;
type CiphertextLen: ArrayLength + ArraySize;
/// Length (in bytes) of the shared secret output by the KEM.
type SharedSecretLen: ArrayLength<u8>;
type SharedSecretLen: ArrayLength + ArraySize;
/// Generates a fresh KEM key pair.
fn generate<R: RngCore + CryptoRng>(
fn generate<R: Rng + CryptoRng>(
rng: &mut R,
) -> Result<(Self::DecapsulationKey, Self::EncapsulationKey), ProtocolError>;
@@ -98,7 +98,7 @@ pub trait KemCoreWrapper {
/// Encapsulates to the given public key, returning the ciphertext and
/// shared secret.
#[allow(clippy::type_complexity)]
fn encapsulate<R: RngCore + CryptoRng>(
fn encapsulate<R: Rng + CryptoRng>(
key: &Self::EncapsulationKey,
rng: &mut R,
) -> Result<
@@ -120,7 +120,7 @@ pub trait KemCoreWrapper {
/// which is required by `ml-kem 0.3.x`.
struct RngCompat<'a, R>(&'a mut R);
impl<R: RngCore> rand_core_10::TryRng for RngCompat<'_, R> {
impl<R: Rng> rand_core::TryRng for RngCompat<'_, R> {
type Error = core::convert::Infallible;
fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
@@ -137,7 +137,7 @@ impl<R: RngCore> rand_core_10::TryRng for RngCompat<'_, R> {
}
}
impl<R: RngCore + CryptoRng> rand_core_10::TryCryptoRng for RngCompat<'_, R> {}
impl<R: Rng + CryptoRng> rand_core::TryCryptoRng for RngCompat<'_, R> {}
type RcEncapsulationKeyLen<K> = <<K as MlKemTrait>::EncapsulationKey as KeySizeUser>::KeySize;
#[allow(deprecated)]
@@ -152,10 +152,10 @@ where
K: MlKemTrait,
K::EncapsulationKey: Encapsulate<Kem = K> + KeyExport + TryKeyInit + Clone,
K::DecapsulationKey: Decapsulate<Kem = K> + ExpandedKeyEncoding + Clone + ZeroizeOnDrop,
RcEncapsulationKeyLen<K>: ArrayLength<u8>,
RcDecapsulationKeyLen<K>: ArrayLength<u8>,
RcCiphertextLen<K>: ArrayLength<u8>,
RcSharedSecretLen<K>: ArrayLength<u8>,
RcEncapsulationKeyLen<K>: ArrayLength + ArraySize,
RcDecapsulationKeyLen<K>: ArrayLength + ArraySize,
RcCiphertextLen<K>: ArrayLength + ArraySize,
RcSharedSecretLen<K>: ArrayLength + ArraySize,
{
type EncapsulationKey = K::EncapsulationKey;
type DecapsulationKey = K::DecapsulationKey;
@@ -164,7 +164,7 @@ where
type CiphertextLen = RcCiphertextLen<K>;
type SharedSecretLen = RcSharedSecretLen<K>;
fn generate<R: RngCore + CryptoRng>(
fn generate<R: Rng + CryptoRng>(
rng: &mut R,
) -> Result<(Self::DecapsulationKey, Self::EncapsulationKey), ProtocolError> {
Ok(K::generate_keypair_from_rng(&mut RngCompat(rng)))
@@ -173,7 +173,7 @@ where
fn serialize_encapsulation_key(
key: &Self::EncapsulationKey,
) -> GenericArray<u8, Self::EncapsulationKeyLen> {
GenericArray::clone_from_slice(key.to_bytes().as_slice())
GenericArray::from_slice(key.to_bytes().as_slice()).clone()
}
fn deserialize_encapsulation_key(
@@ -189,7 +189,7 @@ where
fn serialize_decapsulation_key(
key: &Self::DecapsulationKey,
) -> GenericArray<u8, Self::DecapsulationKeyLen> {
GenericArray::clone_from_slice(key.to_expanded_bytes().as_slice())
GenericArray::from_slice(key.to_expanded_bytes().as_slice()).clone()
}
fn deserialize_decapsulation_key(
@@ -203,7 +203,7 @@ where
.map_err(|_| ProtocolError::SerializationError)
}
fn encapsulate<R: RngCore + CryptoRng>(
fn encapsulate<R: Rng + CryptoRng>(
key: &Self::EncapsulationKey,
rng: &mut R,
) -> Result<
@@ -215,8 +215,8 @@ where
> {
let (ciphertext, shared) = key.encapsulate_with_rng(&mut RngCompat(rng));
Ok((
GenericArray::clone_from_slice(ciphertext.as_slice()),
GenericArray::clone_from_slice(shared.as_slice()),
GenericArray::from_slice(ciphertext.as_slice()).clone(),
GenericArray::from_slice(shared.as_slice()).clone(),
))
}
@@ -227,7 +227,7 @@ where
let ciphertext = MlKemCiphertext::<K>::try_from(encapsulated_key.as_slice())
.map_err(|_| ProtocolError::SerializationError)?;
let shared = key.decapsulate(&ciphertext);
Ok(GenericArray::clone_from_slice(shared.as_slice()))
Ok(GenericArray::from_slice(shared.as_slice()).clone())
}
}
/// Triple Diffie-Hellman-style key exchange that offloads the second hop to a
@@ -281,8 +281,10 @@ pub struct KemKe1Message<G: Group, K: KemCoreWrapper> {
pub struct KemKe2State<K: KemCoreWrapper, H: Hash>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
{
base_state: super::tripledh::Ke2State<H>,
kem_encapsulation_key: GenericArray<u8, K::EncapsulationKeyLen>,
@@ -295,8 +297,10 @@ where
pub struct KemKe2Builder<G: Group, H: Hash, K: KemCoreWrapper>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
{
server_nonce: GenericArray<u8, NonceLen>,
transcript_hasher: H,
@@ -323,8 +327,10 @@ where
pub struct KemKe2Message<G: Group, H: Hash, K: KemCoreWrapper>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
{
dh_message: super::tripledh::Ke2Message<G, H>,
kem_ciphertext: GenericArray<u8, K::CiphertextLen>,
@@ -338,13 +344,15 @@ where
G: Group,
H: Hash,
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
K: KemCoreWrapper,
{
fn drop(&mut self) {
self.server_nonce.zeroize();
self.transcript_hasher.reset();
digest::Digest::reset(&mut self.transcript_hasher);
self.shared_secret_1.zeroize();
self.shared_secret_3.zeroize();
self.kem_shared_secret.zeroize();
@@ -357,8 +365,10 @@ where
G: Group,
H: Hash,
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
K: KemCoreWrapper,
{
}
@@ -369,11 +379,13 @@ where
G::Sk: shared::DiffieHellman<G>,
H: Hash,
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
K: KemCoreWrapper,
NonceLen: Add<K::EncapsulationKeyLen>,
Sum<NonceLen, K::EncapsulationKeyLen>: ArrayLength<u8>,
Sum<NonceLen, K::EncapsulationKeyLen>: ArrayLength,
{
type Group = G;
type Hash = H;
@@ -390,7 +402,7 @@ where
type KE2Message = KemKe2Message<G, H, K>;
type KE3Message = KemKe3Message<H>;
fn generate_ke1<R: RngCore + CryptoRng>(
fn generate_ke1<R: Rng + CryptoRng>(
rng: &mut R,
) -> Result<GenerateKe1Result<Self>, ProtocolError> {
let base = super::tripledh::TripleDh::<G, H>::generate_ke1(rng)?;
@@ -409,7 +421,7 @@ where
})
}
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: RngCore + CryptoRng>(
fn ke2_builder<'a, CS: CipherSuite<KeyExchange = Self>, R: Rng + CryptoRng>(
rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -440,8 +452,11 @@ where
let (kem_ciphertext, kem_shared_secret) = K::encapsulate(&encapsulation_key, rng)?;
let mut transcript_hasher = transcript_hasher;
transcript_hasher.update(ke1_message.kem_encapsulation_key.as_slice());
transcript_hasher.update(kem_ciphertext.as_slice());
digest::Digest::update(
&mut transcript_hasher,
ke1_message.kem_encapsulation_key.as_slice(),
);
digest::Digest::update(&mut transcript_hasher, kem_ciphertext.as_slice());
Ok(KemKe2Builder {
server_nonce,
@@ -462,7 +477,7 @@ where
(&builder.client_e_pk, &builder.kem_encapsulation_key)
}
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke2_input<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
builder: &Self::KE2Builder<'_, CS>,
_: &mut R,
server_s_sk: &PrivateKey<G>,
@@ -516,7 +531,7 @@ where
})
}
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + RngCore>(
fn generate_ke3<CS: CipherSuite<KeyExchange = Self>, R: CryptoRng + Rng>(
_rng: &mut R,
credential_request: SerializedCredentialRequest<CS>,
ke1_message: Self::KE1Message,
@@ -537,8 +552,14 @@ where
ke2_message.dh_message.server_nonce,
&ke2_message.dh_message.server_e_pk.serialize(),
);
transcript_hasher.update(ke1_message.kem_encapsulation_key.as_slice());
transcript_hasher.update(ke2_message.kem_ciphertext.as_slice());
digest::Digest::update(
&mut transcript_hasher,
ke1_message.kem_encapsulation_key.as_slice(),
);
digest::Digest::update(
&mut transcript_hasher,
ke2_message.kem_ciphertext.as_slice(),
);
let shared_secret_1 = ke1_state
.dh_state
@@ -606,14 +627,14 @@ impl<G: Group, K: KemCoreWrapper> Serialize for KemKe1State<G, K>
where
Ke1State<G>: Serialize,
<Ke1State<G> as Serialize>::Len: Add<K::DecapsulationKeyLen>,
Sum<<Ke1State<G> as Serialize>::Len, K::DecapsulationKeyLen>: ArrayLength<u8>,
Sum<<Ke1State<G> as Serialize>::Len, K::DecapsulationKeyLen>: ArrayLength,
{
type Len = Sum<<Ke1State<G> as Serialize>::Len, K::DecapsulationKeyLen>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.dh_state
.serialize()
.concat(K::serialize_decapsulation_key(&self.kem_decapsulation_key))
.cat(K::serialize_decapsulation_key(&self.kem_decapsulation_key))
}
}
@@ -630,22 +651,24 @@ impl<G: Group, K: KemCoreWrapper> Serialize for KemKe1Message<G, K>
where
Ke1Message<G>: Serialize,
<Ke1Message<G> as Serialize>::Len: Add<K::EncapsulationKeyLen>,
Sum<<Ke1Message<G> as Serialize>::Len, K::EncapsulationKeyLen>: ArrayLength<u8>,
Sum<<Ke1Message<G> as Serialize>::Len, K::EncapsulationKeyLen>: ArrayLength,
{
type Len = Sum<<Ke1Message<G> as Serialize>::Len, K::EncapsulationKeyLen>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.dh_message
.serialize()
.concat(self.kem_encapsulation_key.clone())
.cat(self.kem_encapsulation_key.clone())
}
}
impl<K: KemCoreWrapper, H: Hash> Deserialize for KemKe2State<K, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
@@ -659,16 +682,18 @@ where
impl<K: KemCoreWrapper, H: Hash> Serialize for KemKe2State<K, H>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
super::tripledh::Ke2State<H>: Serialize,
<super::tripledh::Ke2State<H> as Serialize>::Len: Add<K::EncapsulationKeyLen>,
Sum<<super::tripledh::Ke2State<H> as Serialize>::Len, K::EncapsulationKeyLen>:
ArrayLength<u8> + Add<K::CiphertextLen>,
ArrayLength + Add<K::CiphertextLen>,
Sum<
Sum<<super::tripledh::Ke2State<H> as Serialize>::Len, K::EncapsulationKeyLen>,
K::CiphertextLen,
>: ArrayLength<u8>,
>: ArrayLength,
{
type Len = Sum<
Sum<<super::tripledh::Ke2State<H> as Serialize>::Len, K::EncapsulationKeyLen>,
@@ -678,16 +703,18 @@ where
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.base_state
.serialize()
.concat(self.kem_encapsulation_key.clone())
.concat(self.server_kem_ciphertext.clone())
.cat(self.kem_encapsulation_key.clone())
.cat(self.server_kem_ciphertext.clone())
}
}
impl<G: Group, H: Hash, K: KemCoreWrapper> Deserialize for KemKe2Message<G, H, K>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
{
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
@@ -700,21 +727,21 @@ where
impl<G: Group, H: Hash, K: KemCoreWrapper> Serialize for KemKe2Message<G, H, K>
where
H::Core: ProxyHash,
<H::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
Le<<H::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: IsLess<U256>,
Le<<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize, U256>: NonZero,
<<H as CoreProxy>::Core as SmallBlockSizeUser>::_BlockSize: Cmp<U256>,
OutputSize<H>: ArrayLength,
NonceLen: Add<G::PkLen>,
Sum<NonceLen, G::PkLen>: ArrayLength<u8> + Add<OutputSize<H>>,
Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>: ArrayLength<u8>,
Sum<NonceLen, G::PkLen>: ArrayLength + Add<OutputSize<H>>,
Sum<Sum<NonceLen, G::PkLen>, OutputSize<H>>: ArrayLength,
super::tripledh::Ke2Message<G, H>: Serialize,
<super::tripledh::Ke2Message<G, H> as Serialize>::Len: Add<K::CiphertextLen>,
<<super::tripledh::Ke2Message<G, H> as Serialize>::Len as Add<K::CiphertextLen>>::Output:
ArrayLength<u8>,
ArrayLength,
{
type Len = Sum<<super::tripledh::Ke2Message<G, H> as Serialize>::Len, K::CiphertextLen>;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.dh_message
.serialize()
.concat(self.kem_ciphertext.clone())
self.dh_message.serialize().cat(self.kem_ciphertext.clone())
}
}
+43 -35
View File
@@ -13,7 +13,7 @@
use derive_where::derive_where;
use digest::{Output, OutputSizeUser};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use rand::{CryptoRng, Rng};
use crate::ciphersuite::CipherSuite;
use crate::errors::ProtocolError;
@@ -32,11 +32,7 @@ use crate::serialization::SliceExt;
))
)]
#[derive_where(Clone)]
#[derive_where(Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk, SK)]
// `NonZeroScalar` doesn't implement `Debug`.
// TODO: remove after `elliptic-curve` bump to v0.14.
#[cfg_attr(not(test), derive_where(Debug; G::Pk, SK))]
#[cfg_attr(test, derive_where(Debug), derive_where(skip_inner(Debug)))]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; G::Pk, SK)]
pub struct KeyPair<G: Group, SK: Clone = PrivateKey<G>> {
pk: PublicKey<G>,
sk: SK,
@@ -60,7 +56,7 @@ impl<G: Group, SK: Clone> KeyPair<G, SK> {
}
impl<G: Group> KeyPair<G> {
pub(crate) fn random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
pub(crate) fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
let sk = G::random_sk(rng);
let pk = G::public_key(&sk);
Self {
@@ -70,7 +66,7 @@ impl<G: Group> KeyPair<G> {
}
/// Generating a random key pair given a cryptographic rng
pub(crate) fn derive_random<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
pub(crate) fn derive_random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
let mut scalar_bytes = GenericArray::<_, <G as Group>::SkLen>::default();
rng.fill_bytes(&mut scalar_bytes);
let sk = G::derive_scalar(scalar_bytes).unwrap();
@@ -133,7 +129,7 @@ where
impl<G: Group> PrivateKey<G> {
/// Private-key signing implementation
pub(crate) fn sign<
R: CryptoRng + RngCore,
R: CryptoRng + Rng,
CS: CipherSuite,
SIG: SignatureProtocol<Group = G>,
KE: Group,
@@ -152,7 +148,7 @@ pub trait PrivateKeySerialization<G: Group>: Clone {
/// Custom error type that can be passed down to `ProtocolError::Custom`
type Error;
/// Serialization size in bytes.
type Len: ArrayLength<u8>;
type Len: ArrayLength;
/// Serialization into bytes
fn serialize_key_pair(key_pair: &KeyPair<G, Self>) -> GenericArray<u8, Self::Len>;
@@ -242,7 +238,7 @@ pub struct OprfSeed<H: OutputSizeUser>(pub(crate) Output<H>);
/// Will be called with `E` being [`PrivateKeySerialization::Error`].
pub trait OprfSeedSerialization<H, E>: Sized {
/// Serialization size in bytes.
type Len: ArrayLength<u8>;
type Len: ArrayLength;
/// Serialization into bytes
fn serialize(&self) -> GenericArray<u8, Self::Len>;
@@ -253,18 +249,22 @@ pub trait OprfSeedSerialization<H, E>: Sized {
fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError<E>>;
}
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H> {
impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H>
where
H::OutputSize: ArrayLength,
{
type Len = H::OutputSize;
fn serialize(&self) -> GenericArray<u8, Self::Len> {
self.0.clone()
GenericArray::from_slice(self.0.as_slice()).clone()
}
fn deserialize_take(input: &mut &[u8]) -> Result<Self, ProtocolError<E>> {
Ok(Self(
input
.take_array("OPRF seed")
.map_err(ProtocolError::into_custom)?,
.map_err(ProtocolError::into_custom)?
.into_ha0_4(),
))
}
}
@@ -275,7 +275,11 @@ impl<H: OutputSizeUser, E> OprfSeedSerialization<H, E> for OprfSeed<H> {
//////////////////////////
#[cfg(test)]
impl<G: Group> KeyPair<G> {
impl<G: Group> KeyPair<G>
where
G::Pk: core::fmt::Debug,
G::Sk: core::fmt::Debug,
{
/// Test-only strategy returning a proptest Strategy based on
/// [`Self::derive_random`]
fn uniform_keypair_strategy() -> proptest::prelude::BoxedStrategy<Self> {
@@ -297,9 +301,6 @@ impl<G: Group> KeyPair<G> {
#[cfg(test)]
mod tests {
use hkdf::Hkdf;
use rand::rngs::OsRng;
use super::*;
use crate::ciphersuite::{KeGroup, OprfHash};
use crate::{
@@ -309,6 +310,9 @@ mod tests {
ServerLoginParameters, ServerLoginStartResult, ServerRegistration,
ServerRegistrationStartResult, ServerSetup,
};
use hkdf::Hkdf;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
macro_rules! test {
($mod:ident, $point:ty) => {
@@ -323,7 +327,7 @@ mod tests {
fn pub_from_priv(kp in KeyPair::<$point>::uniform_keypair_strategy()) {
let pk = kp.public();
let sk = kp.private();
prop_assert_eq!(&sk.public_key(), pk);
prop_assert_eq!(sk.public_key().serialize(), pk.serialize());
}
#[test]
@@ -380,23 +384,24 @@ mod tests {
#[test]
fn remote_key() {
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut OsRng));
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut UnwrapErr(SysRng)));
let pk = sk.public_key();
let sk = RemoteKey(sk);
let keypair = KeyPair::new(sk, pk);
let server_setup =
ServerSetup::<Default, RemoteKey>::new_with_key_pair(&mut OsRng, keypair);
ServerSetup::<Default, RemoteKey>::new_with_key_pair(&mut UnwrapErr(SysRng), keypair);
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
} = ClientRegistration::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes())
.unwrap();
let ServerRegistrationStartResult { message, .. } =
ServerRegistration::start(&server_setup, message, &[]).unwrap();
let ClientRegistrationFinishResult { message, .. } = client
.finish(
&mut OsRng,
&mut UnwrapErr(SysRng),
PASSWORD.as_bytes(),
message,
ClientRegistrationFinishParameters::default(),
@@ -407,9 +412,9 @@ mod tests {
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
} = ClientLogin::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes()).unwrap();
let builder = ServerLogin::builder(
&mut OsRng,
&mut UnwrapErr(SysRng),
&server_setup,
Some(file),
message,
@@ -425,7 +430,7 @@ mod tests {
} = builder.build(shared_secret).unwrap();
let ClientLoginFinishResult { message, .. } = client
.finish(
&mut OsRng,
&mut UnwrapErr(SysRng),
PASSWORD.as_bytes(),
message,
ClientLoginFinishParameters::default(),
@@ -438,22 +443,25 @@ mod tests {
#[test]
fn remote_seed() {
let mut oprf_seed = RemoteSeed::<OprfHash<Default>>(GenericArray::default());
OsRng.fill_bytes(&mut oprf_seed.0);
let mut oprf_seed = RemoteSeed::<OprfHash<Default>>(GenericArray::default().into_ha0_4());
UnwrapErr(SysRng).fill_bytes(&mut oprf_seed.0);
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut OsRng));
let sk = PrivateKey(KeGroup::<Default>::random_sk(&mut UnwrapErr(SysRng)));
let pk = sk.public_key();
let sk = RemoteKey(sk);
let keypair = KeyPair::new(sk, pk);
let server_setup = ServerSetup::<Default, _, _>::new_with_key_pair_and_seed(
&mut OsRng, keypair, oprf_seed,
&mut UnwrapErr(SysRng),
keypair,
oprf_seed,
);
let ClientRegistrationStartResult {
message,
state: client,
} = ClientRegistration::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
} = ClientRegistration::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes())
.unwrap();
let km = server_setup.key_material_info(&[]);
let mut ikm = GenericArray::default();
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
@@ -464,7 +472,7 @@ mod tests {
ServerRegistration::start_with_key_material(&server_setup, ikm, message).unwrap();
let ClientRegistrationFinishResult { message, .. } = client
.finish(
&mut OsRng,
&mut UnwrapErr(SysRng),
PASSWORD.as_bytes(),
message,
ClientRegistrationFinishParameters::default(),
@@ -475,7 +483,7 @@ mod tests {
let ClientLoginStartResult {
message,
state: client,
} = ClientLogin::<Default>::start(&mut OsRng, PASSWORD.as_bytes()).unwrap();
} = ClientLogin::<Default>::start(&mut UnwrapErr(SysRng), PASSWORD.as_bytes()).unwrap();
let km = server_setup.key_material_info(&[]);
let mut ikm = GenericArray::default();
Hkdf::<OprfHash<Default>>::from_prk(&km.ikm.0)
@@ -483,7 +491,7 @@ mod tests {
.expand_multi_info(&km.info, &mut ikm)
.unwrap();
let builder = ServerLogin::builder_with_key_material(
&mut OsRng,
&mut UnwrapErr(SysRng),
&server_setup,
ikm,
Some(file),
@@ -499,7 +507,7 @@ mod tests {
} = builder.build(shared_secret).unwrap();
let ClientLoginFinishResult { message, .. } = client
.finish(
&mut OsRng,
&mut UnwrapErr(SysRng),
PASSWORD.as_bytes(),
message,
ClientLoginFinishParameters::default(),
+3 -3
View File
@@ -15,7 +15,7 @@ use crate::errors::InternalError;
/// Used for the key stretching function in OPAQUE
pub trait Ksf: Default {
/// Computes the key stretching function
fn hash<L: ArrayLength<u8>>(
fn hash<L: ArrayLength>(
&self,
input: GenericArray<u8, L>,
) -> Result<GenericArray<u8, L>, InternalError>;
@@ -26,7 +26,7 @@ pub trait Ksf: Default {
pub struct Identity;
impl Ksf for Identity {
fn hash<L: ArrayLength<u8>>(
fn hash<L: ArrayLength>(
&self,
input: GenericArray<u8, L>,
) -> Result<GenericArray<u8, L>, InternalError> {
@@ -36,7 +36,7 @@ impl Ksf for Identity {
#[cfg(feature = "argon2")]
impl Ksf for argon2::Argon2<'_> {
fn hash<L: ArrayLength<u8>>(
fn hash<L: ArrayLength>(
&self,
input: GenericArray<u8, L>,
) -> Result<GenericArray<u8, L>, InternalError> {
+242 -221
View File
@@ -27,14 +27,14 @@
//!
//! We will use the following choices in this example:
//! ```ignore
//! use opaque_ke::CipherSuite;
//! use opaque_vx::CipherSuite;
//!
//! struct Default;
//!
//! impl CipherSuite for Default {
//! type OprfCs = opaque_ke::Ristretto255;
//! type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! type Ksf = opaque_ke::ksf::Identity;
//! type OprfCs = opaque_vx::Ristretto255;
//! type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! type Ksf = opaque_vx::ksf::Identity;
//! }
//! ```
//! See [examples/simple_login.rs](https://github.com/facebook/opaque-ke/blob/main/examples/simple_login.rs)
@@ -50,26 +50,27 @@
//! To set up the protocol, the server begins by creating a `ServerSetup`
//! object:
//! ```
//! # use opaque_ke::errors::ProtocolError;
//! # use opaque_ke::CipherSuite;
//! # use opaque_ke::ServerSetup;
//! # use opaque_vx::errors::ProtocolError;
//! # use opaque_vx::CipherSuite;
//! # use opaque_vx::ServerSetup;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! use rand::RngCore;
//! use rand::rngs::OsRng;
//! use rand::Rng;
//! use rand::rngs::SysRng;
//! use rand_core::UnwrapErr;
//!
//! let mut rng = OsRng;
//! let mut rng = UnwrapErr(SysRng);
//! let server_setup = ServerSetup::<Default>::new(&mut rng);
//! # Ok::<(), ProtocolError>(())
//! ```
@@ -103,30 +104,31 @@
//! [`ClientRegistration`] which must be persisted on the client for the final
//! step of client registration.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ServerRegistration,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! use opaque_ke::ClientRegistration;
//! use rand::RngCore;
//! use rand::rngs::OsRng;
//! use opaque_vx::ClientRegistration;
//! use rand::Rng;
//! use rand::rngs::SysRng;
//! use rand_core::UnwrapErr;
//!
//! let mut client_rng = OsRng;
//! let mut client_rng = UnwrapErr(SysRng);
//! let client_registration_start_result =
//! ClientRegistration::<Default>::start(&mut client_rng, b"password")?;
//! # Ok::<(), ProtocolError>(())
@@ -140,35 +142,36 @@
//! [`ServerRegistrationStartResult`], which consists of a
//! [`RegistrationResponse`] to be returned to the client.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration,
//! # ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! use opaque_ke::ServerRegistration;
//! use opaque_vx::ServerRegistration;
//!
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! let server_registration_start_result = ServerRegistration::<Default>::start(
//! &server_setup,
@@ -188,35 +191,36 @@
//! which can be used optionally as described in the [Export Key](#export-key)
//! section.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ServerRegistration, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! use opaque_ke::ClientRegistrationFinishParameters;
//! use opaque_vx::ClientRegistrationFinishParameters;
//!
//! let client_registration_finish_result = client_registration_start_result.state.finish(
//! &mut client_rng,
@@ -236,32 +240,33 @@
//! [`ServerRegistration::serialize`] to store the password file for use during
//! the login protocol.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
@@ -287,29 +292,30 @@
//! [`CredentialRequest`] to be sent to the server, and a [`ClientLogin`] which
//! must be persisted on the client for the final step of client login.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ServerRegistration, ServerLogin, CredentialFinalization,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! use opaque_ke::ClientLogin;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! use opaque_vx::ClientLogin;
//!
//! let mut client_rng = OsRng;
//! let mut client_rng = UnwrapErr(SysRng);
//! let client_login_start_result = ClientLogin::<Default>::start(&mut client_rng, b"password")?;
//! # Ok::<(), ProtocolError>(())
//! ```
@@ -323,32 +329,33 @@
//! a [`ServerLogin`] which must be persisted on the server for the final step
//! of login.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
@@ -357,10 +364,10 @@
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! use opaque_ke::{ServerLogin, ServerLoginParameters};
//! use opaque_vx::{ServerLogin, ServerLoginParameters};
//!
//! let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
//! let mut server_rng = OsRng;
//! let mut server_rng = UnwrapErr(SysRng);
//! let server_login_start_result = ServerLogin::start(
//! &mut server_rng,
//! &server_setup,
@@ -393,32 +400,33 @@
//! [`session_key`](struct.ClientLoginFinishResult.html#structfield.session_key)
//! which will match the server's session key upon a successful login.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
@@ -433,7 +441,7 @@
//! # )?;
//! # let server_login_start_result =
//! # ServerLogin::start(&mut server_rng, &server_setup, Some(password_file), client_login_start_result.message, b"[email protected]", ServerLoginParameters::default())?;
//! use opaque_ke::ClientLoginFinishParameters;
//! use opaque_vx::ClientLoginFinishParameters;
//!
//! let client_login_finish_result = client_login_start_result.state.finish(
//! &mut client_rng,
@@ -450,32 +458,33 @@
//! to produce an output consisting of the `session_key` sequence of bytes which
//! will match the client's session key upon a successful login.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
@@ -547,32 +556,33 @@
//! registration (with the `server_s_pk` field of
//! [`ClientRegistrationFinishResult`]) matches this field during login.
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! // During registration, the client obtains a ClientRegistrationFinishResult with
@@ -644,32 +654,33 @@
//! You can access the export key from the `export_key` field of
//! [`ClientRegistrationFinishResult`] and [`ClientLoginFinishResult`].
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! // During registration...
@@ -727,20 +738,20 @@
//! the ciphersuite as follows:
//!
//! ```ignore
//! use opaque_ke::CipherSuite;
//! use opaque_vx::CipherSuite;
//!
//! struct KemSuite;
//!
//! impl CipherSuite for KemSuite {
//! type OprfCs = opaque_ke::Ristretto255;
//! type KeyExchange = opaque_ke::TripleDhKem<opaque_ke::Ristretto255, sha2::Sha512, opaque_ke::ml_kem::MlKem768>;
//! type Ksf = opaque_ke::ksf::Identity;
//! type OprfCs = opaque_vx::Ristretto255;
//! type KeyExchange = opaque_vx::TripleDhKem<opaque_vx::Ristretto255, sha2::Sha512, opaque_vx::ml_kem::MlKem768>;
//! type Ksf = opaque_vx::ksf::Identity;
//! }
//! ```
//!
//! ## Custom Identifiers
//!
//! Typically when applications use OPAQUE to authenticate a client to a server,
//! Typically, when applications use OPAQUE to authenticate a client to a server,
//! the client has a registered username which is sent to the server to identify
//! the corresponding password file established during registration. This
//! username may or may not coincide with the server-side identifier; however,
@@ -756,32 +767,33 @@
//! [`ClientRegistrationFinishParameters`] in [Client Registration
//! Finish](#client-registration-finish):
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, Identifiers, ServerRegistration, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! let client_registration_finish_result = client_registration_start_result.state.finish(
@@ -802,32 +814,33 @@
//! The same identifiers must also be supplied using [`ServerLoginParameters`]
//! in [Server Login Start](#server-login-start):
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, CredentialFinalization, Identifiers, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
@@ -836,9 +849,9 @@
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # use opaque_ke::{ServerLogin, ServerLoginParameters};
//! # use opaque_vx::{ServerLogin, ServerLoginParameters};
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! let server_login_start_result = ServerLogin::start(
//! &mut server_rng,
//! &server_setup,
@@ -859,32 +872,33 @@
//! as well as [`ClientLoginFinishParameters`] in [Client Login
//! Finish](#client-login-finish):
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
@@ -918,32 +932,33 @@
//! and in [`ServerLoginParameters`] in [Server Login
//! Finish](#server-login-finish):
//! ```
//! # use opaque_ke::{
//! # use opaque_vx::{
//! # errors::ProtocolError,
//! # ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, ClientLogin, ClientLoginFinishParameters, Identifiers, ServerLogin, ServerLoginParameters, CredentialFinalization, ServerSetup,
//! # ksf::Identity,
//! # };
//! # use opaque_ke::CipherSuite;
//! # use opaque_vx::CipherSuite;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # use rand::{rngs::OsRng, RngCore};
//! # let mut client_rng = OsRng;
//! # use rand::{rngs::SysRng, Rng};
//! # use rand_core::UnwrapErr;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut client_rng,
//! # b"password",
//! # )?;
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<Default>::new(&mut server_rng);
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut client_rng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::new(Identifiers { client: Some(b"Alice_the_Cryptographer"), server: Some(b"Facebook") }, None))?;
@@ -981,7 +996,7 @@
//!
//! A key exchange protocol typically allows for the specifying of shared
//! "context" information between the two parties before the exchange is
//! complete, so as to bind the integrity of application-specific data or
//! complete, to bind the integrity of application-specific data or
//! configuration parameters to the security of the key exchange. During the
//! login phase, the client and server can specify this context using:
//! - In [Server Login Start](#server-login-start), where the server can
@@ -1008,21 +1023,23 @@
//! exposing the bytes of the private key to this library.
//! ```
//! # use generic_array::{GenericArray, typenum::U0};
//! # use opaque_ke::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
//! # use rand::rngs::OsRng;
//! # type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
//! # use opaque_vx::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, ServerRegistration, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
//! # use rand::rngs::SysRng;
//! # use rand_core::UnwrapErr;
//!
//! type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[derive(Debug, thiserror::Error)]
//! # #[error("test error")]
@@ -1034,9 +1051,9 @@
//! # Ok(<<Ristretto255 as Group>::Sk as DiffieHellman<Ristretto255>>::diffie_hellman(&self.0, pk.to_group_type()))
//! # }
//! # }
//! use opaque_ke::{ServerLogin, ServerLoginParameters, ServerSetup};
//! use opaque_ke::keypair::{KeyPair, PrivateKeySerialization};
//! use opaque_ke::errors::ProtocolError;
//! use opaque_vx::{ServerLogin, ServerLoginParameters, ServerSetup};
//! use opaque_vx::keypair::{KeyPair, PrivateKeySerialization};
//! use opaque_vx::errors::ProtocolError;
//!
//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
//! impl PrivateKeySerialization<Ristretto255> for YourRemoteKey {
@@ -1052,24 +1069,24 @@
//! }
//! }
//!
//! # let sk = Ristretto255::random_sk(&mut OsRng);
//! # let sk = Ristretto255::random_sk(&mut UnwrapErr(SysRng));
//! # let pk = Ristretto255::public_key(&sk);
//! # let pk = Ristretto255::serialize_pk(&pk);
//! # let public_key = PublicKey::deserialize(&pk).unwrap();
//! # let remote_key = YourRemoteKey(sk);
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! let keypair = KeyPair::new(remote_key, public_key);
//! let server_setup = ServerSetup::<Default, YourRemoteKey>::new_with_key_pair(&mut server_rng, keypair);
//!
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut OsRng,
//! # &mut UnwrapErr(SysRng),
//! # b"password",
//! # )?;
//! # let server_registration_start_result = ServerRegistration::<Default>::start(&server_setup, client_registration_start_result.message, b"[email protected]")?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut OsRng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut UnwrapErr(SysRng), b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut OsRng,
//! # &mut UnwrapErr(SysRng),
//! # b"password",
//! # )?;
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
@@ -1102,24 +1119,26 @@
//! # use digest::Output;
//! # use generic_array::{GenericArray, typenum::U0};
//! # use hkdf::Hkdf;
//! # use opaque_ke::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
//! # use rand::rngs::OsRng;
//! # use rand::RngCore;
//! # type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
//! # use opaque_vx::{CipherSuite, ClientLogin, ClientRegistration, ClientRegistrationFinishParameters, keypair::{PrivateKey, PublicKey}, key_exchange::{KeyExchange, group::Group, tripledh::DiffieHellman}};
//! # use rand::rngs::SysRng;
//! # use rand::Rng;
//! # use rand_core::UnwrapErr;
//!
//! type Ristretto255 = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Group;
//! # type Hash = <<Default as CipherSuite>::KeyExchange as KeyExchange>::Hash;
//! # type OprfGroup = <<Default as CipherSuite>::OprfCs as voprf::CipherSuite>::Group;
//! # struct Default;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for Default {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for Default {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_ke::ksf::Identity;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = opaque_vx::ksf::Identity;
//! # }
//! # #[derive(Debug, thiserror::Error)]
//! # #[error("test error")]
@@ -1143,9 +1162,9 @@
//! # Ok(<<Ristretto255 as Group>::Sk as DiffieHellman<Ristretto255>>::diffie_hellman(&self.0, pk.to_group_type()))
//! # }
//! # }
//! use opaque_ke::{ServerLogin, ServerLoginParameters, ServerRegistration, ServerSetup};
//! use opaque_ke::keypair::{KeyPair, OprfSeedSerialization};
//! use opaque_ke::errors::ProtocolError;
//! use opaque_vx::{ServerLogin, ServerLoginParameters, ServerRegistration, ServerSetup};
//! use opaque_vx::keypair::{KeyPair, OprfSeedSerialization};
//! use opaque_vx::errors::ProtocolError;
//!
//! // Implement if you intend to use `ServerSetup::de/serialize` instead of `serde`.
//! impl OprfSeedSerialization<sha2::Sha512, YourRemoteSecretsError> for YourRemoteSeed {
@@ -1160,20 +1179,20 @@
//! }
//! }
//!
//! # let mut oprf_seed = YourRemoteSeed(GenericArray::default());
//! # OsRng.fill_bytes(&mut oprf_seed.0);
//! # let sk = Ristretto255::random_sk(&mut OsRng);
//! # let mut oprf_seed = YourRemoteSeed(GenericArray::default().into_ha0_4());
//! # UnwrapErr(SysRng).fill_bytes(&mut oprf_seed.0);
//! # let sk = Ristretto255::random_sk(&mut UnwrapErr(SysRng));
//! # let pk = Ristretto255::public_key(&sk);
//! # let pk = Ristretto255::serialize_pk(&pk);
//! # let public_key = PublicKey::deserialize(&pk).unwrap();
//! # let remote_key = YourRemoteKey(sk);
//! # let mut server_rng = OsRng;
//! # let mut server_rng = UnwrapErr(SysRng);
//! let keypair = KeyPair::new(remote_key, public_key);
//! let server_setup = ServerSetup::<Default, YourRemoteKey, YourRemoteSeed>::new_with_key_pair_and_seed(&mut server_rng, keypair, oprf_seed);
//!
//! // Incoming registration ...
//! # let client_registration_start_result = ClientRegistration::<Default>::start(
//! # &mut OsRng,
//! # &mut UnwrapErr(SysRng),
//! # b"password",
//! # )?;
//!
@@ -1189,12 +1208,12 @@
//! )?;
//!
//! // Finish registration ...
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut OsRng, b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//! # let client_registration_finish_result = client_registration_start_result.state.finish(&mut UnwrapErr(SysRng), b"password", server_registration_start_result.message, ClientRegistrationFinishParameters::default())?;
//!
//! // Incoming login ...
//! # let password_file_bytes = ServerRegistration::<Default>::finish(client_registration_finish_result.message).serialize();
//! # let client_login_start_result = ClientLogin::<Default>::start(
//! # &mut OsRng,
//! # &mut UnwrapErr(SysRng),
//! # b"password",
//! # )?;
//! # let password_file = ServerRegistration::<Default>::deserialize(&password_file_bytes)?;
@@ -1231,20 +1250,20 @@
//! can be used.
//! ```
//! # use generic_array::GenericArray;
//! use opaque_ke::ksf::Ksf;
//! use opaque_vx::ksf::Ksf;
//!
//! #[derive(Default)]
//! struct CustomKsf(scrypt::Params);
//!
//! // The Ksf trait must be implemented to be used in the ciphersuite.
//! impl Ksf for CustomKsf {
//! fn hash<L: generic_array::ArrayLength<u8>>(
//! fn hash<L: generic_array::ArrayLength>(
//! &self,
//! input: GenericArray<u8, L>,
//! ) -> Result<GenericArray<u8, L>, opaque_ke::errors::InternalError> {
//! ) -> Result<GenericArray<u8, L>, opaque_vx::errors::InternalError> {
//! let mut output = GenericArray::<u8, L>::default();
//! scrypt::scrypt(&input, &[], &self.0, &mut output)
//! .map_err(|_| opaque_ke::errors::InternalError::KsfError)?;
//! .map_err(|_| opaque_vx::errors::InternalError::KsfError)?;
//!
//! Ok(output)
//! }
@@ -1255,37 +1274,38 @@
//! used by the KSF during registration and login. This can be especially
//! helpful if the `Ksf` trait is already implemented.
//! ```
//! # use opaque_ke::CipherSuite;
//! # use opaque_ke::ClientRegistration;
//! # use opaque_ke::ClientRegistrationFinishParameters;
//! # use opaque_ke::ServerSetup;
//! # use opaque_ke::errors::ProtocolError;
//! # use rand::rngs::OsRng;
//! # use rand::RngCore;
//! # use opaque_vx::CipherSuite;
//! # use opaque_vx::ClientRegistration;
//! # use opaque_vx::ClientRegistrationFinishParameters;
//! # use opaque_vx::ServerSetup;
//! # use opaque_vx::errors::ProtocolError;
//! # use rand::rngs::SysRng;
//! # use rand::Rng;
//! # use rand_core::UnwrapErr;
//! # use std::default::Default;
//! # #[cfg(feature = "argon2")]
//! # {
//! # struct DefaultCipherSuite;
//! # #[cfg(feature = "ristretto255")]
//! # impl CipherSuite for DefaultCipherSuite {
//! # type OprfCs = opaque_ke::Ristretto255;
//! # type KeyExchange = opaque_ke::TripleDh<opaque_ke::Ristretto255, sha2::Sha512>;
//! # type OprfCs = opaque_vx::Ristretto255;
//! # type KeyExchange = opaque_vx::TripleDh<opaque_vx::Ristretto255, sha2::Sha512>;
//! # type Ksf = argon2::Argon2<'static>;
//! # }
//! # #[cfg(not(feature = "ristretto255"))]
//! # impl CipherSuite for DefaultCipherSuite {
//! # type OprfCs = p256::NistP256;
//! # type KeyExchange = opaque_ke::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type KeyExchange = opaque_vx::TripleDh<p256::NistP256, sha2::Sha256>;
//! # type Ksf = argon2::Argon2<'static>;
//! # }
//! #
//! # let password = b"password";
//! # let mut rng = OsRng;
//! # let mut rng = UnwrapErr(SysRng);
//! # let server_setup = ServerSetup::<DefaultCipherSuite>::new(&mut rng);
//! # let mut client_rng = OsRng;
//! # let mut client_rng = UnwrapErr(SysRng);
//! # let client_registration_start_result =
//! # ClientRegistration::<DefaultCipherSuite>::start(&mut client_rng, password)?;
//! # use opaque_ke::ServerRegistration;
//! # use opaque_vx::ServerRegistration;
//! # let server_registration_start_result = ServerRegistration::<DefaultCipherSuite>::start(
//! # &server_setup,
//! # client_registration_start_result.message,
@@ -1384,6 +1404,7 @@ mod tests;
#[cfg(feature = "argon2")]
pub use argon2;
pub use generic_array;
pub use hybrid_array;
#[cfg(feature = "kem")]
pub use ml_kem;
pub use rand;
+57 -39
View File
@@ -15,7 +15,8 @@ use digest::Output;
use generic_array::sequence::Concat;
use generic_array::typenum::{Sum, Unsigned};
use generic_array::{ArrayLength, GenericArray};
use rand::{CryptoRng, RngCore};
use hybrid_array::Array;
use rand::{CryptoRng, Rng};
use voprf::{BlindedElement, BlindedElementLen, EvaluationElement, EvaluationElementLen};
use zeroize::Zeroizing;
@@ -50,7 +51,7 @@ use crate::serialization::SliceExt;
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::BlindedElement<CS::OprfCs>)]
pub struct RegistrationRequest<CS: CipherSuite> {
/// blinded password information
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
pub(crate) blinded_element: BlindedElement<CS::OprfCs>,
}
/// The answer sent by the server to the user, upon reception of the
@@ -64,10 +65,11 @@ pub struct RegistrationRequest<CS: CipherSuite> {
))
)]
#[derive_where(Clone)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::EvaluationElement<CS::OprfCs>, <KeGroup<CS> as Group>::Pk)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; voprf::EvaluationElement<CS::OprfCs>, <KeGroup<CS> as Group>::Pk
)]
pub struct RegistrationResponse<CS: CipherSuite> {
/// The server's oprf output
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
pub(crate) evaluation_element: EvaluationElement<CS::OprfCs>,
/// Server's static public key
pub(crate) server_s_pk: PublicKey<KeGroup<CS>>,
}
@@ -111,7 +113,7 @@ pub struct RegistrationUpload<CS: CipherSuite> {
<CS::KeyExchange as KeyExchange>::KE1Message,
)]
pub struct CredentialRequest<CS: CipherSuite> {
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
pub(crate) blinded_element: BlindedElement<CS::OprfCs>,
pub(crate) ke1_message: <CS::KeyExchange as KeyExchange>::KE1Message,
}
@@ -136,7 +138,7 @@ pub struct CredentialRequest<CS: CipherSuite> {
)]
pub struct ServerLoginBuilder<'a, CS: CipherSuite, SK: Clone> {
pub(crate) server_s_sk: SK,
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
pub(crate) evaluation_element: EvaluationElement<CS::OprfCs>,
pub(crate) masking_nonce: Zeroizing<GenericArray<u8, NonceLen>>,
pub(crate) masked_response: MaskedResponse<CS>,
#[cfg(test)]
@@ -184,12 +186,12 @@ impl<CS: CipherSuite, SK: Clone> ServerLoginBuilder<'_, CS, SK> {
#[derive_where(Clone)]
#[derive_where(
Debug, Eq, Hash, PartialEq;
voprf::EvaluationElement<CS::OprfCs>,
EvaluationElement<CS::OprfCs>,
<CS::KeyExchange as KeyExchange>::KE2Message,
)]
pub struct CredentialResponse<CS: CipherSuite> {
/// the server's oprf output
pub(crate) evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
pub(crate) evaluation_element: EvaluationElement<CS::OprfCs>,
pub(crate) masking_nonce: GenericArray<u8, NonceLen>,
pub(crate) masked_response: MaskedResponse<CS>,
pub(crate) ke2_message: <CS::KeyExchange as KeyExchange>::KE2Message,
@@ -225,19 +227,19 @@ pub type RegistrationRequestLen<CS: CipherSuite> = <OprfGroup<CS> as voprf::Grou
impl<CS: CipherSuite> RegistrationRequest<CS> {
/// Only used for testing purposes
#[cfg(test)]
pub(crate) fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
pub(crate) fn get_blinded_element_for_testing(&self) -> BlindedElement<CS::OprfCs> {
self.blinded_element.clone()
}
/// Serialization into bytes
pub fn serialize(&self) -> GenericArray<u8, RegistrationRequestLen<CS>> {
pub fn serialize(&self) -> Array<u8, RegistrationRequestLen<CS>> {
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.blinded_element.value())
}
/// Deserialization from bytes
pub fn deserialize(input: &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
blinded_element: voprf::BlindedElement::deserialize(input)?,
blinded_element: BlindedElement::deserialize(input)?,
})
}
}
@@ -251,11 +253,14 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
pub fn serialize(&self) -> GenericArray<u8, RegistrationResponseLen<CS>>
where
// RegistrationResponse: KgPk + KePk
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
RegistrationResponseLen<CS>: ArrayLength<u8>,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen> + ArrayLength,
RegistrationResponseLen<CS>: ArrayLength,
{
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.evaluation_element.value())
.concat(self.server_s_pk.serialize())
let elem = GenericArray::from_ha0_4(<OprfGroup<CS> as voprf::Group>::serialize_elem(
self.evaluation_element.value(),
));
elem.concat(self.server_s_pk.serialize())
}
/// Deserialization from bytes
@@ -277,7 +282,7 @@ impl<CS: CipherSuite> RegistrationResponse<CS> {
beta: <OprfGroup<CS> as voprf::Group>::Elem,
) -> Self {
Self {
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
evaluation_element: EvaluationElement::from_value_unchecked(beta),
server_s_pk: self.server_s_pk.clone(),
}
}
@@ -294,26 +299,29 @@ impl<CS: CipherSuite> RegistrationUpload<CS> {
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
{
self.client_s_pk
.serialize()
.concat(self.masking_key.clone())
.concat(self.envelope.serialize())
Concat::concat(
Concat::concat(
self.client_s_pk.serialize(),
GenericArray::from_slice(self.masking_key.as_slice()).clone(),
),
self.envelope.serialize(),
)
}
/// Deserialization from bytes
pub fn deserialize(mut input: &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
client_s_pk: PublicKey::deserialize_take(&mut input)?,
masking_key: input.take_array("masking key")?,
masking_key: input.take_array("masking key")?.into_ha0_4(),
envelope: Envelope::deserialize_take(&mut input)?,
})
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng, SK: Clone, OS: Clone>(
pub(crate) fn dummy<R: Rng + CryptoRng, SK: Clone, OS: Clone>(
rng: &mut R,
server_setup: &ServerSetup<CS, SK, OS>,
) -> Self {
@@ -338,11 +346,14 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
where
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
// CredentialRequest: KgPk + Ke1Message
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>> + ArrayLength,
CredentialRequestLen<CS>: ArrayLength,
{
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.blinded_element.value())
.concat(self.ke1_message.serialize())
let elem = GenericArray::from_ha0_4(<OprfGroup<CS> as voprf::Group>::serialize_elem(
self.blinded_element.value(),
));
elem.concat(self.ke1_message.serialize())
}
/// Deserialization from bytes
@@ -372,7 +383,7 @@ impl<CS: CipherSuite> CredentialRequest<CS> {
/// Only used for testing purposes
#[cfg(test)]
pub(crate) fn get_blinded_element_for_testing(&self) -> voprf::BlindedElement<CS::OprfCs> {
pub(crate) fn get_blinded_element_for_testing(&self) -> BlindedElement<CS::OprfCs> {
self.blinded_element.clone()
}
}
@@ -390,18 +401,25 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
where
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen> + ArrayLength,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
<OprfGroup<CS> as voprf::Group>::serialize_elem(self.evaluation_element.value())
.concat(self.masking_nonce)
.concat(self.masked_response.serialize())
.concat(self.ke2_message.serialize())
let elem = GenericArray::from_ha0_4(<OprfGroup<CS> as voprf::Group>::serialize_elem(
self.evaluation_element.value(),
));
Concat::concat(
Concat::concat(
Concat::concat(elem, self.masking_nonce),
self.masked_response.serialize(),
),
self.ke2_message.serialize(),
)
}
/// Deserialization from bytes
@@ -410,7 +428,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize,
{
let evaluation_element = EvaluationElement::deserialize(input)?;
input = &input[voprf::EvaluationElementLen::<CS::OprfCs>::USIZE..];
input = &input[EvaluationElementLen::<CS::OprfCs>::USIZE..];
Ok(Self {
evaluation_element,
@@ -438,7 +456,7 @@ impl<CS: CipherSuite> CredentialResponse<CS> {
beta: <OprfGroup<CS> as voprf::Group>::Elem,
) -> Self {
Self {
evaluation_element: voprf::EvaluationElement::from_value_unchecked(beta),
evaluation_element: EvaluationElement::from_value_unchecked(beta),
masking_nonce: self.masking_nonce,
masked_response: self.masked_response.clone(),
ke2_message: self.ke2_message.clone(),
+74 -66
View File
@@ -8,15 +8,14 @@
//! Provides the main OPAQUE API
use core::ops::{Add, Deref};
use core::ops::Add;
use derive_where::derive_where;
use digest::Output;
use generic_array::sequence::Concat;
use generic_array::typenum::{Sum, Unsigned};
use generic_array::{ArrayLength, GenericArray};
use hkdf::{Hkdf, HkdfExtract};
use rand::{CryptoRng, RngCore};
use hkdf::Hkdf;
use hkdf::SimpleHkdfExtract as HkdfExtract;
use rand::{CryptoRng, Rng};
use subtle::{Choice, ConstantTimeEq, CtOption};
use voprf::{BlindedElement, Group as _, OprfClient, OprfClientLen};
use zeroize::Zeroizing;
@@ -36,7 +35,7 @@ use crate::keypair::{
};
use crate::ksf::Ksf;
use crate::messages::{CredentialRequestLen, RegistrationUploadLen};
use crate::serialization::{GenericArrayExt, SliceExt};
use crate::serialization::{ConcatExt, GenericArrayExt, SliceExt};
use crate::{
CredentialFinalization, CredentialRequest, CredentialResponse, RegistrationRequest,
RegistrationResponse, RegistrationUpload, ServerLoginBuilder,
@@ -70,7 +69,8 @@ const STR_OPAQUE_DERIVE_KEY_PAIR: &[u8; 20] = b"OPAQUE-DeriveKeyPair";
))
)]
#[derive_where(Clone)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk, <KeGroup<CS> as Group>::Sk, SK, OS)]
#[derive_where(Debug, Eq, Hash, Ord, PartialEq, PartialOrd; <KeGroup<CS> as Group>::Pk, <KeGroup<CS> as Group>::Sk, SK, OS
)]
pub struct ServerSetup<
CS: CipherSuite,
SK: Clone = PrivateKey<KeGroup<CS>>,
@@ -94,8 +94,8 @@ pub struct ServerSetup<
voprf::BlindedElement<CS::OprfCs>,
)]
pub struct ClientRegistration<CS: CipherSuite> {
pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
pub(crate) blinded_element: voprf::BlindedElement<CS::OprfCs>,
pub(crate) oprf_client: OprfClient<CS::OprfCs>,
pub(crate) blinded_element: BlindedElement<CS::OprfCs>,
}
/// The state elements the server holds to record a registration
@@ -130,7 +130,7 @@ pub struct ServerRegistration<CS: CipherSuite>(pub(crate) RegistrationUpload<CS>
CredentialRequest<CS>,
)]
pub struct ClientLogin<CS: CipherSuite> {
pub(crate) oprf_client: voprf::OprfClient<CS::OprfCs>,
pub(crate) oprf_client: OprfClient<CS::OprfCs>,
pub(crate) ke1_state: <CS::KeyExchange as KeyExchange>::KE1State,
pub(crate) credential_request: CredentialRequest<CS>,
}
@@ -160,7 +160,7 @@ pub struct ServerLogin<CS: CipherSuite> {
impl<CS: CipherSuite> ServerSetup<CS, PrivateKey<KeGroup<CS>>> {
/// Generate a new instance of server setup
pub fn new<R: CryptoRng + RngCore>(rng: &mut R) -> Self {
pub fn new<R: CryptoRng + Rng>(rng: &mut R) -> Self {
let keypair = KeyPair::random(rng);
Self::new_with_key_pair(rng, keypair)
}
@@ -179,7 +179,7 @@ impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
/// This function should not be used to restore a previously-existing
/// instance of [`ServerSetup`]. Instead, use [`ServerSetup::serialize`] and
/// [`ServerSetup::deserialize`] for this purpose.
pub fn new_with_key_pair_and_seed<R: CryptoRng + RngCore>(
pub fn new_with_key_pair_and_seed<R: CryptoRng + Rng>(
rng: &mut R,
keypair: KeyPair<KeGroup<CS>, SK>,
oprf_seed: OS,
@@ -211,13 +211,13 @@ impl<CS: CipherSuite, SK: Clone, OS: Clone> ServerSetup<CS, SK, OS> {
OS: OprfSeedSerialization<OprfHash<CS>, SK::Error>,
// ServerSetup: Hash + KeSk + KePk
OS::Len: Add<SK::Len>,
Sum<OS::Len, SK::Len>: ArrayLength<u8> + Add<<KeGroup<CS> as Group>::PkLen>,
ServerSetupLen<CS, SK, OS>: ArrayLength<u8>,
Sum<OS::Len, SK::Len>: ArrayLength + Add<<KeGroup<CS> as Group>::PkLen>,
ServerSetupLen<CS, SK, OS>: ArrayLength,
{
self.oprf_seed
.serialize()
.concat(SK::serialize_key_pair(&self.keypair))
.concat(self.dummy_pk.serialize())
.cat(SK::serialize_key_pair(&self.keypair))
.cat(self.dummy_pk.serialize())
}
/// Deserialization from bytes
@@ -246,11 +246,11 @@ impl<CS: CipherSuite, SK: Clone> ServerSetup<CS, SK> {
/// This function should not be used to restore a previously-existing
/// instance of [`ServerSetup`]. Instead, use [`ServerSetup::serialize`] and
/// [`ServerSetup::deserialize`] for this purpose.
pub fn new_with_key_pair<R: CryptoRng + RngCore>(
pub fn new_with_key_pair<R: CryptoRng + Rng>(
rng: &mut R,
keypair: KeyPair<KeGroup<CS>, SK>,
) -> Self {
let mut oprf_seed = GenericArray::default();
let mut oprf_seed = Output::<OprfHash<CS>>::default();
rng.fill_bytes(&mut oprf_seed);
Self::new_with_key_pair_and_seed(rng, keypair, OprfSeed(oprf_seed))
@@ -282,12 +282,13 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
pub fn serialize(&self) -> GenericArray<u8, ClientRegistrationLen<CS>>
where
// ClientRegistration: KgSk + KgPk
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
ClientRegistrationLen<CS>: ArrayLength<u8>,
<OprfGroup<CS> as voprf::Group>::ScalarLen:
Add<<OprfGroup<CS> as voprf::Group>::ElemLen> + ArrayLength,
<OprfGroup<CS> as voprf::Group>::ElemLen: ArrayLength,
ClientRegistrationLen<CS>: ArrayLength,
{
self.oprf_client
.serialize()
.concat(self.blinded_element.serialize())
GenericArray::from_ha0_4(self.oprf_client.serialize())
.cat(GenericArray::from_ha0_4(self.blinded_element.serialize()))
}
/// Deserialization from bytes
@@ -305,7 +306,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
/// Returns an initial "blinded" request to send to the server, as well as a
/// [`ClientRegistration`]
pub fn start<R: RngCore + CryptoRng>(
pub fn start<R: Rng + CryptoRng>(
blinding_factor_rng: &mut R,
password: &[u8],
) -> Result<ClientRegistrationStartResult<CS>, ProtocolError> {
@@ -325,7 +326,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
/// "Unblinds" the server's answer and returns a final message containing
/// cryptographic identifiers, to be sent to the server on setup
/// finalization
pub fn finish<R: CryptoRng + RngCore>(
pub fn finish<R: CryptoRng + Rng>(
self,
rng: &mut R,
password: &[u8],
@@ -357,7 +358,7 @@ impl<CS: CipherSuite> ClientRegistration<CS> {
let result = Envelope::<CS>::seal(
rng,
randomized_pwd_hasher,
&randomized_pwd_hasher,
&registration_response.server_s_pk,
params.identifiers,
)?;
@@ -390,8 +391,8 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
{
self.0.serialize()
@@ -449,7 +450,7 @@ impl<CS: CipherSuite> ServerRegistration<CS> {
}
// Creates a dummy instance used for faking a [CredentialResponse]
pub(crate) fn dummy<R: RngCore + CryptoRng, SK: Clone, S: Clone>(
pub(crate) fn dummy<R: Rng + CryptoRng, SK: Clone, S: Clone>(
rng: &mut R,
server_setup: &ServerSetup<CS, SK, S>,
) -> Self {
@@ -470,18 +471,17 @@ impl<CS: CipherSuite> ClientLogin<CS> {
// CredentialRequest: KgPk + Ke1Message
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
CredentialRequestLen<CS>: ArrayLength,
// ClientLogin: KgSk + CredentialRequest + Ke1State
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
<CS::KeyExchange as KeyExchange>::KE1State: Serialize,
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength<u8>,
ArrayLength + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength,
{
self.oprf_client
.serialize()
.concat(self.credential_request.serialize())
.concat(self.ke1_state.serialize())
GenericArray::from_ha0_4(self.oprf_client.serialize())
.cat(self.credential_request.serialize())
.cat(self.ke1_state.serialize())
}
/// Deserialization from bytes
@@ -504,7 +504,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
impl<CS: CipherSuite> ClientLogin<CS> {
/// Returns an initial "blinded" password request to send to the server, as
/// well as a [`ClientLogin`]
pub fn start<R: RngCore + CryptoRng>(
pub fn start<R: Rng + CryptoRng>(
rng: &mut R,
password: &[u8],
) -> Result<ClientLoginStartResult<CS>, ProtocolError> {
@@ -528,7 +528,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
/// "Unblinds" the server's answer and returns the opened assets from the
/// server
pub fn finish<R: CryptoRng + RngCore>(
pub fn finish<R: CryptoRng + Rng>(
self,
rng: &mut R,
password: &[u8],
@@ -570,7 +570,7 @@ impl<CS: CipherSuite> ClientLogin<CS> {
let opened_envelope = envelope
.open(
randomized_pwd_hasher,
&randomized_pwd_hasher,
server_s_pk.clone(),
params.identifiers,
)
@@ -641,7 +641,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
///
/// See [`ServerLogin::start()`] for the regular path. Or
/// [`ServerLogin::builder()`] with just a remote private key.
pub fn builder_with_key_material<'a, R: RngCore + CryptoRng, SK: Clone, OS: Clone>(
pub fn builder_with_key_material<'a, R: Rng + CryptoRng, SK: Clone, OS: Clone>(
rng: &mut R,
server_setup: &ServerSetup<CS, SK, OS>,
key_material: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
@@ -668,7 +668,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let masked_response = mask_response(
&record.0.masking_key,
masking_nonce.as_slice(),
&masking_nonce,
server_s_pk,
&record.0.envelope,
)?;
@@ -715,7 +715,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
/// Create a [`ServerLoginBuilder`] to use with a remote private key.
///
/// See [`ServerLogin::start()`] for the regular path.
pub fn builder<'a, R: RngCore + CryptoRng, SK: Clone>(
pub fn builder<'a, R: Rng + CryptoRng, SK: Clone>(
rng: &mut R,
server_setup: &ServerSetup<CS, SK>,
password_file: Option<ServerRegistration<CS>>,
@@ -747,7 +747,7 @@ impl<CS: CipherSuite> ServerLogin<CS> {
let credential_response = CredentialResponse {
evaluation_element: builder.evaluation_element.clone(),
masking_nonce: *builder.masking_nonce.deref(),
masking_nonce: *builder.masking_nonce,
masked_response: builder.masked_response.clone(),
ke2_message: result.message,
};
@@ -762,13 +762,13 @@ impl<CS: CipherSuite> ServerLogin<CS> {
#[cfg(test)]
server_mac_key: result.km2,
#[cfg(test)]
oprf_key: builder.oprf_key.deref().clone(),
oprf_key: (*builder.oprf_key).clone(),
})
}
/// From the client's "blinded" password, returns a challenge to be sent
/// back to the client, as well as a [`ServerLogin`]
pub fn start<R: RngCore + CryptoRng>(
pub fn start<R: Rng + CryptoRng>(
rng: &mut R,
server_setup: &ServerSetup<CS>,
password_file: Option<ServerRegistration<CS>>,
@@ -1004,21 +1004,22 @@ pub struct ServerLoginStartResult<CS: CipherSuite> {
#[allow(clippy::type_complexity)]
fn get_password_derived_key<CS: CipherSuite>(
input: &[u8],
oprf_client: voprf::OprfClient<CS::OprfCs>,
oprf_client: OprfClient<CS::OprfCs>,
evaluation_element: voprf::EvaluationElement<CS::OprfCs>,
ksf: Option<&CS::Ksf>,
) -> Result<(Output<OprfHash<CS>>, Hkdf<OprfHash<CS>>), ProtocolError> {
) -> Result<(Output<OprfHash<CS>>, hkdf::SimpleHkdf<OprfHash<CS>>), ProtocolError> {
let oprf_output = oprf_client.finalize(input, &evaluation_element)?;
let oprf_ga = GenericArray::from_ha0_4(oprf_output.clone());
let hardened_output = if let Some(ksf) = ksf {
ksf.hash(oprf_output.clone())
ksf.hash(oprf_ga.clone())
} else {
CS::Ksf::default().hash(oprf_output.clone())
CS::Ksf::default().hash(oprf_ga.clone())
}
.map_err(ProtocolError::from)?;
let mut hkdf = HkdfExtract::<OprfHash<CS>>::new(None);
hkdf.input_ikm(&oprf_output);
hkdf.input_ikm(&oprf_ga);
hkdf.input_ikm(&hardened_output);
Ok(hkdf.finalize())
}
@@ -1039,13 +1040,9 @@ fn oprf_key_material<CS: CipherSuite>(
fn oprf_key_from_key_material<CS: CipherSuite>(
input: GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>,
) -> Result<GenericArray<u8, <OprfGroup<CS> as voprf::Group>::ScalarLen>, InternalError> {
Ok(OprfGroup::<CS>::serialize_scalar(voprf::derive_key::<
CS::OprfCs,
>(
input.as_slice(),
&GenericArray::from(*STR_OPAQUE_DERIVE_KEY_PAIR),
voprf::Mode::Oprf,
)?))
Ok(GenericArray::from_ha0_4(OprfGroup::<CS>::serialize_scalar(
voprf::derive_key::<CS::OprfCs>(&input, STR_OPAQUE_DERIVE_KEY_PAIR, voprf::Mode::Oprf)?,
)))
}
#[cfg_attr(
@@ -1066,19 +1063,28 @@ pub(crate) type MaskedResponseLen<CS: CipherSuite> =
impl<CS: CipherSuite> MaskedResponse<CS> {
pub(crate) fn serialize(&self) -> GenericArray<u8, MaskedResponseLen<CS>> {
self.nonce.concat_ext(&self.hash).concat(self.pk.clone())
}
let hash_ga: &GenericArray<u8, OutputSize<OprfHash<CS>>> =
GenericArray::from_slice(self.hash.as_slice());
self.nonce.concat_ext(hash_ga).cat(self.pk.clone())
}
pub(crate) fn deserialize_take(bytes: &mut &[u8]) -> Result<Self, ProtocolError> {
Ok(Self {
nonce: bytes.take_array("masked nonce")?,
hash: bytes.take_array("masked hash")?,
hash: bytes
.take_array::<OutputSize<OprfHash<CS>>>("masked hash")?
.into_ha0_4(),
pk: bytes.take_array("masked public key")?,
})
}
pub(crate) fn iter(&self) -> impl Clone + Iterator<Item = &[u8]> {
[self.nonce.as_slice(), &self.hash, &self.pk].into_iter()
[
self.nonce.as_slice(),
self.hash.as_slice(),
self.pk.as_slice(),
]
.into_iter()
}
}
@@ -1105,7 +1111,9 @@ fn mask_response<CS: CipherSuite>(
*x1 ^= x2
}
MaskedResponse::deserialize_take(&mut (xor_pad.as_slice()))
let mut slice: &[u8] = &xor_pad;
MaskedResponse::deserialize_take(&mut (slice))
}
fn unmask_response<CS: CipherSuite>(
@@ -1124,7 +1132,7 @@ fn unmask_response<CS: CipherSuite>(
*x1 ^= x2
}
let mut xor_pad = xor_pad.as_slice();
let mut xor_pad: &[u8] = xor_pad.as_ref();
let server_s_pk =
PublicKey::deserialize_take(&mut xor_pad).map_err(|_| ProtocolError::SerializationError)?;
let envelope = Envelope::deserialize_take(&mut xor_pad)?;
@@ -1135,12 +1143,12 @@ fn unmask_response<CS: CipherSuite>(
/// Internal function for computing the blind result by calling the voprf
/// library. Note that for tests, we use the deterministic blinding in order to
/// be able to set the blinding factor directly from the passed-in rng.
fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
fn blind<CS: CipherSuite, R: Rng + CryptoRng>(
rng: &mut R,
password: &[u8],
) -> Result<voprf::OprfClientBlindResult<CS::OprfCs>, voprf::Error> {
#[cfg(not(test))]
let result = voprf::OprfClient::blind(password, rng)?;
let result = OprfClient::blind(password, rng)?;
#[cfg(test)]
let result = {
@@ -1152,7 +1160,7 @@ fn blind<CS: CipherSuite, R: RngCore + CryptoRng>(
break scalar;
}
};
voprf::OprfClient::deterministic_blind_unchecked(password, blind)?
OprfClient::deterministic_blind_unchecked(password, blind)?
};
Ok(result)
+33 -17
View File
@@ -8,18 +8,16 @@
use core::ops::Add;
use crate::errors::ProtocolError;
use digest::Update;
use generic_array::sequence::Concat;
use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray};
use crate::errors::ProtocolError;
use hybrid_array::{Array, ArraySize};
// Corresponds to the I2OSP() function from RFC8017
pub(crate) fn i2osp<L: ArrayLength<u8>>(
input: usize,
) -> Result<GenericArray<u8, L>, ProtocolError> {
const SIZEOF_USIZE: usize = core::mem::size_of::<usize>();
pub(crate) fn i2osp<L: ArrayLength>(input: usize) -> Result<GenericArray<u8, L>, ProtocolError> {
const SIZEOF_USIZE: usize = size_of::<usize>();
// Make sure input fits in output.
if (SIZEOF_USIZE as u32 - input.leading_zeros() / 8) > L::U32 {
@@ -35,12 +33,12 @@ pub(crate) fn i2osp<L: ArrayLength<u8>>(
// Corresponds to the OS2IP() function from RFC8017
#[cfg(test)]
pub(crate) fn os2ip(input: &[u8]) -> Result<usize, ProtocolError> {
if input.len() > core::mem::size_of::<usize>() {
if input.len() > size_of::<usize>() {
return Err(ProtocolError::SerializationError);
}
let mut output_array = [0u8; core::mem::size_of::<usize>()];
output_array[core::mem::size_of::<usize>() - input.len()..].copy_from_slice(input);
let mut output_array = [0u8; size_of::<usize>()];
output_array[size_of::<usize>() - input.len()..].copy_from_slice(input);
Ok(usize::from_be_bytes(output_array))
}
@@ -69,14 +67,14 @@ impl<T: Update> UpdateExt for T {
}
pub(crate) trait SliceExt {
fn take_array<L: ArrayLength<u8>>(
fn take_array<L: ArrayLength + ArraySize>(
self: &mut &Self,
name: &'static str,
) -> Result<GenericArray<u8, L>, ProtocolError>;
}
impl SliceExt for [u8] {
fn take_array<L: ArrayLength<u8>>(
fn take_array<L: ArrayLength + ArraySize>(
self: &mut &Self,
name: &'static str,
) -> Result<GenericArray<u8, L>, ProtocolError> {
@@ -90,23 +88,24 @@ impl SliceExt for [u8] {
let (front, back) = self.split_at(L::USIZE);
*self = back;
Ok(GenericArray::clone_from_slice(front))
let arr: Array<u8, L> = Array::try_from(front).unwrap();
Ok(GenericArray::from(arr))
}
}
pub(crate) trait GenericArrayExt<O: ArrayLength<u8>> {
type Output: ArrayLength<u8>;
pub(crate) trait GenericArrayExt<O: ArrayLength> {
type Output: ArrayLength;
/// This allows us to concat two [`GenericArray`]s but with `where` bounds
/// `Other + Self`. Because sometimes `Self + Other` doesn't imply the
/// bounds and we have to add them to every call.
/// bounds, and we have to add them to every call.
fn concat_ext(&self, rest: &GenericArray<u8, O>) -> GenericArray<u8, Self::Output>;
}
impl<L: ArrayLength<u8>, O: ArrayLength<u8>> GenericArrayExt<O> for GenericArray<u8, L>
impl<L: ArrayLength, O: ArrayLength> GenericArrayExt<O> for GenericArray<u8, L>
where
O: Add<L>,
Sum<O, L>: ArrayLength<u8>,
Sum<O, L>: ArrayLength,
{
type Output = Sum<O, L>;
@@ -119,6 +118,23 @@ where
}
}
pub(crate) trait ConcatExt<N: ArrayLength>: Sized {
fn cat<M: ArrayLength>(self, other: GenericArray<u8, M>) -> GenericArray<u8, Sum<N, M>>
where
N: Add<M>,
Sum<N, M>: ArrayLength;
}
impl<N: ArrayLength> ConcatExt<N> for GenericArray<u8, N> {
fn cat<M: ArrayLength>(self, other: GenericArray<u8, M>) -> GenericArray<u8, Sum<N, M>>
where
N: Add<M>,
Sum<N, M>: ArrayLength,
{
Concat::concat(self, other)
}
}
#[cfg(test)]
mod tests;
+62 -63
View File
@@ -15,8 +15,9 @@ use generic_array::ArrayLength;
use generic_array::typenum::{Sum, Unsigned};
use proptest::collection::vec;
use proptest::prelude::*;
use rand::RngCore;
use rand::rngs::OsRng;
use rand::Rng;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
use voprf::Group as _;
use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
@@ -41,7 +42,7 @@ struct TripleDhRistretto255;
impl CipherSuite for TripleDhRistretto255 {
type OprfCs = Ristretto255;
type KeyExchange = TripleDh<Ristretto255, sha2::Sha512>;
type Ksf = crate::ksf::Identity;
type Ksf = ksf::Identity;
}
#[cfg(all(feature = "ristretto255", feature = "curve25519"))]
@@ -51,31 +52,31 @@ struct TripleDhCurve25519;
impl CipherSuite for TripleDhCurve25519 {
type OprfCs = Ristretto255;
type KeyExchange = TripleDh<Curve25519, sha2::Sha512>;
type Ksf = crate::ksf::Identity;
type Ksf = ksf::Identity;
}
struct TripleDhP256;
impl CipherSuite for TripleDhP256 {
type OprfCs = ::p256::NistP256;
type KeyExchange = TripleDh<::p256::NistP256, sha2::Sha256>;
type Ksf = crate::ksf::Identity;
type OprfCs = p256::NistP256;
type KeyExchange = TripleDh<p256::NistP256, sha2::Sha256>;
type Ksf = ksf::Identity;
}
struct TripleDhP384;
impl CipherSuite for TripleDhP384 {
type OprfCs = ::p384::NistP384;
type KeyExchange = TripleDh<::p384::NistP384, sha2::Sha384>;
type Ksf = crate::ksf::Identity;
type OprfCs = p384::NistP384;
type KeyExchange = TripleDh<p384::NistP384, sha2::Sha384>;
type Ksf = ksf::Identity;
}
struct TripleDhP521;
impl CipherSuite for TripleDhP521 {
type OprfCs = ::p521::NistP521;
type KeyExchange = TripleDh<::p521::NistP521, sha2::Sha512>;
type Ksf = crate::ksf::Identity;
type OprfCs = p521::NistP521;
type KeyExchange = TripleDh<p521::NistP521, sha2::Sha512>;
type Ksf = ksf::Identity;
}
#[cfg(feature = "ecdsa")]
@@ -83,10 +84,9 @@ struct SigmaIP256;
#[cfg(feature = "ecdsa")]
impl CipherSuite for SigmaIP256 {
type OprfCs = ::p256::NistP256;
type KeyExchange =
SigmaI<Ecdsa<::p256::NistP256, sha2::Sha256>, ::p256::NistP256, sha2::Sha256>;
type Ksf = crate::ksf::Identity;
type OprfCs = p256::NistP256;
type KeyExchange = SigmaI<Ecdsa<p256::NistP256, sha2::Sha256>, p256::NistP256, sha2::Sha256>;
type Ksf = ksf::Identity;
}
#[cfg(feature = "ecdsa")]
@@ -94,10 +94,9 @@ struct SigmaIP384;
#[cfg(feature = "ecdsa")]
impl CipherSuite for SigmaIP384 {
type OprfCs = ::p384::NistP384;
type KeyExchange =
SigmaI<Ecdsa<::p384::NistP384, sha2::Sha384>, ::p384::NistP384, sha2::Sha384>;
type Ksf = crate::ksf::Identity;
type OprfCs = p384::NistP384;
type KeyExchange = SigmaI<Ecdsa<p384::NistP384, sha2::Sha384>, p384::NistP384, sha2::Sha384>;
type Ksf = ksf::Identity;
}
#[cfg(all(feature = "ristretto255", feature = "ed25519",))]
@@ -107,7 +106,7 @@ struct SigmaIEd25519;
impl CipherSuite for SigmaIEd25519 {
type OprfCs = Ristretto255;
type KeyExchange = SigmaI<PureEddsa<Ed25519>, Ristretto255, sha2::Sha512>;
type Ksf = crate::ksf::Identity;
type Ksf = ksf::Identity;
}
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
@@ -117,19 +116,19 @@ struct SigmaIEd25519Ph;
impl CipherSuite for SigmaIEd25519Ph {
type OprfCs = Ristretto255;
type KeyExchange = SigmaI<HashEddsa<Ed25519>, Ristretto255, sha2::Sha512>;
type Ksf = crate::ksf::Identity;
type Ksf = ksf::Identity;
}
#[cfg(feature = "ecdsa")]
fn random_point<CS: CipherSuite>() -> <KeGroup<CS> as Group>::Pk {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let sk = KeGroup::<CS>::random_sk(&mut rng);
KeGroup::<CS>::public_key(&sk)
}
fn random_element<CS: CipherSuite>() -> <OprfGroup<CS> as voprf::Group>::Elem {
let mut rng = OsRng;
let scalar = OprfGroup::<CS>::random_scalar(&mut rng);
let mut rng = UnwrapErr(SysRng);
let scalar = OprfGroup::<CS>::random_scalar(&mut rng).unwrap();
OprfGroup::<CS>::base_elem() * &scalar
}
@@ -139,10 +138,10 @@ fn client_registration_roundtrip() -> Result<(), ProtocolError> {
where
// ClientRegistration: KgSk + KgPk
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
ClientRegistrationLen<CS>: ArrayLength<u8>,
ClientRegistrationLen<CS>: ArrayLength,
{
let pw = b"hunter2";
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let blind_result = &voprf::OprfClient::<CS::OprfCs>::blind(pw, &mut rng)?;
@@ -186,12 +185,12 @@ fn server_registration_roundtrip() -> Result<(), ProtocolError> {
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
{
// If we don't have envelope and client_pk, the server registration just
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let mut masking_key = Output::<OprfHash<CS>>::default();
rng.fill_bytes(&mut masking_key);
@@ -287,11 +286,11 @@ fn registration_response_roundtrip() -> Result<(), ProtocolError> {
where
// RegistrationResponse: KgPk + KePk
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
RegistrationResponseLen<CS>: ArrayLength<u8>,
RegistrationResponseLen<CS>: ArrayLength,
{
let elem = random_element::<CS>();
let beta_bytes = OprfGroup::<CS>::serialize_elem(elem);
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let skp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
let pubkey_bytes = skp.public().serialize();
@@ -345,10 +344,10 @@ fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let skp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
let pubkey_bytes = skp.public().serialize();
@@ -360,15 +359,15 @@ fn registration_upload_roundtrip() -> Result<(), ProtocolError> {
let mut masking_key = Output::<OprfHash<CS>>::default();
rng.fill_bytes(&mut masking_key);
let randomized_pwd_hasher = hkdf::Hkdf::new(None, &key);
let randomized_pwd_hasher = hkdf::SimpleHkdf::<OprfHash<CS>>::new(None, &key);
let (envelope, _, _) = Envelope::<CS>::seal_raw(
randomized_pwd_hasher,
&randomized_pwd_hasher,
nonce.into(),
[pubkey_bytes.as_slice()].into_iter(),
InnerEnvelopeMode::Internal,
)
.unwrap();
)?;
let envelope_bytes = envelope.serialize();
let mut input = Vec::new();
@@ -409,9 +408,9 @@ fn triple_dh_credential_request_roundtrip() -> Result<(), ProtocolError> {
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize + Serialize,
// CredentialRequest: KgPk + Ke1Message
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
CredentialRequestLen<CS>: ArrayLength,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let alpha = random_element::<CS>();
let alpha_bytes = OprfGroup::<CS>::serialize_elem(alpha);
@@ -466,17 +465,17 @@ fn triple_dh_credential_response_roundtrip() -> Result<(), ProtocolError> {
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
let elem = random_element::<CS>();
let elem_bytes = OprfGroup::<CS>::serialize_elem(elem);
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let mut masking_nonce = [0u8; 32];
rng.fill_bytes(&mut masking_nonce);
@@ -504,7 +503,7 @@ fn triple_dh_credential_response_roundtrip() -> Result<(), ProtocolError> {
input.extend_from_slice(&masked_response);
input.extend_from_slice(&ke2m);
let l2 = CredentialResponse::<CS>::deserialize(&input).unwrap();
let l2 = CredentialResponse::<CS>::deserialize(&input)?;
let l2_bytes = l2.serialize();
assert_eq!(input, *l2_bytes);
@@ -550,17 +549,17 @@ fn sigma_i_ecdsa_credential_response_roundtrip() -> Result<(), ProtocolError> {
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
let pt = random_point::<CS>();
let pt_bytes = KeGroup::<CS>::serialize_pk(&pt);
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let mut masking_nonce = [0u8; 32];
rng.fill_bytes(&mut masking_nonce);
@@ -630,7 +629,7 @@ fn triple_dh_credential_finalization_roundtrip() -> Result<(), ProtocolError> {
where
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let mut mac = Output::<OprfHash<CS>>::default();
rng.fill_bytes(&mut mac);
@@ -661,7 +660,7 @@ fn sigma_i_ecdsa_credential_finalization_roundtrip() -> Result<(), ProtocolError
where
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let r = KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut rng));
let s = KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut rng));
@@ -696,16 +695,16 @@ fn triple_dh_client_login_roundtrip() -> Result<(), ProtocolError> {
// CredentialRequest: KgPk + Ke1Message
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
CredentialRequestLen<CS>: ArrayLength,
// ClientLogin: KgSk + CredentialRequest + Ke1State
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
<CS::KeyExchange as KeyExchange>::KE1State: Serialize,
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength<u8>,
ArrayLength + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength,
{
let pw = b"hunter2";
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let client_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
let mut client_nonce = [0; NonceLen::USIZE];
@@ -762,7 +761,7 @@ fn triple_dh_ke1_message_roundtrip() -> Result<(), ProtocolError> {
where
<CS::KeyExchange as KeyExchange>::KE1Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let client_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
let mut client_nonce = vec![0u8; NonceLen::USIZE];
@@ -798,7 +797,7 @@ fn triple_dh_ke2_message_roundtrip() -> Result<(), ProtocolError> {
where
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let server_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
let mut mac = Output::<OprfHash<CS>>::default();
@@ -839,7 +838,7 @@ fn sigma_i_ecdsa_ke2_message_roundtrip() -> Result<(), ProtocolError> {
where
<CS::KeyExchange as KeyExchange>::KE2Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let server_e_kp = KeyPair::<KeGroup<CS>>::derive_random(&mut rng);
let mut mac = Output::<OprfHash<CS>>::default();
@@ -878,7 +877,7 @@ fn triple_dh_ke3_message_roundtrip() -> Result<(), ProtocolError> {
where
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let mut mac = Output::<OprfHash<CS>>::default();
rng.fill_bytes(&mut mac);
@@ -910,7 +909,7 @@ fn sigma_i_ecdsa_ke3_message_roundtrip() -> Result<(), ProtocolError> {
where
<CS::KeyExchange as KeyExchange>::KE3Message: Deserialize + Serialize,
{
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
let r = KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut rng));
let s = KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut rng));
let mut mac = Output::<OprfHash<CS>>::default();
@@ -934,7 +933,7 @@ fn sigma_i_ecdsa_ke3_message_roundtrip() -> Result<(), ProtocolError> {
proptest! {
#[test]
fn test_i2osp_os2ip(bytes in vec(any::<u8>(), 0..core::mem::size_of::<usize>())) {
fn test_i2osp_os2ip(bytes in vec(any::<u8>(), 0..size_of::<usize>())) {
use generic_array::typenum::{U0, U1, U2, U3, U4, U5, U6, U7};
let input = os2ip(&bytes).unwrap();
+70 -80
View File
@@ -19,8 +19,9 @@ use generic_array::{ArrayLength, GenericArray};
#[cfg(feature = "kem")]
use ml_kem::MlKem768;
use rand::SeedableRng;
use rand::rngs::OsRng;
use rand::rngs::SysRng;
use rand_chacha::ChaCha20Rng;
use rand_core::UnwrapErr;
use serde_json::Value;
use subtle::ConstantTimeEq;
use voprf::Group as _;
@@ -42,6 +43,7 @@ use crate::messages::{
RegistrationResponseLen, RegistrationUploadLen,
};
use crate::opaque::*;
use crate::tests::decode;
use crate::tests::mock_rng::CycleRng;
use crate::*;
@@ -261,10 +263,6 @@ pub struct TestVectorParameters {
static STR_PASSWORD: &str = "password";
fn decode(values: &Value, key: &str) -> Option<Vec<u8>> {
values[key].as_str().and_then(|s| hex::decode(s).ok())
}
fn populate_test_vectors(values: &Value) -> TestVectorParameters {
TestVectorParameters {
client_s_pk: decode(values, "client_s_pk").unwrap(),
@@ -521,37 +519,37 @@ where
<CS::KeyExchange as KeyExchange>::KE3Message: Serialize,
// ClientRegistration: KgSk + KgPk
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
ClientRegistrationLen<CS>: ArrayLength<u8>,
ClientRegistrationLen<CS>: ArrayLength,
// RegistrationResponse: KgPk + KePk
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
RegistrationResponseLen<CS>: ArrayLength<u8>,
RegistrationResponseLen<CS>: ArrayLength,
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
// CredentialRequest: KgPk + Ke1Message
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
CredentialRequestLen<CS>: ArrayLength,
// ClientLogin: KgSk + CredentialRequest + Ke1State
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
<CS::KeyExchange as KeyExchange>::KE1State: Serialize,
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength<u8>,
ArrayLength + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength,
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
use rand::RngCore;
use rand::Rng;
use crate::keypair::KeyPair;
@@ -588,20 +586,19 @@ where
let dummy_client_pk = dummy_client_pk.serialize();
let server_setup = ServerSetup::<CS>::deserialize(
&[
oprf_seed.as_ref(),
&server_s_kp.private().serialize(),
&dummy_client_pk,
oprf_seed.as_slice(),
server_s_kp.private().serialize().as_slice(),
dummy_client_pk.as_slice(),
]
.concat(),
)
.unwrap();
)?;
let blinding_factor = <OprfGroup<CS> as voprf::Group>::random_scalar(&mut rng);
let blinding_factor = <OprfGroup<CS> as voprf::Group>::random_scalar(&mut rng)?;
let blinding_factor_bytes = OprfGroup::<CS>::serialize_scalar(blinding_factor);
let mut blinding_factor_registration_rng = CycleRng::new(blinding_factor_bytes.to_vec());
let client_registration_start_result =
ClientRegistration::<CS>::start(&mut blinding_factor_registration_rng, password).unwrap();
ClientRegistration::<CS>::start(&mut blinding_factor_registration_rng, password)?;
let blinding_factor_bytes_returned = OprfGroup::<CS>::serialize_scalar(
client_registration_start_result
.state
@@ -620,8 +617,8 @@ where
&server_setup,
client_registration_start_result.message,
credential_identifier,
)
.unwrap();
)?;
let registration_response_bytes = server_registration_start_result.message.serialize();
let mut client_s_sk_and_nonce: Vec<u8> = Vec::new();
@@ -629,21 +626,18 @@ where
client_s_sk_and_nonce.extend_from_slice(&envelope_nonce);
let mut finish_registration_rng = CycleRng::new(client_s_sk_and_nonce);
let client_registration_finish_result = client_registration_start_result
.state
.finish(
&mut finish_registration_rng,
password,
server_registration_start_result.message,
ClientRegistrationFinishParameters::new(
Identifiers {
client: Some(id_u),
server: Some(id_s),
},
None,
),
)
.unwrap();
let client_registration_finish_result = client_registration_start_result.state.finish(
&mut finish_registration_rng,
password,
server_registration_start_result.message,
ClientRegistrationFinishParameters::new(
Identifiers {
client: Some(id_u),
server: Some(id_s),
},
None,
),
)?;
let registration_upload_bytes = client_registration_finish_result.message.serialize();
let password_file = ServerRegistration::finish(client_registration_finish_result.message);
@@ -656,7 +650,7 @@ where
let mut client_login_start_rng = CycleRng::new(client_login_start);
let client_login_start_result =
ClientLogin::<CS>::start(&mut client_login_start_rng, password).unwrap();
ClientLogin::<CS>::start(&mut client_login_start_rng, password)?;
let credential_request_bytes = client_login_start_result.message.serialize();
let client_login_state = client_login_start_result.state.serialize().to_vec();
@@ -683,27 +677,23 @@ where
server: Some(id_s),
},
},
)
.unwrap();
)?;
let credential_response_bytes = server_login_start_result.message.serialize();
let server_login_state = server_login_start_result.state.serialize();
let client_login_finish_result = client_login_start_result
.state
.finish(
&mut CycleRng::new(client_sig_rng.to_vec()),
password,
server_login_start_result.message,
ClientLoginFinishParameters::new(
Some(context),
Identifiers {
client: Some(id_u),
server: Some(id_s),
},
None,
),
)
.unwrap();
let client_login_finish_result = client_login_start_result.state.finish(
&mut CycleRng::new(client_sig_rng.to_vec()),
password,
server_login_start_result.message,
ClientLoginFinishParameters::new(
Some(context),
Identifiers {
client: Some(id_u),
server: Some(id_s),
},
None,
),
)?;
let credential_finalization_bytes = client_login_finish_result.message.serialize();
Ok(TestVectorParameters {
@@ -787,7 +777,7 @@ fn test_registration_request() -> Result<(), ProtocolError> {
where
// ClientRegistration: KgSk + KgPk
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<<OprfGroup<CS> as voprf::Group>::ElemLen>,
ClientRegistrationLen<CS>: ArrayLength<u8>,
ClientRegistrationLen<CS>: ArrayLength,
{
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
let mut rng = CycleRng::new(parameters.blinding_factor.to_vec());
@@ -821,15 +811,15 @@ fn test_serialization() -> Result<(), ProtocolError> {
let client_registration_start_result =
ClientRegistration::<CS>::start(&mut rng, &parameters.password)?;
// Test the bincode serialization (binary).
// Test the postcard serialization (binary).
let registration_request =
bincode::serialize(&client_registration_start_result.message).unwrap();
postcard::to_allocvec(&client_registration_start_result.message).unwrap();
assert_eq!(
registration_request.len(),
RegistrationRequestLen::<CS>::USIZE
);
let registration_request: RegistrationRequest<CS> =
bincode::deserialize(&registration_request).unwrap();
postcard::from_bytes(&registration_request).unwrap();
assert_eq!(
hex::encode(client_registration_start_result.message.serialize()),
hex::encode(registration_request.serialize()),
@@ -852,7 +842,7 @@ fn test_registration_response() -> Result<(), ProtocolError> {
where
// RegistrationResponse: KgPk + KePk
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
RegistrationResponseLen<CS>: ArrayLength<u8>,
RegistrationResponseLen<CS>: ArrayLength,
{
let parameters = populate_test_vectors(
&serde_json::from_str(test_vector).map_err(|_| ProtocolError::SerializationError)?,
@@ -894,8 +884,8 @@ fn test_registration_upload() -> Result<(), ProtocolError> {
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
{
let parameters = populate_test_vectors(
&serde_json::from_str(test_vector).map_err(|_| ProtocolError::SerializationError)?,
@@ -945,8 +935,8 @@ fn test_password_file() -> Result<(), ProtocolError> {
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
{
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
@@ -977,13 +967,13 @@ fn test_credential_request() -> Result<(), ProtocolError> {
// CredentialRequest: KgPk + Ke1Message
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
CredentialRequestLen<CS>: ArrayLength,
// ClientLogin: KgSk + CredentialRequest + Ke1State
<OprfGroup<CS> as voprf::Group>::ScalarLen: Add<CredentialRequestLen<CS>>,
<CS::KeyExchange as KeyExchange>::KE1State: Serialize,
Sum<<OprfGroup<CS> as voprf::Group>::ScalarLen, CredentialRequestLen<CS>>:
ArrayLength<u8> + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength<u8>,
ArrayLength + Add<Ke1StateLen<CS>>,
ClientLoginLen<CS>: ArrayLength,
{
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
@@ -1024,12 +1014,12 @@ fn test_credential_response() -> Result<(), ProtocolError> {
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
let parameters = populate_test_vectors(&serde_json::from_str(test_vector).unwrap());
@@ -1182,8 +1172,8 @@ fn test_complete_flow<CS: CipherSuite>(
login_password: &[u8],
) -> Result<(), ProtocolError> {
let credential_identifier = b"credentialIdentifier";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let mut client_rng = UnwrapErr(SysRng);
let mut server_rng = UnwrapErr(SysRng);
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<CS>::start(&mut client_rng, registration_password)?;
@@ -1330,8 +1320,8 @@ fn test_reflected_value_error_registration() -> Result<(), ProtocolError> {
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError> {
let credential_identifier = b"credentialIdentifier";
let password = b"password";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let mut client_rng = UnwrapErr(SysRng);
let mut server_rng = UnwrapErr(SysRng);
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<CS>::start(&mut client_rng, password)?;
@@ -1377,8 +1367,8 @@ fn test_reflected_value_error_login() -> Result<(), ProtocolError> {
fn inner<CS: CipherSuite>(_test_vector: &str) -> Result<(), ProtocolError> {
let credential_identifier = b"credentialIdentifier";
let password = b"password";
let mut client_rng = OsRng;
let mut server_rng = OsRng;
let mut client_rng = UnwrapErr(SysRng);
let mut server_rng = UnwrapErr(SysRng);
let server_setup = ServerSetup::<CS>::new(&mut server_rng);
let client_registration_start_result =
ClientRegistration::<CS>::start(&mut client_rng, password)?;
+24 -17
View File
@@ -9,9 +9,10 @@
use core::cmp::min;
use std::vec::Vec;
use rand::{CryptoRng, Error, RngCore};
use core::convert::Infallible;
use rand_core::{TryCryptoRng, TryRng};
/// A simple implementation of `RngCore` for testing purposes.
/// A simple implementation of `Rng` for testing purposes.
///
/// This generates a cyclic sequence (i.e. cycles over an initial buffer)
#[derive(Clone, Debug)]
@@ -38,29 +39,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
View File
@@ -6,6 +6,9 @@
// of this source tree. You may select, at your option, one of the above-listed
// licenses.
use serde_json::Value;
use std::vec::Vec;
mod full_test;
#[rustfmt::skip]
#[allow(dead_code)]
@@ -14,3 +17,7 @@ pub mod mock_rng;
mod parser;
mod rfc9807_vectors;
mod test_opaque_vectors;
pub(crate) fn decode(values: &Value, key: &str) -> Option<Vec<u8>> {
values[key].as_str().and_then(|s| hex::decode(s).ok())
}
+4 -8
View File
@@ -15,20 +15,18 @@ pub(crate) fn rfc_to_json(input: &str) -> String {
}
fn parse_vector_types(input: &str) -> String {
let re = regex::Regex::new(r" (?P<type>.+?) Test Vectors").unwrap();
let re = regex::Regex::new(r" {2}(?P<type>.+?) Test Vectors").unwrap();
let mut vector_types = 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 vector_type = format!(
"\"{}\": [\n {} \n]",
&caps["type"].trim(),
parse_ciphersuites(chunks[count])
);
vector_types.push(vector_type);
count += 1;
}
vector_types.join(",\n")
@@ -36,15 +34,14 @@ fn parse_vector_types(input: &str) -> String {
fn parse_ciphersuites(input: &str) -> String {
let re = regex::Regex::new(
r" Configuration\n(.|\n)*?OPRF: (?P<oprf>.*?)\n(.|\n)*?Group: (?P<group>.*?)\n",
r" Configuration\n([\s\S])*?OPRF: (?P<oprf>.*?)\n([\s\S])*?Group: (?P<group>.*?)\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["oprf"],
@@ -52,7 +49,6 @@ fn parse_ciphersuites(input: &str) -> String {
parse_params(chunks[count])
);
ciphersuites.push(ciphersuite);
count += 1;
}
ciphersuites.join(",\n")
+34 -35
View File
@@ -10,13 +10,6 @@ use core::ops::Add;
use std::vec;
use std::vec::Vec;
use digest::OutputSizeUser;
use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray};
use rand::RngCore;
use rand::rngs::OsRng;
use serde_json::Value;
use crate::ciphersuite::{CipherSuite, KeGroup, OprfGroup, OprfHash};
use crate::envelope::EnvelopeLen;
use crate::errors::*;
@@ -30,8 +23,16 @@ use crate::messages::{
RegistrationResponseLen, RegistrationUploadLen,
};
use crate::opaque::*;
use crate::tests::decode;
use crate::tests::mock_rng::CycleRng;
use crate::*;
use digest::OutputSizeUser;
use generic_array::typenum::Sum;
use generic_array::{ArrayLength, GenericArray};
use rand::Rng;
use rand::rngs::SysRng;
use rand_core::UnwrapErr;
use serde_json::Value;
#[allow(non_snake_case)]
#[derive(Debug)]
@@ -87,19 +88,15 @@ macro_rules! parse_default {
};
}
fn decode(values: &Value, key: &str) -> Option<Vec<u8>> {
values[key].as_str().and_then(|s| hex::decode(s).ok())
}
fn populate_test_vectors<CS: CipherSuite>(values: &Value) -> OpaqueTestVectorParameters {
let mut rng = OsRng;
let mut rng = UnwrapErr(SysRng);
OpaqueTestVectorParameters {
dummy_public_key: {
match decode(values, "client_public_key") {
Some(value) => value,
None => KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut OsRng)).to_vec(),
}
decode(values, "client_public_key").unwrap_or_else(|| {
KeGroup::<CS>::serialize_sk(&KeGroup::<CS>::random_sk(&mut UnwrapErr(SysRng)))
.to_vec()
})
},
dummy_masking_key: {
match decode(values, "masking_key") {
@@ -151,8 +148,8 @@ where
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
{
let password_file = ServerRegistration::<CS>::finish(
@@ -185,12 +182,14 @@ fn tests() -> Result<(), ProtocolError> {
serde_json::from_str(super::parser::rfc_to_json(super::rfc9807_vectors::VECTORS).as_str())
.expect("Could not parse json");
std::eprintln!("{}", serde_json::to_string_pretty(&rfc).unwrap());
#[cfg(feature = "ristretto255")]
{
struct Ristretto255Sha512NoKsf;
impl CipherSuite for Ristretto255Sha512NoKsf {
type OprfCs = crate::Ristretto255;
type KeyExchange = TripleDh<crate::Ristretto255, sha2::Sha512>;
type OprfCs = Ristretto255;
type KeyExchange = TripleDh<Ristretto255, sha2::Sha512>;
type Ksf = Identity;
}
@@ -331,7 +330,7 @@ fn test_registration_response<CS: CipherSuite>(
where
// RegistrationResponse: KgPk + KePk
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<<KeGroup<CS> as Group>::PkLen>,
RegistrationResponseLen<CS>: ArrayLength<u8>,
RegistrationResponseLen<CS>: ArrayLength,
{
for parameters in tvs {
let server_setup = ServerSetup::<CS>::deserialize(
@@ -370,8 +369,8 @@ where
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
{
for parameters in tvs {
let mut rng = CycleRng::new(parameters.blind_registration.to_vec());
@@ -417,7 +416,7 @@ where
// CredentialRequest: KgPk + Ke1Message
<CS::KeyExchange as KeyExchange>::KE1Message: Serialize,
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<Ke1MessageLen<CS>>,
CredentialRequestLen<CS>: ArrayLength<u8>,
CredentialRequestLen<CS>: ArrayLength,
{
for parameters in tvs {
let client_login_start = [
@@ -444,18 +443,18 @@ where
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
for parameters in tvs {
let server_setup = ServerSetup::<CS>::deserialize(
@@ -530,7 +529,7 @@ where
ClientLogin::<CS>::start(&mut client_login_start_rng, &parameters.password)?;
let client_login_finish_result = client_login_start_result.state.finish(
&mut OsRng,
&mut UnwrapErr(SysRng),
&parameters.password,
CredentialResponse::<CS>::deserialize(&parameters.KE2)?,
ClientLoginFinishParameters::new(
@@ -576,8 +575,8 @@ where
// RegistrationUpload: (KePk + Hash) + Envelope
<KeGroup<CS> as Group>::PkLen: Add<OutputSize<OprfHash<CS>>>,
Sum<<KeGroup<CS> as Group>::PkLen, OutputSize<OprfHash<CS>>>:
ArrayLength<u8> + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength<u8>,
ArrayLength + Add<EnvelopeLen<CS>>,
RegistrationUploadLen<CS>: ArrayLength,
// ServerRegistration = RegistrationUpload
{
for parameters in tvs {
@@ -644,12 +643,12 @@ where
// CredentialResponseWithoutKeLen: (KgPk + Nonce) + MaskedResponse
<OprfGroup<CS> as voprf::Group>::ElemLen: Add<NonceLen>,
Sum<<OprfGroup<CS> as voprf::Group>::ElemLen, NonceLen>:
ArrayLength<u8> + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength<u8>,
ArrayLength + Add<MaskedResponseLen<CS>>,
CredentialResponseWithoutKeLen<CS>: ArrayLength,
// CredentialResponse: CredentialResponseWithoutKeLen + Ke2Message
<CS::KeyExchange as KeyExchange>::KE2Message: Serialize,
CredentialResponseWithoutKeLen<CS>: Add<Ke2MessageLen<CS>>,
CredentialResponseLen<CS>: ArrayLength<u8>,
CredentialResponseLen<CS>: ArrayLength,
{
for parameters in tvs {
let server_setup = ServerSetup::<CS>::deserialize(
-112
View File
@@ -1,112 +0,0 @@
// 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::OutputSizeUser;
use generic_array::GenericArray;
use generic_array::sequence::{Concat, Split};
use generic_array::typenum::Sum;
use opaque_ke::ksf::Identity;
use opaque_ke::{CipherSuite, ClientLogin, ServerLogin, ServerRegistration, ServerSetup, TripleDh};
use opaque_ke_3::key_exchange::group::KeGroup as v3KeGroup;
use opaque_ke_3::key_exchange::tripledh::TripleDh as v3TripleDh;
use opaque_ke_3::keypair::KeyPair;
use opaque_ke_3::ksf::Identity as v3Identity;
use opaque_ke_3::{
CipherSuite as v3CipherSuite, ClientRegistration as v3ClientRegistration,
ServerRegistration as v3ServerRegistration, ServerSetup as v3ServerSetup,
};
use p256::NistP256;
use rand::rngs::OsRng;
use sha2::Sha256;
const PASSWORD: &[u8] = b"test password";
const CLIENT_IDENTIFIER: &[u8] = b"test client identifier";
struct OldCipherSuite;
impl v3CipherSuite for OldCipherSuite {
type OprfCs = NistP256;
type KeGroup = NistP256;
type KeyExchange = v3TripleDh;
type Ksf = v3Identity;
}
struct NewCipherSuite;
impl CipherSuite for NewCipherSuite {
type OprfCs = NistP256;
type KeyExchange = TripleDh<NistP256, Sha256>;
type Ksf = Identity;
}
#[test]
fn registration_upload() {
// V3 registration.
let result = v3ClientRegistration::<OldCipherSuite>::start(&mut OsRng, PASSWORD).unwrap();
let client = result.state;
let old_server_setup = v3ServerSetup::<OldCipherSuite>::new(&mut OsRng);
let response =
v3ServerRegistration::start(&old_server_setup, result.message, CLIENT_IDENTIFIER)
.unwrap()
.message;
let upload = client
.finish(&mut OsRng, PASSWORD, response, Default::default())
.unwrap()
.message;
let old_registration = v3ServerRegistration::finish(upload);
// `ServerSetup` migration.
let server_setup = {
let old_serialized = old_server_setup.serialize();
type OldSeedLen = <<<OldCipherSuite as v3CipherSuite>::OprfCs as voprf::CipherSuite>::Hash as OutputSizeUser>::OutputSize;
type OldSkLen = <<OldCipherSuite as v3CipherSuite>::KeGroup as v3KeGroup>::SkLen;
let (old_serialied_rest, old_fake_keypair_serialized): (
GenericArray<u8, Sum<OldSeedLen, OldSkLen>>,
_,
) = old_serialized.split();
let old_fake_keypair =
KeyPair::<<OldCipherSuite as v3CipherSuite>::KeGroup>::from_private_key_slice(
&old_fake_keypair_serialized,
)
.unwrap();
let old_fake_pk_serialized = old_fake_keypair.public().serialize();
let new_serialized = old_serialied_rest.concat(old_fake_pk_serialized);
ServerSetup::<NewCipherSuite>::deserialize(&new_serialized).unwrap()
};
// `ServerRegistration` migration.
let old_registration_serialized = old_registration.serialize();
let registration =
ServerRegistration::<NewCipherSuite>::deserialize(&old_registration_serialized).unwrap();
// Check if new `ServerRegistration` still works.
let result = ClientLogin::<NewCipherSuite>::start(&mut OsRng, PASSWORD).unwrap();
let client = result.state;
let result = ServerLogin::start(
&mut OsRng,
&server_setup,
Some(registration),
result.message,
CLIENT_IDENTIFIER,
Default::default(),
)
.unwrap();
let server = result.state;
let result = client
.finish(&mut OsRng, PASSWORD, result.message, Default::default())
.unwrap();
server.finish(result.message, Default::default()).unwrap();
}
+12 -12
View File
@@ -36,27 +36,27 @@ use elliptic_curve::sec1::{FromEncodedPoint, ModulusSize, Tag, ToEncodedPoint};
use elliptic_curve::{AffinePoint, CurveArithmetic, FieldBytesSize, Group as _, ProjectivePoint};
use generic_array::typenum::Unsigned;
use generic_array::{ArrayLength, GenericArray};
use opaque_ke::key_exchange::KeyExchange;
use opaque_ke::key_exchange::group::Group;
use opaque_vx::key_exchange::KeyExchange;
use opaque_vx::key_exchange::group::Group;
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
use opaque_ke::key_exchange::group::ed25519::{self, Ed25519};
use opaque_ke::key_exchange::group::elliptic_curve::NonIdentity;
use opaque_vx::key_exchange::group::ed25519::{self, Ed25519};
use opaque_vx::key_exchange::group::elliptic_curve::NonIdentity;
#[cfg(feature = "ecdsa")]
use opaque_ke::key_exchange::sigma_i::ecdsa::{self, Ecdsa, PreHash};
use opaque_vx::key_exchange::sigma_i::ecdsa::{self, Ecdsa, PreHash};
#[cfg(all(feature = "ristretto255", feature = "ed25519"))]
use opaque_ke::key_exchange::sigma_i::pure_eddsa::PureEddsa;
use opaque_vx::key_exchange::sigma_i::pure_eddsa::PureEddsa;
#[cfg(feature = "ecdsa")]
use opaque_ke::key_exchange::sigma_i::{CachedMessage, HashOutput, Message, SigmaI};
use opaque_ke::key_exchange::tripledh::TripleDh;
use opaque_ke::keypair::{KeyPair, PublicKey};
use opaque_ke::ksf::Identity;
use opaque_ke::{
use opaque_vx::key_exchange::sigma_i::{CachedMessage, HashOutput, Message, SigmaI};
use opaque_vx::key_exchange::tripledh::TripleDh;
use opaque_vx::keypair::{KeyPair, PublicKey};
use opaque_vx::ksf::Identity;
use opaque_vx::{
CipherSuite, ClientLogin, ClientLoginFinishParameters, ClientLoginStartResult,
ClientRegistration, ClientRegistrationFinishParameters, ClientRegistrationStartResult,
ServerLogin, ServerLoginParameters, ServerLoginStartResult, ServerRegistration, ServerSetup,
};
#[cfg(all(feature = "curve25519", feature = "ristretto255"))]
use opaque_ke::{Curve25519, Ristretto255};
use opaque_vx::{Curve25519, Ristretto255};
use p256::NistP256;
use p384::NistP384;
use p521::NistP521;