Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5313acd537 | |||
| 8c4cacb23c | |||
| cee27b1479 |
+403
-1
@@ -2,7 +2,409 @@
|
||||
|
||||
Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [2026.3-parmigiano] (2026-02-11)
|
||||
|
||||
- chore: disable LP on parmigiano branch ([#6422])
|
||||
- bugfix: revert faulty mixnet-based registration changes from LP ([#6420])
|
||||
- bugfix: registration client fallback handling for LP ([#6419])
|
||||
- bugfix: unify LP and Authenticator peer registration and IP allocation ([#6412])
|
||||
- bugfix: expose Wireguard PSK for vpn client ([#6411])
|
||||
- bugfix: make LP timeouts configurable ([#6409])
|
||||
- chore: include LP x25519 key in node description ([#6408])
|
||||
- bugfix: use Send-safe RNG ([#6404])
|
||||
- bugfix: use local KEM key instead of x25519 for KKT exchange ([#6402])
|
||||
- improve: LP gateway probe CLI and behavior ([#6400])
|
||||
- feature: negotiate and use LP protocol version ([#6399])
|
||||
- bugfix: use correct reserved bytes when parsing LpHeader ([#6398])
|
||||
- bugfix: share IP allocation across LP components ([#6395])
|
||||
- feature: hex-encode LP key digests ([#6394])
|
||||
- test: add socks5 test to gateway-probe ([#6393])
|
||||
- chore: refactor LP gateway probe file structure ([#6391])
|
||||
- chore: reduce HttpClientError size to satisfy clippy ([#6390])
|
||||
- feature: two-step dVPN registration via LP ([#6386])
|
||||
- chore: add extra configured nym api url to env ([#6382])
|
||||
- feature: inject dVPN PSK after LP registration ([#6378])
|
||||
- feature: include signing key digests in LP responses ([#6373])
|
||||
- feature: reuse Noise x25519 key for LP KTT ([#6372])
|
||||
- bugfix: topology fallback during epoch transitions ([#6363])
|
||||
- feature: NS API socks5 support ([#6361])
|
||||
- feature: dynamically select required KEM key hash for LP ([#6358])
|
||||
- bugfix: fix KKT integration in LP client-node flow ([#6357])
|
||||
- bugfix: LP mixnet registration fixes and packet kind tagging ([#6356])
|
||||
- feature: announce KEM key hashes on LP endpoints ([#6349])
|
||||
- revert: faulty drop changes ([#6346])
|
||||
- chore: small LP QoL and cleanup changes ([#6340])
|
||||
- feature: apply configured API URLs via env ([#6337])
|
||||
- chore: take reserved bytes directly from LP header ([#6336])
|
||||
- chore: x25519 / ed25519 cleanup in LP ([#6335])
|
||||
- feature: encrypted KKT support in LP ([#6331])
|
||||
- bugfix: reject packets with incompatible LP versions ([#6326])
|
||||
- refactor: standardise LP serialisation format ([#6324])
|
||||
- build(deps): upgrade def_guard_wireguard to v0.8.0 ([#6315])
|
||||
- chore: crates.io publishing preparation v2 ([#6270])
|
||||
|
||||
[#6422]: https://github.com/nymtech/nym/pull/6422
|
||||
[#6420]: https://github.com/nymtech/nym/pull/6420
|
||||
[#6419]: https://github.com/nymtech/nym/pull/6419
|
||||
[#6412]: https://github.com/nymtech/nym/pull/6412
|
||||
[#6411]: https://github.com/nymtech/nym/pull/6411
|
||||
[#6409]: https://github.com/nymtech/nym/pull/6409
|
||||
[#6408]: https://github.com/nymtech/nym/pull/6408
|
||||
[#6404]: https://github.com/nymtech/nym/pull/6404
|
||||
[#6402]: https://github.com/nymtech/nym/pull/6402
|
||||
[#6400]: https://github.com/nymtech/nym/pull/6400
|
||||
[#6399]: https://github.com/nymtech/nym/pull/6399
|
||||
[#6398]: https://github.com/nymtech/nym/pull/6398
|
||||
[#6395]: https://github.com/nymtech/nym/pull/6395
|
||||
[#6394]: https://github.com/nymtech/nym/pull/6394
|
||||
[#6393]: https://github.com/nymtech/nym/pull/6393
|
||||
[#6391]: https://github.com/nymtech/nym/pull/6391
|
||||
[#6390]: https://github.com/nymtech/nym/pull/6390
|
||||
[#6386]: https://github.com/nymtech/nym/pull/6386
|
||||
[#6382]: https://github.com/nymtech/nym/pull/6382
|
||||
[#6378]: https://github.com/nymtech/nym/pull/6378
|
||||
[#6373]: https://github.com/nymtech/nym/pull/6373
|
||||
[#6372]: https://github.com/nymtech/nym/pull/6372
|
||||
[#6363]: https://github.com/nymtech/nym/pull/6363
|
||||
[#6361]: https://github.com/nymtech/nym/pull/6361
|
||||
[#6358]: https://github.com/nymtech/nym/pull/6358
|
||||
[#6357]: https://github.com/nymtech/nym/pull/6357
|
||||
[#6356]: https://github.com/nymtech/nym/pull/6356
|
||||
[#6349]: https://github.com/nymtech/nym/pull/6349
|
||||
[#6346]: https://github.com/nymtech/nym/pull/6346
|
||||
[#6340]: https://github.com/nymtech/nym/pull/6340
|
||||
[#6337]: https://github.com/nymtech/nym/pull/6337
|
||||
[#6336]: https://github.com/nymtech/nym/pull/6336
|
||||
[#6335]: https://github.com/nymtech/nym/pull/6335
|
||||
[#6331]: https://github.com/nymtech/nym/pull/6331
|
||||
[#6326]: https://github.com/nymtech/nym/pull/6326
|
||||
[#6324]: https://github.com/nymtech/nym/pull/6324
|
||||
[#6315]: https://github.com/nymtech/nym/pull/6315
|
||||
[#6270]: https://github.com/nymtech/nym/pull/6270
|
||||
|
||||
## [2026.2-oscypek] (2026-01-27)
|
||||
|
||||
- bugfix: downgrade gateway protocol to clients proposed version ([#6377])
|
||||
- bugfix: ack fix ([#6364])
|
||||
- Cherry pick/api urls oscypek ([#6348])
|
||||
- Update nix to v0.30.1 ([#6316])
|
||||
- Deriving Serialize for GatewayData ([#6314])
|
||||
- chore: remove repetitive words in comment ([#6313])
|
||||
- [bugfix] Sqlite transaction escalation was causing errors ([#6299])
|
||||
- DNS static table pre-resolve ([#6297])
|
||||
- Add Copy+Clone to nym_api_provider::Config ([#6296])
|
||||
- [chore] clippy fixes and use fixed rust version from REQUIRED_RUSTC_VERSION ([#6295])
|
||||
- build(deps): bump SonarSource/sonarqube-scan-action from 6 to 7 ([#6294])
|
||||
- build(deps): bump mikefarah/yq from 4.49.2 to 4.50.1 ([#6293])
|
||||
- build(deps): bump actions/upload-artifact from 5 to 6 ([#6292])
|
||||
- build(deps): bump actions/download-artifact from 6 to 7 ([#6291])
|
||||
- build(deps): bump js-yaml from 3.14.1 to 3.14.2 in /documentation/docs ([#6290])
|
||||
- build(deps): bump next from 15.4.9 to 15.4.10 in /nym-node-status-api/nym-node-status-ui ([#6289])
|
||||
- build(deps): bump next from 14.2.33 to 14.2.35 ([#6288])
|
||||
- LP Registration + Telescoping + Gateway Probe Localnet Mode ([#6286])
|
||||
- build(deps): bump next from 15.5.7 to 15.5.9 in /documentation/docs ([#6285])
|
||||
- build(deps): bump next from 15.4.7 to 15.4.9 in /nym-node-status-api/nym-node-status-ui ([#6284])
|
||||
- Minor DNS improvements ([#6283])
|
||||
- HTTP client without default features ([#6281])
|
||||
- DNS: reduce number of attempts ([#6278])
|
||||
- [bugfix] use proper mixing delay instead of poisson delay in cover traffic ([#6269])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.3 in /wasm/zknym-lib/internal-dev ([#6261])
|
||||
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.3 in /wasm/mix-fetch/internal-dev ([#6260])
|
||||
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.2 in /wasm/client/internal-dev ([#6251])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.2 in /nym-credential-proxy/vpn-api-lib-wasm/internal-dev ([#6250])
|
||||
- [Feature] Fallback gateway listener and remove legacy key support ([#6249])
|
||||
- build(deps-dev): bump node-forge from 1.3.0 to 1.3.2 in /clients/native/examples/js-examples/websocket ([#6248])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.2 ([#6246])
|
||||
- build(deps): bump pnpm/action-setup from 4.1.0 to 4.2.0 ([#6245])
|
||||
- build(deps): bump actions/download-artifact from 5 to 6 ([#6244])
|
||||
- build(deps): bump actions/checkout from 4 to 6 ([#6243])
|
||||
- build(deps): bump mikefarah/yq from 4.48.1 to 4.49.2 ([#6242])
|
||||
- build(deps): bump actions/upload-artifact from 4 to 5 ([#6241])
|
||||
- fix: fix assertion ([#6238])
|
||||
- Initial changes to support extra configurable parameters and to print… ([#6237])
|
||||
- Data Observatory ([#6172])
|
||||
|
||||
[#6377]: https://github.com/nymtech/nym/pull/6377
|
||||
[#6364]: https://github.com/nymtech/nym/pull/6364
|
||||
[#6348]: https://github.com/nymtech/nym/pull/6348
|
||||
[#6316]: https://github.com/nymtech/nym/pull/6316
|
||||
[#6314]: https://github.com/nymtech/nym/pull/6314
|
||||
[#6313]: https://github.com/nymtech/nym/pull/6313
|
||||
[#6299]: https://github.com/nymtech/nym/pull/6299
|
||||
[#6297]: https://github.com/nymtech/nym/pull/6297
|
||||
[#6296]: https://github.com/nymtech/nym/pull/6296
|
||||
[#6295]: https://github.com/nymtech/nym/pull/6295
|
||||
[#6294]: https://github.com/nymtech/nym/pull/6294
|
||||
[#6293]: https://github.com/nymtech/nym/pull/6293
|
||||
[#6292]: https://github.com/nymtech/nym/pull/6292
|
||||
[#6291]: https://github.com/nymtech/nym/pull/6291
|
||||
[#6290]: https://github.com/nymtech/nym/pull/6290
|
||||
[#6289]: https://github.com/nymtech/nym/pull/6289
|
||||
[#6288]: https://github.com/nymtech/nym/pull/6288
|
||||
[#6286]: https://github.com/nymtech/nym/pull/6286
|
||||
[#6285]: https://github.com/nymtech/nym/pull/6285
|
||||
[#6284]: https://github.com/nymtech/nym/pull/6284
|
||||
[#6283]: https://github.com/nymtech/nym/pull/6283
|
||||
[#6281]: https://github.com/nymtech/nym/pull/6281
|
||||
[#6278]: https://github.com/nymtech/nym/pull/6278
|
||||
[#6269]: https://github.com/nymtech/nym/pull/6269
|
||||
[#6261]: https://github.com/nymtech/nym/pull/6261
|
||||
[#6260]: https://github.com/nymtech/nym/pull/6260
|
||||
[#6251]: https://github.com/nymtech/nym/pull/6251
|
||||
[#6250]: https://github.com/nymtech/nym/pull/6250
|
||||
[#6249]: https://github.com/nymtech/nym/pull/6249
|
||||
[#6248]: https://github.com/nymtech/nym/pull/6248
|
||||
[#6246]: https://github.com/nymtech/nym/pull/6246
|
||||
[#6245]: https://github.com/nymtech/nym/pull/6245
|
||||
[#6244]: https://github.com/nymtech/nym/pull/6244
|
||||
[#6243]: https://github.com/nymtech/nym/pull/6243
|
||||
[#6242]: https://github.com/nymtech/nym/pull/6242
|
||||
[#6241]: https://github.com/nymtech/nym/pull/6241
|
||||
[#6238]: https://github.com/nymtech/nym/pull/6238
|
||||
[#6237]: https://github.com/nymtech/nym/pull/6237
|
||||
[#6172]: https://github.com/nymtech/nym/pull/6172
|
||||
|
||||
## [2026.2-oscypek] (2026-01-27)
|
||||
|
||||
- bugfix: downgrade gateway protocol to clients proposed version ([#6377])
|
||||
- bugfix: ack fix ([#6364])
|
||||
- Cherry pick/api urls oscypek ([#6348])
|
||||
- Update nix to v0.30.1 ([#6316])
|
||||
- Deriving Serialize for GatewayData ([#6314])
|
||||
- chore: remove repetitive words in comment ([#6313])
|
||||
- [bugfix] Sqlite transaction escalation was causing errors ([#6299])
|
||||
- DNS static table pre-resolve ([#6297])
|
||||
- Add Copy+Clone to nym_api_provider::Config ([#6296])
|
||||
- [chore] clippy fixes and use fixed rust version from REQUIRED_RUSTC_VERSION ([#6295])
|
||||
- build(deps): bump SonarSource/sonarqube-scan-action from 6 to 7 ([#6294])
|
||||
- build(deps): bump mikefarah/yq from 4.49.2 to 4.50.1 ([#6293])
|
||||
- build(deps): bump actions/upload-artifact from 5 to 6 ([#6292])
|
||||
- build(deps): bump actions/download-artifact from 6 to 7 ([#6291])
|
||||
- build(deps): bump js-yaml from 3.14.1 to 3.14.2 in /documentation/docs ([#6290])
|
||||
- build(deps): bump next from 15.4.9 to 15.4.10 in /nym-node-status-api/nym-node-status-ui ([#6289])
|
||||
- build(deps): bump next from 14.2.33 to 14.2.35 ([#6288])
|
||||
- LP Registration + Telescoping + Gateway Probe Localnet Mode ([#6286])
|
||||
- build(deps): bump next from 15.5.7 to 15.5.9 in /documentation/docs ([#6285])
|
||||
- build(deps): bump next from 15.4.7 to 15.4.9 in /nym-node-status-api/nym-node-status-ui ([#6284])
|
||||
- Minor DNS improvements ([#6283])
|
||||
- HTTP client without default features ([#6281])
|
||||
- DNS: reduce number of attempts ([#6278])
|
||||
- [bugfix] use proper mixing delay instead of poisson delay in cover traffic ([#6269])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.3 in /wasm/zknym-lib/internal-dev ([#6261])
|
||||
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.3 in /wasm/mix-fetch/internal-dev ([#6260])
|
||||
- build(deps-dev): bump node-forge from 1.3.1 to 1.3.2 in /wasm/client/internal-dev ([#6251])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.2 in /nym-credential-proxy/vpn-api-lib-wasm/internal-dev ([#6250])
|
||||
- [Feature] Fallback gateway listener and remove legacy key support ([#6249])
|
||||
- build(deps-dev): bump node-forge from 1.3.0 to 1.3.2 in /clients/native/examples/js-examples/websocket ([#6248])
|
||||
- build(deps): bump node-forge from 1.3.1 to 1.3.2 ([#6246])
|
||||
- build(deps): bump pnpm/action-setup from 4.1.0 to 4.2.0 ([#6245])
|
||||
- build(deps): bump actions/download-artifact from 5 to 6 ([#6244])
|
||||
- build(deps): bump actions/checkout from 4 to 6 ([#6243])
|
||||
- build(deps): bump mikefarah/yq from 4.48.1 to 4.49.2 ([#6242])
|
||||
- build(deps): bump actions/upload-artifact from 4 to 5 ([#6241])
|
||||
- fix: fix assertion ([#6238])
|
||||
- Initial changes to support extra configurable parameters and to print… ([#6237])
|
||||
- Data Observatory ([#6172])
|
||||
|
||||
[#6377]: https://github.com/nymtech/nym/pull/6377
|
||||
[#6364]: https://github.com/nymtech/nym/pull/6364
|
||||
[#6348]: https://github.com/nymtech/nym/pull/6348
|
||||
[#6316]: https://github.com/nymtech/nym/pull/6316
|
||||
[#6314]: https://github.com/nymtech/nym/pull/6314
|
||||
[#6313]: https://github.com/nymtech/nym/pull/6313
|
||||
[#6299]: https://github.com/nymtech/nym/pull/6299
|
||||
[#6297]: https://github.com/nymtech/nym/pull/6297
|
||||
[#6296]: https://github.com/nymtech/nym/pull/6296
|
||||
[#6295]: https://github.com/nymtech/nym/pull/6295
|
||||
[#6294]: https://github.com/nymtech/nym/pull/6294
|
||||
[#6293]: https://github.com/nymtech/nym/pull/6293
|
||||
[#6292]: https://github.com/nymtech/nym/pull/6292
|
||||
[#6291]: https://github.com/nymtech/nym/pull/6291
|
||||
[#6290]: https://github.com/nymtech/nym/pull/6290
|
||||
[#6289]: https://github.com/nymtech/nym/pull/6289
|
||||
[#6288]: https://github.com/nymtech/nym/pull/6288
|
||||
[#6286]: https://github.com/nymtech/nym/pull/6286
|
||||
[#6285]: https://github.com/nymtech/nym/pull/6285
|
||||
[#6284]: https://github.com/nymtech/nym/pull/6284
|
||||
[#6283]: https://github.com/nymtech/nym/pull/6283
|
||||
[#6281]: https://github.com/nymtech/nym/pull/6281
|
||||
[#6278]: https://github.com/nymtech/nym/pull/6278
|
||||
[#6269]: https://github.com/nymtech/nym/pull/6269
|
||||
[#6261]: https://github.com/nymtech/nym/pull/6261
|
||||
[#6260]: https://github.com/nymtech/nym/pull/6260
|
||||
[#6251]: https://github.com/nymtech/nym/pull/6251
|
||||
[#6250]: https://github.com/nymtech/nym/pull/6250
|
||||
[#6249]: https://github.com/nymtech/nym/pull/6249
|
||||
[#6248]: https://github.com/nymtech/nym/pull/6248
|
||||
[#6246]: https://github.com/nymtech/nym/pull/6246
|
||||
[#6245]: https://github.com/nymtech/nym/pull/6245
|
||||
[#6244]: https://github.com/nymtech/nym/pull/6244
|
||||
[#6243]: https://github.com/nymtech/nym/pull/6243
|
||||
[#6242]: https://github.com/nymtech/nym/pull/6242
|
||||
[#6241]: https://github.com/nymtech/nym/pull/6241
|
||||
[#6238]: https://github.com/nymtech/nym/pull/6238
|
||||
[#6237]: https://github.com/nymtech/nym/pull/6237
|
||||
[#6172]: https://github.com/nymtech/nym/pull/6172
|
||||
|
||||
## [2026.1-niolo] (2026-01-13)
|
||||
|
||||
- bugfix: mozzarella -> niolo config migration ([#6259])
|
||||
- chore: remove run DKG migration ([#6253])
|
||||
- bugfix: reexposed 'derive_extended_private_key' ([#6247])
|
||||
- Bump js-yaml from 3.14.1 to 3.14.2 in /sdk/typescript/codegen/contract-clients ([#6231])
|
||||
- Statistics API v2 ([#6227])
|
||||
- Bump golang.org/x/crypto from 0.39.0 to 0.45.0 in /nym-gateway-probe/netstack_ping ([#6220])
|
||||
- Update chain registry link ([#6219])
|
||||
- Bump glob from 10.3.4 to 10.5.0 in /documentation/scripts/post-process ([#6216])
|
||||
- Bump js-yaml from 4.1.0 to 4.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6215])
|
||||
- gateway-probe fixes for run-local ([#6212])
|
||||
- chore: updated default endpoint for retrieving attestation.json ([#6207])
|
||||
- chore: remove support for legacy mixnode within the performance contract ([#6205])
|
||||
- feat: upgrade mode: VPN adjustments ([#6189])
|
||||
- Bump min-document from 2.19.0 to 2.19.1 ([#6181])
|
||||
- Bump next from 15.4.1 to 15.4.7 in /nym-node-status-api/nym-node-status-ui ([#6180])
|
||||
- feat: merge intermediate upgrade mode changes ([#6174])
|
||||
- Add weighted scoring to NS API ([#6144])
|
||||
- build(deps): bump mikefarah/yq from 4.47.1 to 4.48.1 ([#6107])
|
||||
- build(deps): bump SonarSource/sonarqube-scan-action from 5 to 6 in /.github/workflows ([#6068])
|
||||
- build(deps): bump tar-fs from 3.0.9 to 3.1.1 in /sdk/typescript/tests/integration-tests/mix-fetch ([#6063])
|
||||
- build(deps): bump ammonia from 4.1.1 to 4.1.2 ([#6057])
|
||||
- build(deps): bump tower-http from 0.5.2 to 0.6.6 ([#6030])
|
||||
- build(deps): bump actions/setup-go from 5 to 6 ([#6013])
|
||||
- build(deps): bump next from 14.2.28 to 14.2.32 ([#5996])
|
||||
- build(deps): bump tracing-subscriber from 0.3.19 to 0.3.20 ([#5993])
|
||||
- build(deps): bump actions/upload-pages-artifact from 3 to 4 ([#5992])
|
||||
|
||||
[#6259]: https://github.com/nymtech/nym/pull/6259
|
||||
[#6253]: https://github.com/nymtech/nym/pull/6253
|
||||
[#6247]: https://github.com/nymtech/nym/pull/6247
|
||||
[#6231]: https://github.com/nymtech/nym/pull/6231
|
||||
[#6227]: https://github.com/nymtech/nym/pull/6227
|
||||
[#6220]: https://github.com/nymtech/nym/pull/6220
|
||||
[#6219]: https://github.com/nymtech/nym/pull/6219
|
||||
[#6216]: https://github.com/nymtech/nym/pull/6216
|
||||
[#6215]: https://github.com/nymtech/nym/pull/6215
|
||||
[#6212]: https://github.com/nymtech/nym/pull/6212
|
||||
[#6207]: https://github.com/nymtech/nym/pull/6207
|
||||
[#6205]: https://github.com/nymtech/nym/pull/6205
|
||||
[#6189]: https://github.com/nymtech/nym/pull/6189
|
||||
[#6181]: https://github.com/nymtech/nym/pull/6181
|
||||
[#6180]: https://github.com/nymtech/nym/pull/6180
|
||||
[#6174]: https://github.com/nymtech/nym/pull/6174
|
||||
[#6144]: https://github.com/nymtech/nym/pull/6144
|
||||
[#6107]: https://github.com/nymtech/nym/pull/6107
|
||||
[#6068]: https://github.com/nymtech/nym/pull/6068
|
||||
[#6063]: https://github.com/nymtech/nym/pull/6063
|
||||
[#6057]: https://github.com/nymtech/nym/pull/6057
|
||||
[#6030]: https://github.com/nymtech/nym/pull/6030
|
||||
[#6013]: https://github.com/nymtech/nym/pull/6013
|
||||
[#5996]: https://github.com/nymtech/nym/pull/5996
|
||||
[#5993]: https://github.com/nymtech/nym/pull/5993
|
||||
[#5992]: https://github.com/nymtech/nym/pull/5992
|
||||
|
||||
## [2025.21-mozzarella] (2025-11-25)
|
||||
|
||||
- [bugfix] Tunnel not waiting on MixnetClient to shut down cleanly ([#6225])
|
||||
- bugfix: fix credential proxy upgrade mode attestation url arg ([#6202])
|
||||
- HTTP API resilience enable & domain rotation conditions ([#6200])
|
||||
- Remove debug feature from http-macro spec in gateway probe ([#6195])
|
||||
- DNS relibility and troubleshooting ([#6179])
|
||||
- [bugfix] Distinguish authenticator errors by credential spent ([#6176])
|
||||
- Typescript SDK 1.4.1 ([#6146])
|
||||
- Enable URL rotation and retries for mixnet gateway init ([#6126])
|
||||
- Feature/credential proxy jwt ([#5957])
|
||||
|
||||
[#6225]: https://github.com/nymtech/nym/pull/6225
|
||||
[#6202]: https://github.com/nymtech/nym/pull/6202
|
||||
[#6200]: https://github.com/nymtech/nym/pull/6200
|
||||
[#6195]: https://github.com/nymtech/nym/pull/6195
|
||||
[#6179]: https://github.com/nymtech/nym/pull/6179
|
||||
[#6176]: https://github.com/nymtech/nym/pull/6176
|
||||
[#6146]: https://github.com/nymtech/nym/pull/6146
|
||||
[#6126]: https://github.com/nymtech/nym/pull/6126
|
||||
[#5957]: https://github.com/nymtech/nym/pull/5957
|
||||
|
||||
## [2025.20-leerdammer] (2025-11-12)
|
||||
|
||||
## [2026.3-parmigiano] (2026-02-11)
|
||||
|
||||
- chore: disable LP on parmigiano branch ([#6422])
|
||||
- bugfix: revert faulty mixnet-based registration changes from LP ([#6420])
|
||||
- bugfix: registration client fallback handling for LP ([#6419])
|
||||
- bugfix: unify LP and Authenticator peer registration and IP allocation ([#6412])
|
||||
- bugfix: expose Wireguard PSK for vpn client ([#6411])
|
||||
- bugfix: make LP timeouts configurable ([#6409])
|
||||
- chore: include LP x25519 key in node description ([#6408])
|
||||
- bugfix: use Send-safe RNG ([#6404])
|
||||
- bugfix: use local KEM key instead of x25519 for KKT exchange ([#6402])
|
||||
- improve: LP gateway probe CLI and behavior ([#6400])
|
||||
- feature: negotiate and use LP protocol version ([#6399])
|
||||
- bugfix: use correct reserved bytes when parsing LpHeader ([#6398])
|
||||
- bugfix: share IP allocation across LP components ([#6395])
|
||||
- feature: hex-encode LP key digests ([#6394])
|
||||
- test: add socks5 test to gateway-probe ([#6393])
|
||||
- chore: refactor LP gateway probe file structure ([#6391])
|
||||
- chore: reduce HttpClientError size to satisfy clippy ([#6390])
|
||||
- feature: two-step dVPN registration via LP ([#6386])
|
||||
- chore: add extra configured nym api url to env ([#6382])
|
||||
- feature: inject dVPN PSK after LP registration ([#6378])
|
||||
- feature: include signing key digests in LP responses ([#6373])
|
||||
- feature: reuse Noise x25519 key for LP KTT ([#6372])
|
||||
- bugfix: topology fallback during epoch transitions ([#6363])
|
||||
- feature: NS API socks5 support ([#6361])
|
||||
- feature: dynamically select required KEM key hash for LP ([#6358])
|
||||
- bugfix: fix KKT integration in LP client-node flow ([#6357])
|
||||
- bugfix: LP mixnet registration fixes and packet kind tagging ([#6356])
|
||||
- feature: announce KEM key hashes on LP endpoints ([#6349])
|
||||
- revert: faulty drop changes ([#6346])
|
||||
- chore: small LP QoL and cleanup changes ([#6340])
|
||||
- feature: apply configured API URLs via env ([#6337])
|
||||
- chore: take reserved bytes directly from LP header ([#6336])
|
||||
- chore: x25519 / ed25519 cleanup in LP ([#6335])
|
||||
- feature: encrypted KKT support in LP ([#6331])
|
||||
- bugfix: reject packets with incompatible LP versions ([#6326])
|
||||
- refactor: standardise LP serialisation format ([#6324])
|
||||
- build(deps): upgrade def_guard_wireguard to v0.8.0 ([#6315])
|
||||
- chore: crates.io publishing preparation v2 ([#6270])
|
||||
|
||||
[#6422]: https://github.com/nymtech/nym/pull/6422
|
||||
[#6420]: https://github.com/nymtech/nym/pull/6420
|
||||
[#6419]: https://github.com/nymtech/nym/pull/6419
|
||||
[#6412]: https://github.com/nymtech/nym/pull/6412
|
||||
[#6411]: https://github.com/nymtech/nym/pull/6411
|
||||
[#6409]: https://github.com/nymtech/nym/pull/6409
|
||||
[#6408]: https://github.com/nymtech/nym/pull/6408
|
||||
[#6404]: https://github.com/nymtech/nym/pull/6404
|
||||
[#6402]: https://github.com/nymtech/nym/pull/6402
|
||||
[#6400]: https://github.com/nymtech/nym/pull/6400
|
||||
[#6399]: https://github.com/nymtech/nym/pull/6399
|
||||
[#6398]: https://github.com/nymtech/nym/pull/6398
|
||||
[#6395]: https://github.com/nymtech/nym/pull/6395
|
||||
[#6394]: https://github.com/nymtech/nym/pull/6394
|
||||
[#6393]: https://github.com/nymtech/nym/pull/6393
|
||||
[#6391]: https://github.com/nymtech/nym/pull/6391
|
||||
[#6390]: https://github.com/nymtech/nym/pull/6390
|
||||
[#6386]: https://github.com/nymtech/nym/pull/6386
|
||||
[#6382]: https://github.com/nymtech/nym/pull/6382
|
||||
[#6378]: https://github.com/nymtech/nym/pull/6378
|
||||
[#6373]: https://github.com/nymtech/nym/pull/6373
|
||||
[#6372]: https://github.com/nymtech/nym/pull/6372
|
||||
[#6363]: https://github.com/nymtech/nym/pull/6363
|
||||
[#6361]: https://github.com/nymtech/nym/pull/6361
|
||||
[#6358]: https://github.com/nymtech/nym/pull/6358
|
||||
[#6357]: https://github.com/nymtech/nym/pull/6357
|
||||
[#6356]: https://github.com/nymtech/nym/pull/6356
|
||||
[#6349]: https://github.com/nymtech/nym/pull/6349
|
||||
[#6346]: https://github.com/nymtech/nym/pull/6346
|
||||
[#6340]: https://github.com/nymtech/nym/pull/6340
|
||||
[#6337]: https://github.com/nymtech/nym/pull/6337
|
||||
[#6336]: https://github.com/nymtech/nym/pull/6336
|
||||
[#6335]: https://github.com/nymtech/nym/pull/6335
|
||||
[#6331]: https://github.com/nymtech/nym/pull/6331
|
||||
[#6326]: https://github.com/nymtech/nym/pull/6326
|
||||
[#6324]: https://github.com/nymtech/nym/pull/6324
|
||||
[#6315]: https://github.com/nymtech/nym/pull/6315
|
||||
[#6270]: https://github.com/nymtech/nym/pull/6270
|
||||
|
||||
## [2026.2-oscypek] (2026-01-27)
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -5417,7 +5417,6 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-signer-check-types",
|
||||
"nym-ecash-time",
|
||||
"nym-kkt-ciphersuite",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-network-defaults",
|
||||
"nym-node-requests",
|
||||
@@ -6475,7 +6474,6 @@ dependencies = [
|
||||
"nym-http-api-client-macro",
|
||||
"nym-ip-packet-client",
|
||||
"nym-ip-packet-requests",
|
||||
"nym-kkt-ciphersuite",
|
||||
"nym-lp",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-network-defaults",
|
||||
@@ -7525,6 +7523,8 @@ dependencies = [
|
||||
"nym-test-utils",
|
||||
"nym-wireguard-types",
|
||||
"serde",
|
||||
"time",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+3
-4
@@ -432,7 +432,6 @@ nym-http-api-client = { version = "1.20.1", path = "common/http-api-client" }
|
||||
nym-http-api-client-macro = { version = "1.20.1", path = "common/http-api-client-macro" }
|
||||
nym-http-api-common = { version = "1.20.1", path = "common/http-api-common", default-features = false }
|
||||
nym-id = { version = "1.20.1", path = "common/nym-id" }
|
||||
nym-kkt-ciphersuite = { path = "common/nym-kkt-ciphersuite" }
|
||||
nym-ip-packet-client = { version = "1.20.1", path = "nym-ip-packet-client" }
|
||||
nym-ip-packet-requests = { version = "1.20.1", path = "common/ip-packet-requests" }
|
||||
nym-metrics = { version = "1.20.1", path = "common/nym-metrics" }
|
||||
@@ -483,9 +482,9 @@ nym-vesting-contract-common = { version = "1.20.1", path = "common/cosmwasm-smar
|
||||
nym-verloc = { version = "1.20.1", path = "common/verloc" }
|
||||
nym-wireguard = { version = "1.20.1", path = "common/wireguard" }
|
||||
nym-wireguard-types = { version = "1.20.1", path = "common/wireguard-types" }
|
||||
nym-wireguard-private-metadata-shared = { version = "1.20.1", path = "common/wireguard-private-metadata/shared" }
|
||||
nym-wireguard-private-metadata-client = { version = "1.20.1", path = "common/wireguard-private-metadata/client" }
|
||||
nym-wireguard-private-metadata-server = { version = "1.20.1", path = "common/wireguard-private-metadata/server" }
|
||||
nym-wireguard-private-metadata-shared = { version = "1.20.1", path = "common/wireguard-private-metadata/shared" }
|
||||
nym-wireguard-private-metadata-client = { version = "1.20.1", path = "common/wireguard-private-metadata/client" }
|
||||
nym-wireguard-private-metadata-server = { version = "1.20.1", path = "common/wireguard-private-metadata/server" }
|
||||
nym-sqlx-pool-guard = { version = "1.2.0", path = "nym-sqlx-pool-guard" }
|
||||
nym-wasm-client-core = { version = "1.20.1", path = "common/wasm/client-core" }
|
||||
nym-wasm-storage = { version = "1.20.1", path = "common/wasm/storage" }
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/*
|
||||
* Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
ALTER TABLE wireguard_peer
|
||||
ADD COLUMN psk VARCHAR;
|
||||
@@ -577,21 +577,4 @@ impl BandwidthGatewayStorage for GatewayStorage {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the stored PSK of the wireguard peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: the unique public key of the wireguard peer.
|
||||
/// * `psk`: the PSK of the wireguard peer.
|
||||
async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), GatewayStorageError> {
|
||||
self.wireguard_peer_manager
|
||||
.update_peer_psk(public_key, psk)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{error::GatewayStorageError, make_bincode_serializer};
|
||||
use defguard_wireguard_rs::key::Key;
|
||||
use nym_credentials_interface::{AvailableBandwidth, ClientTicket, CredentialSpendingData};
|
||||
use nym_gateway_requests::shared_key::SharedSymmetricKey;
|
||||
use sqlx::FromRow;
|
||||
@@ -96,7 +95,6 @@ pub struct WireguardPeer {
|
||||
pub public_key: String,
|
||||
pub allowed_ips: Vec<u8>,
|
||||
pub client_id: i64,
|
||||
pub psk: Option<String>,
|
||||
}
|
||||
|
||||
impl WireguardPeer {
|
||||
@@ -112,7 +110,6 @@ impl WireguardPeer {
|
||||
source,
|
||||
})?,
|
||||
client_id,
|
||||
psk: value.preshared_key.map(|psk| psk.to_lower_hex()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -135,11 +132,6 @@ impl TryFrom<WireguardPeer> for defguard_wireguard_rs::host::Peer {
|
||||
field_key: "allowed_ips",
|
||||
source,
|
||||
})?,
|
||||
preshared_key: value
|
||||
.psk
|
||||
.map(|psk| Key::decode(&psk))
|
||||
.transpose()
|
||||
.map_err(|_| Self::Error::TypeConversion { field_key: "psk" })?,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,18 +170,6 @@ pub trait BandwidthGatewayStorage: dyn_clone::DynClone {
|
||||
/// * `peer_public_key`: wireguard public key of the peer to be removed.
|
||||
async fn remove_wireguard_peer(&self, peer_public_key: &str)
|
||||
-> Result<(), GatewayStorageError>;
|
||||
|
||||
/// Update the stored PSK of the wireguard peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: the unique public key of the wireguard peer.
|
||||
/// * `psk`: the PSK of the wireguard peer.
|
||||
async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), GatewayStorageError>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
@@ -519,16 +507,5 @@ pub mod mock {
|
||||
self.write().await.wireguard_peers.remove(peer_public_key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), GatewayStorageError> {
|
||||
if let Some(peer) = self.write().await.wireguard_peers.get_mut(public_key) {
|
||||
peer.psk = psk.map(|psk| psk.to_owned())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,29 +98,4 @@ impl WgPeerManager {
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update the stored PSK of the wireguard peer.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: the unique public key of the wireguard peer.
|
||||
/// * `psk`: the PSK of the wireguard peer.
|
||||
pub(crate) async fn update_peer_psk(
|
||||
&self,
|
||||
public_key: &str,
|
||||
psk: Option<&str>,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE wireguard_peer
|
||||
SET psk = ?
|
||||
WHERE public_key = ?
|
||||
"#,
|
||||
psk,
|
||||
public_key,
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::error::KKTCiphersuiteError;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Display;
|
||||
use strum_macros::{Display, EnumIter, EnumString};
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
pub mod error;
|
||||
|
||||
@@ -45,31 +45,14 @@ pub mod xwing {
|
||||
pub const PUBLIC_KEY_LENGTH: usize = x25519::PUBLIC_KEY_LENGTH + ml_kem768::PUBLIC_KEY_LENGTH;
|
||||
}
|
||||
|
||||
pub type KEMKeyDigests = KeyDigests;
|
||||
pub type SigningKeyDigests = KeyDigests;
|
||||
pub type KEMKeyDigests = HashMap<HashFunction, Vec<u8>>;
|
||||
|
||||
pub type KeyDigests = HashMap<HashFunction, Vec<u8>>;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
Debug,
|
||||
IntoPrimitive,
|
||||
TryFromPrimitive,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
Display,
|
||||
)]
|
||||
#[strum(ascii_case_insensitive)]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive, EnumIter)]
|
||||
#[repr(u8)]
|
||||
pub enum HashFunction {
|
||||
Blake3 = 0,
|
||||
Shake256 = 1,
|
||||
Shake128 = 2,
|
||||
SHAKE256 = 1,
|
||||
SHAKE128 = 2,
|
||||
SHA256 = 3,
|
||||
}
|
||||
|
||||
@@ -84,8 +67,8 @@ impl HashFunction {
|
||||
hasher.finalize_xof().fill(&mut out);
|
||||
hasher.reset();
|
||||
}
|
||||
HashFunction::Shake256 => libcrux_sha3::shake256_ema(&mut out, data.as_ref()),
|
||||
HashFunction::Shake128 => libcrux_sha3::shake128_ema(&mut out, data.as_ref()),
|
||||
HashFunction::SHAKE256 => libcrux_sha3::shake256_ema(&mut out, data.as_ref()),
|
||||
HashFunction::SHAKE128 => libcrux_sha3::shake128_ema(&mut out, data.as_ref()),
|
||||
HashFunction::SHA256 => libcrux_sha3::sha256_ema(&mut out, data.as_ref()),
|
||||
}
|
||||
|
||||
@@ -93,6 +76,17 @@ impl HashFunction {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for HashFunction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
HashFunction::Blake3 => "blake3",
|
||||
HashFunction::SHAKE128 => "shake128",
|
||||
HashFunction::SHAKE256 => "shake256",
|
||||
HashFunction::SHA256 => "sha256",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum HashLength {
|
||||
@@ -149,21 +143,7 @@ impl HashLength {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
Debug,
|
||||
IntoPrimitive,
|
||||
TryFromPrimitive,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
Display,
|
||||
)]
|
||||
#[strum(ascii_case_insensitive)]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum SignatureScheme {
|
||||
Ed25519 = 0,
|
||||
@@ -192,21 +172,15 @@ impl SignatureScheme {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
Hash,
|
||||
Debug,
|
||||
IntoPrimitive,
|
||||
TryFromPrimitive,
|
||||
EnumIter,
|
||||
EnumString,
|
||||
Display,
|
||||
)]
|
||||
#[strum(ascii_case_insensitive)]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
impl Display for SignatureScheme {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
SignatureScheme::Ed25519 => "ed25519",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum KEM {
|
||||
XWing = 0,
|
||||
@@ -226,6 +200,17 @@ impl KEM {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for KEM {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(match self {
|
||||
KEM::MlKem768 => "mlkem768",
|
||||
KEM::XWing => "xwing",
|
||||
KEM::X25519 => "x25519",
|
||||
KEM::McEliece => "mceliece",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct Ciphersuite {
|
||||
hash_function: HashFunction,
|
||||
@@ -350,34 +335,3 @@ impl Display for Ciphersuite {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
#[test]
|
||||
fn kem_display_consistency() {
|
||||
for kem in KEM::iter() {
|
||||
let display = format!("{kem}");
|
||||
assert_eq!(kem, KEM::from_str(&display).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_function_display_consistency() {
|
||||
for hash_fn in HashFunction::iter() {
|
||||
let display = format!("{hash_fn}");
|
||||
assert_eq!(hash_fn, HashFunction::from_str(&display).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_scheme_display_consistency() {
|
||||
for scheme in SignatureScheme::iter() {
|
||||
let display = format!("{scheme}");
|
||||
assert_eq!(scheme, SignatureScheme::from_str(&display).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ strum = { workspace = true }
|
||||
|
||||
# internal
|
||||
nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"] }
|
||||
nym-kkt-ciphersuite = { workspace = true, features = ["digests"] }
|
||||
nym-kkt-ciphersuite = { path = "../nym-kkt-ciphersuite", features = ["digests"] }
|
||||
|
||||
libcrux-kem = { git = "https://github.com/cryspen/libcrux" }
|
||||
libcrux-ecdh = { git = "https://github.com/cryspen/libcrux", features = ["codec"] }
|
||||
|
||||
@@ -54,8 +54,8 @@ pub fn kkt_benchmark(c: &mut Criterion) {
|
||||
for hash_function in [
|
||||
HashFunction::Blake3,
|
||||
HashFunction::SHA256,
|
||||
HashFunction::Shake128,
|
||||
HashFunction::Shake256,
|
||||
HashFunction::SHAKE128,
|
||||
HashFunction::SHAKE256,
|
||||
] {
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
kem,
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
|
||||
use classic_mceliece_rust::keypair_boxed;
|
||||
|
||||
use nym_kkt_ciphersuite::{DEFAULT_HASH_LEN, KeyDigests};
|
||||
use nym_kkt_ciphersuite::{DEFAULT_HASH_LEN, KEMKeyDigests};
|
||||
use rand::{CryptoRng, RngCore};
|
||||
|
||||
pub fn generate_keypair_ed25519<R>(
|
||||
@@ -78,7 +78,7 @@ pub fn hash_key_bytes(
|
||||
|
||||
/// attempt to produce digests of the provided key using all known [HashFunction] with a default
|
||||
/// hash length where variable output is available
|
||||
pub fn produce_key_digests(key_bytes: &[u8]) -> KeyDigests {
|
||||
pub fn produce_key_digests(key_bytes: &[u8]) -> KEMKeyDigests {
|
||||
use strum::IntoEnumIterator;
|
||||
let mut digests = HashMap::new();
|
||||
for hash in HashFunction::iter() {
|
||||
|
||||
@@ -48,8 +48,8 @@ mod test {
|
||||
for hash_function in [
|
||||
HashFunction::Blake3,
|
||||
HashFunction::SHA256,
|
||||
HashFunction::Shake128,
|
||||
HashFunction::Shake256,
|
||||
HashFunction::SHAKE128,
|
||||
HashFunction::SHAKE256,
|
||||
] {
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
kem,
|
||||
@@ -254,8 +254,8 @@ mod test {
|
||||
for hash_function in [
|
||||
HashFunction::Blake3,
|
||||
HashFunction::SHA256,
|
||||
HashFunction::Shake128,
|
||||
HashFunction::Shake256,
|
||||
HashFunction::SHAKE128,
|
||||
HashFunction::SHAKE256,
|
||||
] {
|
||||
let ciphersuite = Ciphersuite::resolve_ciphersuite(
|
||||
kem,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_kkt::ciphersuite::{KEM, KEMKeyDigests, SignatureScheme, SigningKeyDigests};
|
||||
use nym_kkt::ciphersuite::{HashFunction, KEM};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -49,26 +49,18 @@ impl LpLocalPeer {
|
||||
let expected_kem_key_digests = match &self.kem_psq {
|
||||
None => HashMap::new(),
|
||||
Some(kem_keys) => {
|
||||
let hashes =
|
||||
nym_kkt::key_utils::produce_key_digests(kem_keys.public_key().as_bytes());
|
||||
|
||||
let mut digests = HashMap::new();
|
||||
digests.insert(
|
||||
KEM::X25519,
|
||||
nym_kkt::key_utils::produce_key_digests(kem_keys.public_key().as_bytes()),
|
||||
);
|
||||
digests.insert(KEM::X25519, hashes);
|
||||
digests
|
||||
}
|
||||
};
|
||||
|
||||
let mut expected_signing_key_digests = HashMap::new();
|
||||
expected_signing_key_digests.insert(
|
||||
SignatureScheme::Ed25519,
|
||||
nym_kkt::key_utils::produce_key_digests(self.ed25519.public_key().as_bytes()),
|
||||
);
|
||||
|
||||
LpRemotePeer {
|
||||
ed25519_public: *self.ed25519.public_key(),
|
||||
x25519_public: *self.x25519.public_key(),
|
||||
expected_kem_key_digests,
|
||||
expected_signing_key_digests,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,11 +85,8 @@ pub struct LpRemotePeer {
|
||||
/// Remote X25519 public key (Noise static key)
|
||||
pub(crate) x25519_public: x25519::PublicKey,
|
||||
|
||||
/// Expected digests of the remote's KEM key
|
||||
pub(crate) expected_kem_key_digests: HashMap<KEM, KEMKeyDigests>,
|
||||
|
||||
/// Expected digests of the remote's signing key
|
||||
pub(crate) expected_signing_key_digests: HashMap<SignatureScheme, SigningKeyDigests>,
|
||||
/// Expected digest of the remote's KEM key
|
||||
pub(crate) expected_kem_key_digests: HashMap<KEM, HashMap<HashFunction, Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl LpRemotePeer {
|
||||
@@ -106,7 +95,6 @@ impl LpRemotePeer {
|
||||
ed25519_public,
|
||||
x25519_public,
|
||||
expected_kem_key_digests: Default::default(),
|
||||
expected_signing_key_digests: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,13 +107,11 @@ impl LpRemotePeer {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_key_digests(
|
||||
pub fn with_kem_key_digests(
|
||||
mut self,
|
||||
expected_kem_key_digests: HashMap<KEM, KEMKeyDigests>,
|
||||
expected_signing_key_digests: HashMap<SignatureScheme, SigningKeyDigests>,
|
||||
expected_kem_key_digests: HashMap<KEM, HashMap<HashFunction, Vec<u8>>>,
|
||||
) -> Self {
|
||||
self.expected_kem_key_digests = expected_kem_key_digests;
|
||||
self.expected_signing_key_digests = expected_signing_key_digests;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ workspace = true
|
||||
[dependencies]
|
||||
bincode = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tokio-util.workspace = true
|
||||
|
||||
nym-authenticator-requests = { workspace = true }
|
||||
nym-credentials-interface = { workspace = true }
|
||||
@@ -22,8 +23,9 @@ nym-crypto = { workspace = true }
|
||||
nym-ip-packet-requests = { workspace = true }
|
||||
nym-sphinx = { workspace = true }
|
||||
nym-wireguard-types = { workspace = true }
|
||||
nym-kkt-ciphersuite = { workspace = true }
|
||||
nym-kkt-ciphersuite = { path = "../nym-kkt-ciphersuite" }
|
||||
|
||||
[dev-dependencies]
|
||||
bincode.workspace = true
|
||||
time.workspace = true
|
||||
nym-test-utils = { workspace = true }
|
||||
|
||||
@@ -1,50 +1,47 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_authenticator_requests::AuthenticatorVersion;
|
||||
use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_ip_packet_requests::IpPair;
|
||||
use nym_kkt_ciphersuite::{KEM, KEMKeyDigests, SignatureScheme};
|
||||
use nym_sphinx::addressing::Recipient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
||||
use nym_authenticator_requests::AuthenticatorVersion;
|
||||
use nym_crypto::asymmetric::x25519::{PublicKey, serde_helpers::bs58_x25519_pubkey};
|
||||
use nym_ip_packet_requests::IpPair;
|
||||
use nym_sphinx::addressing::{NodeIdentity, Recipient};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
mod lp_messages;
|
||||
mod serialisation;
|
||||
|
||||
pub use lp_messages::{
|
||||
LpDvpnRegistrationRequest, LpMixnetGatewayData, LpMixnetRegistrationRequest,
|
||||
LpRegistrationData, LpRegistrationRequest, LpRegistrationResponse, RegistrationMode,
|
||||
};
|
||||
use nym_kkt_ciphersuite::{KEM, KEMKeyDigests};
|
||||
pub use serialisation::BincodeError;
|
||||
|
||||
mod lp_messages;
|
||||
mod serialisation;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NymNodeInformation {
|
||||
pub identity: ed25519::PublicKey,
|
||||
pub struct NymNode {
|
||||
pub identity: NodeIdentity,
|
||||
pub ip_address: IpAddr,
|
||||
pub ipr_address: Option<Recipient>,
|
||||
pub authenticator_address: Option<Recipient>,
|
||||
pub lp_data: Option<NymNodeLPInformation>,
|
||||
pub lp_data: Option<LpData>,
|
||||
pub version: AuthenticatorVersion,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct WireguardConfiguration {
|
||||
pub struct GatewayData {
|
||||
#[serde(with = "bs58_x25519_pubkey")]
|
||||
pub public_key: x25519::PublicKey,
|
||||
pub public_key: PublicKey,
|
||||
pub endpoint: SocketAddr,
|
||||
pub private_ipv4: Ipv4Addr,
|
||||
pub private_ipv6: Ipv6Addr,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NymNodeLPInformation {
|
||||
pub struct LpData {
|
||||
pub address: SocketAddr,
|
||||
pub expected_kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub expected_signing_key_hashes: HashMap<SignatureScheme, KEMKeyDigests>,
|
||||
pub x25519: x25519::PublicKey,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
//! LP (Lewes Protocol) registration message types shared between client and gateway.
|
||||
|
||||
use crate::WireguardConfiguration;
|
||||
use crate::GatewayData;
|
||||
use crate::serialisation::{BincodeError, BincodeOptions, lp_bincode_serializer};
|
||||
use nym_credentials_interface::{CredentialSpendingData, TicketType};
|
||||
use nym_crypto::aes::cipher::crypto_common::rand_core::{CryptoRng, RngCore};
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -51,9 +50,6 @@ pub struct LpDvpnRegistrationRequest {
|
||||
|
||||
/// Ticket type for bandwidth allocation
|
||||
pub ticket_type: TicketType,
|
||||
|
||||
/// Preshared key to be used for the connection
|
||||
pub psk: [u8; 32],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -98,7 +94,7 @@ pub struct LpRegistrationResponse {
|
||||
|
||||
/// Gateway configuration data for dVPN mode (WireGuard)
|
||||
/// This matches what WireguardRegistrationResult expects
|
||||
pub gateway_data: Option<WireguardConfiguration>,
|
||||
pub gateway_data: Option<GatewayData>,
|
||||
|
||||
/// Gateway data for mixnet mode
|
||||
///
|
||||
@@ -112,25 +108,17 @@ pub struct LpRegistrationResponse {
|
||||
|
||||
impl LpRegistrationRequest {
|
||||
/// Create a new dVPN registration request
|
||||
pub fn new_dvpn<R>(
|
||||
rng: &mut R,
|
||||
pub fn new_dvpn(
|
||||
wg_public_key: nym_wireguard_types::PeerPublicKey,
|
||||
credential: CredentialSpendingData,
|
||||
ticket_type: TicketType,
|
||||
) -> Self
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
let mut psk = [0u8; 32];
|
||||
rng.fill_bytes(&mut psk);
|
||||
|
||||
) -> Self {
|
||||
Self {
|
||||
registration_data: LpRegistrationData::Dvpn {
|
||||
data: Box::new(LpDvpnRegistrationRequest {
|
||||
wg_public_key,
|
||||
credential,
|
||||
ticket_type,
|
||||
psk,
|
||||
}),
|
||||
},
|
||||
#[allow(clippy::expect_used)]
|
||||
@@ -164,7 +152,7 @@ impl LpRegistrationRequest {
|
||||
|
||||
impl LpRegistrationResponse {
|
||||
/// Create a success response with GatewayData (for dVPN mode)
|
||||
pub fn success(allocated_bandwidth: i64, gateway_data: WireguardConfiguration) -> Self {
|
||||
pub fn success(allocated_bandwidth: i64, gateway_data: GatewayData) -> Self {
|
||||
Self {
|
||||
success: true,
|
||||
error: None,
|
||||
@@ -214,10 +202,10 @@ mod tests {
|
||||
use std::net::Ipv4Addr;
|
||||
// ==================== Helper Functions ====================
|
||||
|
||||
fn create_test_gateway_data() -> WireguardConfiguration {
|
||||
fn create_test_gateway_data() -> GatewayData {
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
WireguardConfiguration {
|
||||
GatewayData {
|
||||
public_key: nym_crypto::asymmetric::x25519::PublicKey::from(
|
||||
nym_sphinx::PublicKey::from([1u8; 32]),
|
||||
),
|
||||
|
||||
+1
-5
@@ -23,8 +23,4 @@ NYM_API=https://canary-api.performance.nymte.ch/api/
|
||||
NYXD_WS=wss://rpc.canary-validator.performance.nymte.ch/websocket
|
||||
NYM_VPN_API=https://nym-vpn-api-git-deploy-canary-nyx-network-staging.vercel.app/api/
|
||||
|
||||
UPGRADE_MODE_ATTESTER_ED25519_PUBKEY=U1NXToPYUTsh7pYPLcwXCXwcL6pGoLUou7fyAJrNz8b
|
||||
UPGRADE_MODE_ATTESTATION_URL=http://upgrade-mode.performance.nymte.ch/.wellknown/canary/attestation.json
|
||||
|
||||
NYM_APIS=[{"url":"https://canary-api.performance.nymte.ch/api/","front_hosts":null},{"url":"https://nym-frontdoor.vercel.app/canary/nym-api/","front_hosts":["vercel.app","vercel.com"]}]
|
||||
NYM_VPN_APIS=[{"url":"https://nym-vpn-api-git-deploy-canary-nyx-network-staging.vercel.app/api/","front_hosts":["vercel.app","vercel.com"]},{"url":"https://nym-frontdoor.vercel.app/canary/nym-vpn-api/","front_hosts":["vercel.app","vercel.com"]}]
|
||||
UPGRADE_MODE_ATTESTER_ED25519_PUBKEY=U1NXToPYUTsh7pYPLcwXCXwcL6pGoLUou7fyAJrNz8b
|
||||
+1
-5
@@ -24,8 +24,4 @@ NYXD_WS=wss://rpc.sandbox.nymtech.net/websocket
|
||||
NYM_API=https://sandbox-nym-api1.nymtech.net/api/
|
||||
NYM_VPN_API=https://nym-vpn-api-git-deploy-sandbox-nyx-network-staging.vercel.app/api/
|
||||
|
||||
UPGRADE_MODE_ATTESTER_ED25519_PUBKEY=EGwzKXPrqStv8cHF68VT2LbQuEBGDPzhCAixScvybfem
|
||||
UPGRADE_MODE_ATTESTATION_URL=http://upgrade-mode.performance.nymte.ch/.wellknown/sandbox/attestation.json
|
||||
|
||||
NYM_APIS=[{"url":"https://sandbox-nym-api1.nymtech.net/api/","front_hosts":null},{"url":"https://nym-frontdoor.vercel.app/sandbox/nym-api/","front_hosts":["vercel.app","vercel.com"]}]
|
||||
NYM_VPN_APIS=[{"url":"https://nym-vpn-api-git-deploy-sandbox-nyx-network-staging.vercel.app/api/","front_hosts":["vercel.app","vercel.com"]},{"url":"https://nym-frontdoor.vercel.app/sandbox/nym-vpn-api/","front_hosts":["vercel.app","vercel.com"]}]
|
||||
UPGRADE_MODE_ATTESTER_ED25519_PUBKEY=EGwzKXPrqStv8cHF68VT2LbQuEBGDPzhCAixScvybfem
|
||||
@@ -497,7 +497,7 @@ impl<R, S> FreshHandler<R, S> {
|
||||
Err(incompatible_err)
|
||||
} else {
|
||||
debug!("the client is using exactly the same (or older) protocol version as we are. We're good to continue!");
|
||||
Ok(client_protocol_version)
|
||||
Ok(CURRENT_PROTOCOL_VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,10 @@ use nym_gateway_storage::models::PersistedBandwidth;
|
||||
use nym_gateway_storage::traits::BandwidthGatewayStorage;
|
||||
use nym_metrics::{add_histogram_obs, inc, inc_by};
|
||||
use nym_registration_common::{
|
||||
LpDvpnRegistrationRequest, LpMixnetGatewayData, LpMixnetRegistrationRequest,
|
||||
LpRegistrationData, LpRegistrationRequest, LpRegistrationResponse, WireguardConfiguration,
|
||||
GatewayData, LpDvpnRegistrationRequest, LpMixnetGatewayData, LpMixnetRegistrationRequest,
|
||||
LpRegistrationData, LpRegistrationRequest, LpRegistrationResponse,
|
||||
};
|
||||
use nym_wireguard::PeerControlRequest;
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use std::sync::Arc;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::*;
|
||||
@@ -186,7 +185,7 @@ async fn check_existing_registration(
|
||||
|
||||
Some(LpRegistrationResponse::success(
|
||||
bandwidth,
|
||||
WireguardConfiguration {
|
||||
GatewayData {
|
||||
public_key: *wg_data.keypair().public_key(),
|
||||
endpoint: wg_data.config().bind_address,
|
||||
private_ipv4,
|
||||
@@ -195,20 +194,6 @@ async fn check_existing_registration(
|
||||
))
|
||||
}
|
||||
|
||||
/// In the case of an already registered WG peer, update its PSK.
|
||||
async fn update_peer_psk(
|
||||
peer: PeerPublicKey,
|
||||
psk: Key,
|
||||
state: &LpHandlerState,
|
||||
) -> Result<(), GatewayError> {
|
||||
let encoded_psk = psk.to_lower_hex();
|
||||
state
|
||||
.storage
|
||||
.update_peer_psk(&peer.to_string(), Some(&encoded_psk))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn process_dvpn_registration(
|
||||
request: Box<LpDvpnRegistrationRequest>,
|
||||
state: &LpHandlerState,
|
||||
@@ -221,13 +206,6 @@ async fn process_dvpn_registration(
|
||||
// without wasting credentials
|
||||
let wg_key_str = request.wg_public_key.to_string();
|
||||
if let Some(existing_response) = check_existing_registration(&wg_key_str, state).await {
|
||||
// TODO: this flow will be changed in subsequent PRs as it's wasting credentials regardless
|
||||
if let Err(err) = update_peer_psk(request.wg_public_key, Key::new(request.psk), state).await
|
||||
{
|
||||
return LpRegistrationResponse::error(format!(
|
||||
"WireGuard peer PSK update failed: {err}"
|
||||
));
|
||||
}
|
||||
info!("LP dVPN re-registration for existing peer {wg_key_str} (idempotent)",);
|
||||
inc!("lp_registration_dvpn_idempotent");
|
||||
return existing_response;
|
||||
@@ -237,7 +215,6 @@ async fn process_dvpn_registration(
|
||||
let (gateway_data, client_id) = match register_wg_peer(
|
||||
request.wg_public_key.inner().as_ref(),
|
||||
request.ticket_type,
|
||||
Key::new(request.psk),
|
||||
state,
|
||||
)
|
||||
.await
|
||||
@@ -372,9 +349,8 @@ pub async fn process_registration(
|
||||
async fn register_wg_peer(
|
||||
public_key_bytes: &[u8],
|
||||
ticket_type: nym_credentials_interface::TicketType,
|
||||
psk: Key,
|
||||
state: &LpHandlerState,
|
||||
) -> Result<(WireguardConfiguration, i64), GatewayError> {
|
||||
) -> Result<(GatewayData, i64), GatewayError> {
|
||||
let Some(wg_controller) = &state.wg_peer_controller else {
|
||||
return Err(GatewayError::ServiceProviderNotRunning {
|
||||
service: "WireGuard".to_string(),
|
||||
@@ -398,10 +374,8 @@ async fn register_wg_peer(
|
||||
let peer_key = Key::new(key_bytes);
|
||||
|
||||
// Allocate IPs from centralized pool managed by PeerController
|
||||
let registration_data =
|
||||
nym_wireguard::PeerRegistrationData::new(peer_key.clone()).with_preshared_key(psk);
|
||||
let registration_data = nym_wireguard::PeerRegistrationData::new(peer_key.clone());
|
||||
|
||||
let psk = registration_data.preshared_key.clone();
|
||||
// Request IP allocation from PeerController
|
||||
let (tx, rx) = oneshot::channel();
|
||||
wg_controller
|
||||
@@ -441,7 +415,6 @@ async fn register_wg_peer(
|
||||
format!("{client_ipv6}/128").parse()?,
|
||||
];
|
||||
peer.persistent_keepalive_interval = Some(25);
|
||||
peer.preshared_key = psk;
|
||||
|
||||
// Store peer in database FIRST (before adding to controller)
|
||||
// This ensures bandwidth storage exists when controller's generate_bandwidth_manager() is called
|
||||
@@ -494,7 +467,7 @@ async fn register_wg_peer(
|
||||
|
||||
// Create GatewayData response (matching authenticator response format)
|
||||
Ok((
|
||||
WireguardConfiguration {
|
||||
GatewayData {
|
||||
public_key: gateway_pubkey,
|
||||
endpoint: gateway_endpoint,
|
||||
private_ipv4: client_ipv4,
|
||||
|
||||
+12
-10
@@ -93,9 +93,6 @@ pub struct GatewayTasksBuilder {
|
||||
/// ed25519 keypair used to assert one's identity.
|
||||
identity_keypair: Arc<ed25519::KeyPair>,
|
||||
|
||||
/// x25519 keypair used within KTT exchange
|
||||
x25519_keypair: Arc<x25519::KeyPair>,
|
||||
|
||||
/// x25519 (for now, to be changed into MlKem) keypair used for the PSQ derivation
|
||||
kem_psq_keys: Arc<x25519::KeyPair>,
|
||||
|
||||
@@ -127,7 +124,6 @@ impl GatewayTasksBuilder {
|
||||
pub fn new(
|
||||
config: Config,
|
||||
identity: Arc<ed25519::KeyPair>,
|
||||
x25519: Arc<x25519::KeyPair>,
|
||||
kem_psq_keys: Arc<x25519::KeyPair>,
|
||||
storage: GatewayStorage,
|
||||
mix_packet_sender: MixForwardingSender,
|
||||
@@ -146,7 +142,6 @@ impl GatewayTasksBuilder {
|
||||
wireguard_data: None,
|
||||
user_agent,
|
||||
identity_keypair: identity,
|
||||
x25519_keypair: x25519,
|
||||
kem_psq_keys,
|
||||
storage,
|
||||
mix_packet_sender,
|
||||
@@ -336,14 +331,21 @@ impl GatewayTasksBuilder {
|
||||
.as_ref()
|
||||
.map(|wg_data| wg_data.inner.peer_tx().clone());
|
||||
|
||||
// We use standard RFC 7748 conversion to derive X25519 keys from Ed25519 identity keys.
|
||||
// This allows callers to provide only Ed25519 keys (which they already have for signing/identity)
|
||||
// without needing to manage separate X25519 keypairs.
|
||||
//
|
||||
// Security: Ed25519→X25519 conversion is cryptographically sound (RFC 7748).
|
||||
// The derived X25519 keys are used for:
|
||||
// - Noise protocol ephemeral DH
|
||||
// - PSQ ECDH baseline security (pre-quantum)
|
||||
let x25519_keys = Arc::new(self.identity_keypair.to_x25519());
|
||||
|
||||
let handler_state = lp_listener::LpHandlerState {
|
||||
ecash_verifier: self.ecash_manager().await?,
|
||||
storage: self.storage.clone(),
|
||||
local_lp_peer: LpLocalPeer::new(
|
||||
self.identity_keypair.clone(),
|
||||
self.x25519_keypair.clone(),
|
||||
)
|
||||
.with_kem_psq_key(self.kem_psq_keys.clone()),
|
||||
local_lp_peer: LpLocalPeer::new(self.identity_keypair.clone(), x25519_keys)
|
||||
.with_kem_psq_key(self.kem_psq_keys.clone()),
|
||||
metrics: self.metrics.clone(),
|
||||
active_clients_store,
|
||||
wg_peer_controller,
|
||||
|
||||
@@ -392,6 +392,7 @@ mod tests {
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
entry.base.peer.as_remote(),
|
||||
entry.base.socket_addr,
|
||||
client_data.base.socket_addr.ip(),
|
||||
);
|
||||
|
||||
// 1. establish mock connection between client and gateway and retrieve gateway's handle
|
||||
@@ -441,7 +442,6 @@ mod tests {
|
||||
let gateway_identity = entry.base.peer.ed25519().public_key();
|
||||
let registration_result = client
|
||||
.register(
|
||||
&mut client_rng,
|
||||
&wg_keypair,
|
||||
gateway_identity,
|
||||
&client_data.ticket_provider,
|
||||
@@ -483,6 +483,7 @@ mod tests {
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
entry.base.peer.as_remote(),
|
||||
entry.base.socket_addr,
|
||||
client_data.base.socket_addr.ip(),
|
||||
);
|
||||
|
||||
// 1. establish mock connection between client and gateway and retrieve gateway's handle
|
||||
@@ -507,7 +508,6 @@ mod tests {
|
||||
let gateway_identity = entry.base.peer.ed25519().public_key();
|
||||
let registration_result = client
|
||||
.register(
|
||||
&mut client_rng,
|
||||
&wg_keypair,
|
||||
gateway_identity,
|
||||
&client_data.ticket_provider,
|
||||
@@ -544,6 +544,7 @@ mod tests {
|
||||
client_data.base.peer.ed25519().clone(),
|
||||
entry.base.peer.as_remote(),
|
||||
entry.base.socket_addr,
|
||||
client_data.base.socket_addr.ip(),
|
||||
);
|
||||
|
||||
// START: ENTRY SETUP
|
||||
@@ -648,7 +649,6 @@ mod tests {
|
||||
let exit_registration_result = nested_session
|
||||
.handshake_and_register(
|
||||
&mut entry_client,
|
||||
&mut client_rng,
|
||||
&client_data.base.x25519_wg_keys,
|
||||
exit.base.peer.ed25519().public_key(),
|
||||
&client_data.ticket_provider,
|
||||
@@ -660,7 +660,6 @@ mod tests {
|
||||
// 14. complete registration with the entry
|
||||
let entry_registration_result = entry_client
|
||||
.register(
|
||||
&mut client_rng,
|
||||
&client_data.base.x25519_wg_keys,
|
||||
entry.base.peer.ed25519().public_key(),
|
||||
&client_data.ticket_provider,
|
||||
|
||||
@@ -49,7 +49,7 @@ nym-noise-keys = { workspace = true }
|
||||
nym-network-defaults = { workspace = true }
|
||||
nym-ticketbooks-merkle = { workspace = true }
|
||||
nym-ecash-signer-check-types = { workspace = true }
|
||||
nym-kkt-ciphersuite = { workspace = true }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = { workspace = true }
|
||||
|
||||
@@ -8,7 +8,6 @@ use celes::Country;
|
||||
use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_pubkey;
|
||||
use nym_crypto::asymmetric::x25519::serde_helpers::bs58_x25519_pubkey;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_kkt_ciphersuite::{HashFunction, SignatureScheme, KEM};
|
||||
use nym_network_defaults::{WG_METADATA_PORT, WG_TUNNEL_PORT};
|
||||
use nym_noise_keys::VersionedNoiseKeyV1;
|
||||
use schemars::JsonSchema;
|
||||
@@ -212,20 +211,6 @@ pub struct LewesProtocolDetailsV1 {
|
||||
/// Digests of the KEM keys available to this node alongside hashing algorithms used
|
||||
/// for their computation.
|
||||
pub kem_keys: HashMap<LPKEM, HashMap<LPHashFunction, Vec<u8>>>,
|
||||
|
||||
/// Digests of the signing keys available to this node alongside hashing algorithms used
|
||||
/// for their computation.
|
||||
pub signing_keys: HashMap<LPSignatureScheme, HashMap<LPHashFunction, Vec<u8>>>,
|
||||
}
|
||||
|
||||
/// Convert map of digests from `nym_node_requests` types into `nym-api-requests` types
|
||||
fn translate_digests(
|
||||
digests: HashMap<nym_node_requests::api::v1::lewes_protocol::models::LPHashFunction, Vec<u8>>,
|
||||
) -> HashMap<LPHashFunction, Vec<u8>> {
|
||||
digests
|
||||
.into_iter()
|
||||
.map(|(hash_fn, digest)| (hash_fn.into(), digest))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(
|
||||
@@ -274,26 +259,6 @@ pub enum LPHashFunction {
|
||||
Sha256,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
JsonSchema,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Display,
|
||||
EnumString,
|
||||
ToSchema,
|
||||
)]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
pub enum LPSignatureScheme {
|
||||
Ed25519,
|
||||
}
|
||||
|
||||
impl From<nym_node_requests::api::v1::node::models::HostInformation> for HostInformationV1 {
|
||||
fn from(value: nym_node_requests::api::v1::node::models::HostInformation) -> Self {
|
||||
HostInformationV1 {
|
||||
@@ -416,12 +381,15 @@ impl From<nym_node_requests::api::v1::lewes_protocol::models::LewesProtocol>
|
||||
kem_keys: value
|
||||
.kem_keys
|
||||
.into_iter()
|
||||
.map(|(kem, digests)| (kem.into(), translate_digests(digests)))
|
||||
.collect(),
|
||||
signing_keys: value
|
||||
.signing_keys
|
||||
.into_iter()
|
||||
.map(|(scheme, digests)| (scheme.into(), translate_digests(digests)))
|
||||
.map(|(kem, digests)| {
|
||||
(
|
||||
kem.into(),
|
||||
digests
|
||||
.into_iter()
|
||||
.map(|(hash_fn, digest)| (hash_fn.into(), digest))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
@@ -456,45 +424,3 @@ impl From<nym_node_requests::api::v1::lewes_protocol::models::LPHashFunction> fo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_node_requests::api::v1::lewes_protocol::models::LPSignatureScheme>
|
||||
for LPSignatureScheme
|
||||
{
|
||||
fn from(value: nym_node_requests::api::v1::lewes_protocol::models::LPSignatureScheme) -> Self {
|
||||
match value {
|
||||
nym_node_requests::api::v1::lewes_protocol::models::LPSignatureScheme::Ed25519 => {
|
||||
LPSignatureScheme::Ed25519
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LPKEM> for KEM {
|
||||
fn from(value: LPKEM) -> Self {
|
||||
match value {
|
||||
LPKEM::MlKem768 => KEM::MlKem768,
|
||||
LPKEM::XWing => KEM::XWing,
|
||||
LPKEM::X25519 => KEM::X25519,
|
||||
LPKEM::McEliece => KEM::McEliece,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LPHashFunction> for HashFunction {
|
||||
fn from(value: LPHashFunction) -> Self {
|
||||
match value {
|
||||
LPHashFunction::Blake3 => HashFunction::Blake3,
|
||||
LPHashFunction::Shake128 => HashFunction::Shake128,
|
||||
LPHashFunction::Shake256 => HashFunction::Shake256,
|
||||
LPHashFunction::Sha256 => HashFunction::SHA256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LPSignatureScheme> for SignatureScheme {
|
||||
fn from(value: LPSignatureScheme) -> Self {
|
||||
match value {
|
||||
LPSignatureScheme::Ed25519 => SignatureScheme::Ed25519,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ impl From<NymNodeDescriptionV1> for NymNodeDescriptionV2 {
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn mock_nym_node_description(seed: u64) -> NymNodeDescriptionV2 {
|
||||
use crate::models::{LPHashFunction, LPSignatureScheme, LPKEM};
|
||||
use crate::models::{LPHashFunction, LPKEM};
|
||||
use nym_test_utils::helpers::{u64_seeded_rng, RngCore};
|
||||
|
||||
let mut rng = u64_seeded_rng(seed);
|
||||
@@ -257,16 +257,11 @@ pub fn mock_nym_node_description(seed: u64) -> NymNodeDescriptionV2 {
|
||||
// just reuse the same x25519 key for everything - this is just a data mock
|
||||
let x25519 = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let mut kem_hashes_wrapper = std::collections::HashMap::new();
|
||||
let mut signing_keys_hashes_wrapper = std::collections::HashMap::new();
|
||||
let mut kem_hashes = std::collections::HashMap::new();
|
||||
let mut signing_keys_hashes = std::collections::HashMap::new();
|
||||
let mut hashes_wrapper = std::collections::HashMap::new();
|
||||
let mut hashes = std::collections::HashMap::new();
|
||||
|
||||
kem_hashes.insert(LPHashFunction::Sha256, vec![(seed % 256) as u8; 32]);
|
||||
kem_hashes_wrapper.insert(LPKEM::X25519, kem_hashes);
|
||||
|
||||
signing_keys_hashes.insert(LPHashFunction::Sha256, vec![(seed % 256) as u8; 32]);
|
||||
signing_keys_hashes_wrapper.insert(LPSignatureScheme::Ed25519, signing_keys_hashes);
|
||||
hashes.insert(LPHashFunction::Sha256, vec![(seed % 256) as u8; 32]);
|
||||
hashes_wrapper.insert(LPKEM::X25519, hashes);
|
||||
|
||||
NymNodeDescriptionV2 {
|
||||
node_id: rng.next_u32(),
|
||||
@@ -337,8 +332,7 @@ pub fn mock_nym_node_description(seed: u64) -> NymNodeDescriptionV2 {
|
||||
enabled: true,
|
||||
control_port: 1234,
|
||||
data_port: 2345,
|
||||
kem_keys: kem_hashes_wrapper,
|
||||
signing_keys: signing_keys_hashes_wrapper,
|
||||
kem_keys: hashes_wrapper,
|
||||
}),
|
||||
mixnet_websockets: WebSocketsV2 {
|
||||
ws_port: 9000,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use nym_authenticator_requests::client_message::QueryMessageImpl;
|
||||
use nym_bandwidth_controller::{BandwidthTicketProvider, DEFAULT_TICKETS_TO_SPEND};
|
||||
use nym_crypto::asymmetric::x25519::KeyPair;
|
||||
use nym_registration_common::WireguardConfiguration;
|
||||
use nym_registration_common::GatewayData;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -261,7 +261,7 @@ impl AuthenticatorClient {
|
||||
&mut self,
|
||||
controller: &dyn BandwidthTicketProvider,
|
||||
ticketbook_type: TicketType,
|
||||
) -> std::result::Result<WireguardConfiguration, RegistrationError> {
|
||||
) -> std::result::Result<GatewayData, RegistrationError> {
|
||||
debug!("Registering with the wg gateway...");
|
||||
let pub_key = self.peer_public_key();
|
||||
|
||||
@@ -348,7 +348,7 @@ impl AuthenticatorClient {
|
||||
&self.ip_addr, ®istered_data
|
||||
);
|
||||
|
||||
let gateway_data = WireguardConfiguration {
|
||||
let gateway_data = GatewayData {
|
||||
public_key: registered_data.pub_key().inner().into(),
|
||||
endpoint: SocketAddr::new(self.ip_addr, registered_data.wg_port()),
|
||||
private_ipv4: registered_data.private_ips().ipv4,
|
||||
|
||||
@@ -65,8 +65,6 @@ nym-node-status-client = { path = "../nym-node-status-api/nym-node-status-client
|
||||
nym-node-requests = { path = "../nym-node/nym-node-requests" }
|
||||
nym-registration-client = { path = "../nym-registration-client" }
|
||||
nym-lp = { path = "../common/nym-lp" }
|
||||
nym-kkt-ciphersuite = { workspace = true }
|
||||
|
||||
nym-mixnet-contract-common = { path = "../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-network-defaults = { path = "../common/network-defaults" }
|
||||
nym-registration-common = { path = "../common/registration" }
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::types::Entry;
|
||||
use anyhow::bail;
|
||||
use anyhow::{Context, bail};
|
||||
use base64::{Engine as _, engine::general_purpose};
|
||||
use bytes::BytesMut;
|
||||
use clap::Args;
|
||||
@@ -33,14 +39,7 @@ use nym_sdk::mixnet::{
|
||||
NodeIdentity, Recipient, ReconstructedMessage, StoragePaths,
|
||||
};
|
||||
use rand::rngs::OsRng;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::{codec::Decoder, sync::CancellationToken};
|
||||
use tracing::*;
|
||||
@@ -63,11 +62,8 @@ mod types;
|
||||
use crate::bandwidth_helpers::{acquire_bandwidth, import_bandwidth};
|
||||
use crate::nodes::{DirectoryNode, NymApiDirectory};
|
||||
pub use mode::TestMode;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_kkt_ciphersuite::{KEM, KEMKeyDigests, SignatureScheme};
|
||||
use nym_lp::peer::LpRemotePeer;
|
||||
use nym_node_status_client::models::AttachedTicketMaterials;
|
||||
use nym_registration_client::{LpRegistrationClient, NestedLpSession};
|
||||
pub use types::{IpPingReplies, ProbeOutcome, ProbeResult};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
@@ -160,24 +156,17 @@ pub struct TestedNodeDetails {
|
||||
authenticator_address: Option<Recipient>,
|
||||
authenticator_version: AuthenticatorVersion,
|
||||
ip_address: Option<IpAddr>,
|
||||
lp_data: Option<TestedNodeLpDetails>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TestedNodeLpDetails {
|
||||
pub address: SocketAddr,
|
||||
pub expected_kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub expected_signing_key_hashes: HashMap<SignatureScheme, KEMKeyDigests>,
|
||||
pub x25519: x25519::PublicKey,
|
||||
lp_address: Option<std::net::SocketAddr>,
|
||||
}
|
||||
|
||||
impl TestedNodeDetails {
|
||||
/// Create from CLI args (localnet mode - no HTTP query needed)
|
||||
pub fn from_cli(identity: NodeIdentity, lp_data: TestedNodeLpDetails) -> Self {
|
||||
/// Only identity and LP address are required; other fields are None/default.
|
||||
pub fn from_cli(identity: NodeIdentity, lp_address: std::net::SocketAddr) -> Self {
|
||||
Self {
|
||||
identity,
|
||||
ip_address: Some(lp_data.address.ip()),
|
||||
lp_data: Some(lp_data),
|
||||
ip_address: Some(lp_address.ip()),
|
||||
lp_address: Some(lp_address),
|
||||
// These are None in localnet mode - only needed for mixnet/authenticator
|
||||
exit_router_address: None,
|
||||
authenticator_address: None,
|
||||
@@ -187,7 +176,7 @@ impl TestedNodeDetails {
|
||||
|
||||
/// Check if this node has sufficient info for LP testing
|
||||
pub fn can_test_lp(&self) -> bool {
|
||||
self.lp_data.is_some()
|
||||
self.lp_address.is_some()
|
||||
}
|
||||
|
||||
/// Check if this node has sufficient info for mixnet testing
|
||||
@@ -588,8 +577,11 @@ impl Probe {
|
||||
}
|
||||
|
||||
// Check if node has LP address
|
||||
let Some(lp_data) = node_info.lp_data else {
|
||||
bail!("Gateway does not have LP data configured");
|
||||
let (lp_address, ip_address) = match (node_info.lp_address, node_info.ip_address) {
|
||||
(Some(lp_addr), Some(ip_addr)) => (lp_addr, ip_addr),
|
||||
_ => {
|
||||
bail!("Gateway does not have LP address configured");
|
||||
}
|
||||
};
|
||||
|
||||
info!("Testing LP registration for gateway {}", node_info.identity);
|
||||
@@ -605,10 +597,15 @@ impl Probe {
|
||||
);
|
||||
|
||||
// Run LP registration probe
|
||||
let lp_outcome =
|
||||
lp_registration_probe(node_info.identity, lp_data, &bw_controller, use_mock_ecash)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let lp_outcome = lp_registration_probe(
|
||||
node_info.identity,
|
||||
lp_address,
|
||||
ip_address,
|
||||
&bw_controller,
|
||||
use_mock_ecash,
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
// Return result with only LP outcome
|
||||
Ok(ProbeResult {
|
||||
@@ -928,8 +925,10 @@ impl Probe {
|
||||
};
|
||||
|
||||
// Test LP registration if node has LP address
|
||||
let lp_outcome = if let Some(lp_data) = node_info.lp_data {
|
||||
info!("Node has LP data, testing LP registration...");
|
||||
let lp_outcome = if let (Some(lp_address), Some(ip_address)) =
|
||||
(node_info.lp_address, node_info.ip_address)
|
||||
{
|
||||
info!("Node has LP address, testing LP registration...");
|
||||
|
||||
// Prepare bandwidth credential for LP registration
|
||||
let config = nym_validator_client::nyxd::Config::try_from_nym_network_details(
|
||||
@@ -942,10 +941,15 @@ impl Probe {
|
||||
client,
|
||||
);
|
||||
|
||||
let outcome =
|
||||
lp_registration_probe(node_info.identity, lp_data, &bw_controller, use_mock_ecash)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
let outcome = lp_registration_probe(
|
||||
node_info.identity,
|
||||
lp_address,
|
||||
ip_address,
|
||||
&bw_controller,
|
||||
use_mock_ecash,
|
||||
)
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
|
||||
Some(outcome)
|
||||
} else {
|
||||
@@ -1077,16 +1081,10 @@ async fn wg_probe(
|
||||
Ok(wg_outcome)
|
||||
}
|
||||
|
||||
fn to_lp_remote_peer(identity: ed25519::PublicKey, data: TestedNodeLpDetails) -> LpRemotePeer {
|
||||
LpRemotePeer::new(identity, data.x25519).with_key_digests(
|
||||
data.expected_kem_key_hashes,
|
||||
data.expected_signing_key_hashes,
|
||||
)
|
||||
}
|
||||
|
||||
async fn lp_registration_probe<St>(
|
||||
gateway_identity: NodeIdentity,
|
||||
gateway_lp_data: TestedNodeLpDetails,
|
||||
gateway_lp_address: std::net::SocketAddr,
|
||||
gateway_ip: IpAddr,
|
||||
bandwidth_controller: &nym_bandwidth_controller::BandwidthController<
|
||||
nym_validator_client::nyxd::NyxdClient<nym_validator_client::HttpRpcClient>,
|
||||
St,
|
||||
@@ -1100,10 +1098,10 @@ where
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_registration_client::LpRegistrationClient;
|
||||
|
||||
let lp_address = gateway_lp_data.address;
|
||||
let peer = to_lp_remote_peer(gateway_identity, gateway_lp_data);
|
||||
|
||||
info!("Starting LP registration probe for gateway at {lp_address}",);
|
||||
info!(
|
||||
"Starting LP registration probe for gateway at {}",
|
||||
gateway_lp_address
|
||||
);
|
||||
|
||||
let mut lp_outcome = types::LpProbeResults::default();
|
||||
|
||||
@@ -1112,18 +1110,23 @@ where
|
||||
let client_ed25519_keypair = std::sync::Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
|
||||
// Step 0: Derive X25519 keys from Ed25519 for the gateways
|
||||
let gateway_x25519_key = gateway_identity
|
||||
.to_x25519()
|
||||
.context("failed to convert entry ed25519 key to x25519")?;
|
||||
let peer = LpRemotePeer::new(gateway_identity, gateway_x25519_key);
|
||||
|
||||
// Create LP registration client (uses Ed25519 keys directly, derives X25519 internally)
|
||||
let mut client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
client_ed25519_keypair,
|
||||
peer,
|
||||
lp_address,
|
||||
gateway_lp_address,
|
||||
gateway_ip,
|
||||
);
|
||||
|
||||
// Step 1: Perform handshake (connection is implicit in packet-per-connection model)
|
||||
// LpRegistrationClient uses packet-per-connection model - connect() is gone,
|
||||
// connection is established during handshake and registration automatically.
|
||||
info!("Performing LP handshake at {lp_address}...");
|
||||
info!("Performing LP handshake at {}...", gateway_lp_address);
|
||||
match client.perform_handshake().await {
|
||||
Ok(_) => {
|
||||
info!("LP handshake completed successfully");
|
||||
@@ -1168,7 +1171,7 @@ where
|
||||
);
|
||||
|
||||
match client
|
||||
.register_with_credential(&mut rng, &wg_keypair, credential, ticket_type)
|
||||
.register_with_credential(&wg_keypair, credential, ticket_type)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
@@ -1183,7 +1186,6 @@ where
|
||||
info!("Using real bandwidth controller for LP registration");
|
||||
match client
|
||||
.register(
|
||||
&mut rng,
|
||||
&wg_keypair,
|
||||
&gateway_ed25519_pubkey,
|
||||
bandwidth_controller,
|
||||
@@ -1243,38 +1245,49 @@ where
|
||||
St: nym_sdk::mixnet::CredentialStorage + Clone + Send + Sync + 'static,
|
||||
<St as nym_sdk::mixnet::CredentialStorage>::StorageError: Send + Sync,
|
||||
{
|
||||
// Validate that both gateways have required information
|
||||
let entry_lp_data = entry_gateway
|
||||
.lp_data
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("Entry gateway missing LP data"))?;
|
||||
|
||||
let exit_lp_data = exit_gateway
|
||||
.lp_data
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow::anyhow!("Exit gateway missing LP data"))?;
|
||||
|
||||
let entry_address = entry_lp_data.address;
|
||||
let exit_address = exit_lp_data.address;
|
||||
|
||||
let entry_ip = entry_address.ip();
|
||||
let exit_ip = exit_address.ip();
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_registration_client::{LpRegistrationClient, NestedLpSession};
|
||||
|
||||
info!("Starting LP-based WireGuard probe (entry→exit via forwarding)");
|
||||
|
||||
let mut wg_outcome = WgProbeResults::default();
|
||||
|
||||
// Validate that both gateways have required information
|
||||
let entry_lp_address = entry_gateway
|
||||
.lp_address
|
||||
.ok_or_else(|| anyhow::anyhow!("Entry gateway missing LP address"))?;
|
||||
let exit_lp_address = exit_gateway
|
||||
.lp_address
|
||||
.ok_or_else(|| anyhow::anyhow!("Exit gateway missing LP address"))?;
|
||||
let entry_ip = entry_gateway
|
||||
.ip_address
|
||||
.ok_or_else(|| anyhow::anyhow!("Entry gateway missing IP address"))?;
|
||||
let exit_ip = exit_gateway
|
||||
.ip_address
|
||||
.ok_or_else(|| anyhow::anyhow!("Exit gateway missing IP address"))?;
|
||||
|
||||
// Generate Ed25519 keypairs for LP protocol
|
||||
let mut rng = rand::thread_rng();
|
||||
let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut rng));
|
||||
|
||||
// Step 0: Derive X25519 keys from Ed25519 for the gateways
|
||||
let entry_x25519_public = entry_gateway
|
||||
.identity
|
||||
.to_x25519()
|
||||
.context("failed to convert entry ed25519 key to x25519")?;
|
||||
|
||||
let exit_x25519_public = exit_gateway
|
||||
.identity
|
||||
.to_x25519()
|
||||
.context("failed to convert exit ed25519 key to x25519")?;
|
||||
|
||||
// Generate WireGuard keypairs for VPN registration
|
||||
let entry_wg_keypair = x25519::KeyPair::new(&mut rng);
|
||||
let exit_wg_keypair = x25519::KeyPair::new(&mut rng);
|
||||
|
||||
let entry_peer = to_lp_remote_peer(entry_gateway.identity, entry_lp_data);
|
||||
let exit_peer = to_lp_remote_peer(exit_gateway.identity, exit_lp_data);
|
||||
let entry_peer = LpRemotePeer::new(entry_gateway.identity, entry_x25519_public);
|
||||
let exit_peer = LpRemotePeer::new(exit_gateway.identity, exit_x25519_public);
|
||||
|
||||
// STEP 1: Establish outer LP session with entry gateway
|
||||
// LpRegistrationClient uses packet-per-connection model - connect() is gone,
|
||||
@@ -1283,7 +1296,8 @@ where
|
||||
let mut entry_client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
entry_lp_keypair,
|
||||
entry_peer,
|
||||
entry_address,
|
||||
entry_lp_address,
|
||||
entry_ip,
|
||||
);
|
||||
|
||||
// Perform handshake with entry gateway (connection is implicit)
|
||||
@@ -1296,7 +1310,7 @@ where
|
||||
// STEP 2: Use nested session to register with exit gateway via forwarding
|
||||
info!("Registering with exit gateway via entry forwarding...");
|
||||
let mut nested_session =
|
||||
NestedLpSession::new(exit_address.to_string(), exit_lp_keypair, exit_peer);
|
||||
NestedLpSession::new(exit_lp_address.to_string(), exit_lp_keypair, exit_peer);
|
||||
|
||||
// Convert exit gateway identity to ed25519 public key for registration
|
||||
let exit_gateway_pubkey = ed25519::PublicKey::from_bytes(&exit_gateway.identity.to_bytes())
|
||||
@@ -1312,7 +1326,6 @@ where
|
||||
match nested_session
|
||||
.handshake_and_register_with_credential(
|
||||
&mut entry_client,
|
||||
&mut rng,
|
||||
&exit_wg_keypair,
|
||||
credential,
|
||||
TicketType::V1WireguardExit,
|
||||
@@ -1329,7 +1342,6 @@ where
|
||||
match nested_session
|
||||
.handshake_and_register(
|
||||
&mut entry_client,
|
||||
&mut rng,
|
||||
&exit_wg_keypair,
|
||||
&exit_gateway_pubkey,
|
||||
bandwidth_controller,
|
||||
@@ -1360,12 +1372,7 @@ where
|
||||
TicketType::V1WireguardEntry,
|
||||
);
|
||||
match entry_client
|
||||
.register_with_credential(
|
||||
&mut rng,
|
||||
&entry_wg_keypair,
|
||||
credential,
|
||||
TicketType::V1WireguardEntry,
|
||||
)
|
||||
.register_with_credential(&entry_wg_keypair, credential, TicketType::V1WireguardEntry)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
@@ -1377,7 +1384,6 @@ where
|
||||
} else {
|
||||
match entry_client
|
||||
.register(
|
||||
&mut rng,
|
||||
&entry_wg_keypair,
|
||||
&entry_gateway_pubkey,
|
||||
bandwidth_controller,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{TestedNodeDetails, TestedNodeLpDetails};
|
||||
use crate::TestedNodeDetails;
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use nym_api_requests::models::{
|
||||
AuthenticatorDetailsV2, DeclaredRolesV2, DescribedNodeTypeV2, HostInformationV2,
|
||||
@@ -19,7 +19,6 @@ use nym_validator_client::client::NymApiClientExt;
|
||||
use nym_validator_client::models::NymNodeDescriptionV2;
|
||||
use rand::prelude::IteratorRandom;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{debug, info, warn};
|
||||
@@ -97,14 +96,16 @@ impl DirectoryNode {
|
||||
}
|
||||
|
||||
pub fn to_testable_node(&self) -> anyhow::Result<TestedNodeDetails> {
|
||||
let description = &self.described.description;
|
||||
|
||||
let exit_router_address = description
|
||||
let exit_router_address = self
|
||||
.described
|
||||
.description
|
||||
.ip_packet_router
|
||||
.as_ref()
|
||||
.map(|ipr| ipr.address.parse().context("malformed ipr address"))
|
||||
.transpose()?;
|
||||
let authenticator_address = description
|
||||
let authenticator_address = self
|
||||
.described
|
||||
.description
|
||||
.authenticator
|
||||
.as_ref()
|
||||
.map(|ipr| {
|
||||
@@ -113,59 +114,32 @@ impl DirectoryNode {
|
||||
.context("malformed authenticator address")
|
||||
})
|
||||
.transpose()?;
|
||||
let authenticator_version =
|
||||
AuthenticatorVersion::from(description.build_information.build_version.as_str());
|
||||
let ip_address = description
|
||||
let authenticator_version = AuthenticatorVersion::from(
|
||||
self.described
|
||||
.description
|
||||
.build_information
|
||||
.build_version
|
||||
.as_str(),
|
||||
);
|
||||
let ip_address = self
|
||||
.described
|
||||
.description
|
||||
.host_information
|
||||
.ip_address
|
||||
.first()
|
||||
.copied()
|
||||
.ok_or_else(|| anyhow!("no ip address known"))?;
|
||||
.copied();
|
||||
|
||||
let lp_data = match (
|
||||
description.lewes_protocol.clone(),
|
||||
description.host_information.keys.x25519_versioned_noise,
|
||||
) {
|
||||
(Some(lp_data), Some(noise_key)) => Some(TestedNodeLpDetails {
|
||||
address: SocketAddr::new(ip_address, lp_data.control_port),
|
||||
expected_kem_key_hashes: lp_data
|
||||
.kem_keys
|
||||
.into_iter()
|
||||
.map(|(kem, digests)| {
|
||||
(
|
||||
kem.into(),
|
||||
digests
|
||||
.into_iter()
|
||||
.map(|(hash_fn, digest)| (hash_fn.into(), digest))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
expected_signing_key_hashes: lp_data
|
||||
.signing_keys
|
||||
.into_iter()
|
||||
.map(|(scheme, digests)| {
|
||||
(
|
||||
scheme.into(),
|
||||
digests
|
||||
.into_iter()
|
||||
.map(|(hash_fn, digest)| (hash_fn.into(), digest))
|
||||
.collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
x25519: noise_key.x25519_pubkey,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
// Derive LP address from gateway IP + default LP control port (41264)
|
||||
// TODO: Update this when LP address is exposed in node description API
|
||||
let lp_address = ip_address.map(|ip| std::net::SocketAddr::new(ip, 41264));
|
||||
|
||||
Ok(TestedNodeDetails {
|
||||
identity: self.identity(),
|
||||
exit_router_address,
|
||||
authenticator_address,
|
||||
authenticator_version,
|
||||
ip_address: Some(ip_address),
|
||||
lp_data,
|
||||
ip_address,
|
||||
lp_address,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+27
-114
@@ -5,15 +5,11 @@ use anyhow::bail;
|
||||
use clap::{Parser, Subcommand};
|
||||
use nym_bin_common::bin_info;
|
||||
use nym_config::defaults::setup_env;
|
||||
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||
use nym_gateway_probe::nodes::{NymApiDirectory, query_gateway_by_ip};
|
||||
use nym_gateway_probe::{
|
||||
CredentialArgs, NetstackArgs, ProbeResult, TestMode, TestedNode, TestedNodeDetails,
|
||||
TestedNodeLpDetails,
|
||||
};
|
||||
use nym_kkt_ciphersuite::{HashFunction, KEM};
|
||||
use nym_sdk::mixnet::NodeIdentity;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::Path;
|
||||
use std::{path::PathBuf, sync::OnceLock};
|
||||
@@ -50,70 +46,32 @@ struct CliArgs {
|
||||
#[arg(long, global = true)]
|
||||
gateway_ip: Option<String>,
|
||||
|
||||
// ##########
|
||||
// ENTRY
|
||||
// ##########
|
||||
/// Ed25519 identity of the entry gateway (base58 encoded)
|
||||
/// When provided, skips HTTP API query - use for localnet testing
|
||||
#[arg(long, global = true)]
|
||||
entry_gateway_identity: Option<String>,
|
||||
|
||||
/// x25519 key of the entry gateway used for KKT exchange (base58 encoded)
|
||||
#[arg(long, global = true, requires = "entry_gateway_identity")]
|
||||
entry_gateway_x25519_key: Option<String>,
|
||||
|
||||
/// expected kem type of the entry gateway used during KKT exchange
|
||||
#[arg(long, global = true, requires = "entry_gateway_x25519_key")]
|
||||
entry_gateway_kem_key_type: Option<String>,
|
||||
|
||||
/// expected hash function used for the entry gateway kem key digest
|
||||
#[arg(long, global = true, requires = "entry_gateway_kem_key_type")]
|
||||
entry_gateway_kem_key_hash_function: Option<String>,
|
||||
|
||||
/// expected entry gateway kem key digest (base58 encoded)
|
||||
#[arg(long, global = true, requires = "entry_gateway_kem_key_hash_function")]
|
||||
entry_gateway_kem_hey_hash_bs58: Option<String>,
|
||||
|
||||
/// LP listener address for entry gateway (e.g., "192.168.66.6:41264")
|
||||
/// Used with --entry-gateway-identity for localnet mode
|
||||
#[arg(long, global = true)]
|
||||
entry_lp_address: Option<SocketAddr>,
|
||||
|
||||
// ##########
|
||||
// EXIT
|
||||
// ##########
|
||||
/// The address of the exit gateway for LP forwarding tests (used with --test-lp-wg)
|
||||
/// When specified, --gateway-ip becomes the entry gateway and this becomes the exit gateway
|
||||
/// Supports formats: IP (192.168.66.5), IP:PORT (192.168.66.5:8080), HOST:PORT (localhost:30004)
|
||||
#[arg(long, global = true)]
|
||||
exit_gateway_ip: Option<String>,
|
||||
|
||||
/// Ed25519 identity of the entry gateway (base58 encoded)
|
||||
/// When provided, skips HTTP API query - use for localnet testing
|
||||
#[arg(long, global = true)]
|
||||
entry_gateway_identity: Option<String>,
|
||||
|
||||
/// Ed25519 identity of the exit gateway (base58 encoded)
|
||||
/// When provided, skips HTTP API query - use for localnet testing
|
||||
#[arg(long, global = true)]
|
||||
exit_gateway_identity: Option<String>,
|
||||
|
||||
/// x25519 key of the exit gateway used for KKT exchange (base58 encoded)
|
||||
#[arg(long, global = true, requires = "exit_gateway_identity")]
|
||||
exit_gateway_x25519_key: Option<String>,
|
||||
|
||||
/// expected kem type of the exit gateway used during KKT exchange
|
||||
#[arg(long, global = true, requires = "exit_gateway_x25519_key")]
|
||||
exit_gateway_kem_key_type: Option<String>,
|
||||
|
||||
/// expected hash function used for the exit gateway kem key digest
|
||||
#[arg(long, global = true, requires = "exit_gateway_kem_key_type")]
|
||||
exit_gateway_kem_key_hash_function: Option<String>,
|
||||
|
||||
/// expected exit gateway kem key digest (base58 encoded)
|
||||
#[arg(long, global = true, requires = "exit_gateway_kem_key_hash_function")]
|
||||
exit_gateway_kem_hey_hash_bs58: Option<String>,
|
||||
/// LP listener address for entry gateway (e.g., "192.168.66.6:41264")
|
||||
/// Used with --entry-gateway-identity for localnet mode
|
||||
#[arg(long, global = true)]
|
||||
entry_lp_address: Option<String>,
|
||||
|
||||
/// LP listener address for exit gateway (e.g., "172.18.0.5:41264")
|
||||
/// This is the address the entry gateway uses to reach exit (for forwarding)
|
||||
/// Used with --exit-gateway-identity for localnet mode
|
||||
#[arg(long, global = true)]
|
||||
exit_lp_address: Option<SocketAddr>,
|
||||
exit_lp_address: Option<String>,
|
||||
|
||||
/// Default LP control port when deriving LP address from gateway IP
|
||||
#[arg(long, global = true, default_value = "41264")]
|
||||
@@ -244,10 +202,6 @@ fn mode_to_flags(mode: TestMode) -> (bool, bool, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::todo)]
|
||||
#[allow(unreachable_code, unused)]
|
||||
// ^^^^ // NOTE: to be changed by @SW
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub(crate) async fn run() -> anyhow::Result<ProbeResult> {
|
||||
let args = CliArgs::parse();
|
||||
if !args.no_log {
|
||||
@@ -270,35 +224,21 @@ pub(crate) async fn run() -> anyhow::Result<ProbeResult> {
|
||||
// 3. Directory mode: uses nym-api directory service
|
||||
|
||||
// Localnet mode: identity provided via CLI, skip HTTP queries entirely
|
||||
if let Some(kem_key_digest) = &args.entry_gateway_kem_hey_hash_bs58 {
|
||||
if let Some(entry_identity_str) = &args.entry_gateway_identity {
|
||||
info!("Using localnet mode with CLI-provided gateway identity");
|
||||
|
||||
// SAFETY: if kem key digest is provided, all other LP data must also be present
|
||||
// (enforced by clap)
|
||||
let hash_fn: HashFunction = args
|
||||
.entry_gateway_kem_key_hash_function
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.parse()?;
|
||||
let kem_type: KEM = args.entry_gateway_kem_key_type.as_ref().unwrap().parse()?;
|
||||
let x25519_key: x25519::PublicKey =
|
||||
args.entry_gateway_x25519_key.as_ref().unwrap().parse()?;
|
||||
let identity: ed25519::PublicKey = args.entry_gateway_identity.as_ref().unwrap().parse()?;
|
||||
let digest = bs58::decode(&kem_key_digest).into_vec()?;
|
||||
|
||||
let mut expected_kem_key_hashes = HashMap::new();
|
||||
let mut digests = HashMap::new();
|
||||
digests.insert(hash_fn, digest);
|
||||
expected_kem_key_hashes.insert(kem_type, digests);
|
||||
let entry_identity = NodeIdentity::from_base58_string(entry_identity_str)?;
|
||||
|
||||
// Entry LP address: explicit or derived from gateway_ip + lp_port
|
||||
let entry_lp_addr: SocketAddr = if let Some(lp_addr) = args.entry_lp_address {
|
||||
let entry_lp_addr: SocketAddr = if let Some(lp_addr) = &args.entry_lp_address {
|
||||
lp_addr
|
||||
.parse()
|
||||
.map_err(|e| anyhow::anyhow!("Invalid entry-lp-address '{}': {}", lp_addr, e))?
|
||||
} else if let Some(gw_ip) = &args.gateway_ip {
|
||||
// Derive LP address from gateway IP
|
||||
let ip: std::net::IpAddr = gw_ip
|
||||
.parse()
|
||||
.map_err(|e| anyhow::anyhow!("Invalid gateway-ip '{gw_ip}': {e}"))?;
|
||||
.map_err(|e| anyhow::anyhow!("Invalid gateway-ip '{}': {}", gw_ip, e))?;
|
||||
SocketAddr::new(ip, args.lp_port)
|
||||
} else {
|
||||
anyhow::bail!(
|
||||
@@ -306,47 +246,20 @@ pub(crate) async fn run() -> anyhow::Result<ProbeResult> {
|
||||
);
|
||||
};
|
||||
|
||||
let entry_lp_node = TestedNodeLpDetails {
|
||||
address: entry_lp_addr,
|
||||
expected_kem_key_hashes,
|
||||
expected_signing_key_hashes: todo!(),
|
||||
x25519: x25519_key,
|
||||
};
|
||||
let entry_details = TestedNodeDetails::from_cli(identity, entry_lp_node);
|
||||
let entry_details = TestedNodeDetails::from_cli(entry_identity, entry_lp_addr);
|
||||
|
||||
// Parse exit gateway if provided
|
||||
let exit_details = if let Some(kem_key_digest) = &args.exit_gateway_kem_hey_hash_bs58 {
|
||||
let exit_lp_addr = *args.exit_lp_address.as_ref().ok_or_else(|| {
|
||||
anyhow::anyhow!("--exit-lp-address required with --exit-gateway-identity")
|
||||
})?;
|
||||
|
||||
// SAFETY: if kem key digest is provided, all other LP data must also be present
|
||||
// (enforced by clap)
|
||||
let hash_fn: HashFunction = args
|
||||
.exit_gateway_kem_key_hash_function
|
||||
let exit_details = if let Some(exit_identity_str) = &args.exit_gateway_identity {
|
||||
let exit_identity = NodeIdentity::from_base58_string(exit_identity_str)?;
|
||||
let exit_lp_addr: SocketAddr = args
|
||||
.exit_lp_address
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.parse()?;
|
||||
let kem_type: KEM = args.exit_gateway_kem_key_type.as_ref().unwrap().parse()?;
|
||||
let x25519_key: x25519::PublicKey =
|
||||
args.exit_gateway_x25519_key.as_ref().unwrap().parse()?;
|
||||
let identity: ed25519::PublicKey =
|
||||
args.exit_gateway_identity.as_ref().unwrap().parse()?;
|
||||
let digest = bs58::decode(&kem_key_digest).into_vec()?;
|
||||
|
||||
let mut expected_kem_key_hashes = HashMap::new();
|
||||
let mut digests = HashMap::new();
|
||||
digests.insert(hash_fn, digest);
|
||||
expected_kem_key_hashes.insert(kem_type, digests);
|
||||
|
||||
let exit_lp_node = TestedNodeLpDetails {
|
||||
address: exit_lp_addr,
|
||||
expected_kem_key_hashes,
|
||||
expected_signing_key_hashes: Default::default(),
|
||||
x25519: x25519_key,
|
||||
};
|
||||
|
||||
Some(TestedNodeDetails::from_cli(identity, exit_lp_node))
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("--exit-lp-address required with --exit-gateway-identity")
|
||||
})?
|
||||
.parse()
|
||||
.map_err(|e| anyhow::anyhow!("Invalid exit-lp-address: {}", e))?;
|
||||
Some(TestedNodeDetails::from_cli(exit_identity, exit_lp_addr))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ nym-exit-policy = { workspace = true }
|
||||
nym-noise-keys = { workspace = true }
|
||||
nym-wireguard-types = { workspace = true }
|
||||
nym-upgrade-mode-check = { workspace = true, features = ["openapi"] }
|
||||
nym-kkt-ciphersuite = { workspace = true }
|
||||
nym-kkt-ciphersuite = { path = "../../common/nym-kkt-ciphersuite" }
|
||||
|
||||
# feature-specific dependencies:
|
||||
|
||||
|
||||
@@ -22,28 +22,26 @@ pub struct LewesProtocol {
|
||||
/// Digests of the KEM keys available to this node alongside hashing algorithms used
|
||||
/// for their computation.
|
||||
pub kem_keys: HashMap<LPKEM, HashMap<LPHashFunction, Vec<u8>>>,
|
||||
|
||||
/// Digests of the signing keys available to this node alongside hashing algorithms used
|
||||
/// for their computation.
|
||||
pub signing_keys: HashMap<LPSignatureScheme, HashMap<LPHashFunction, Vec<u8>>>,
|
||||
}
|
||||
|
||||
impl LewesProtocol {
|
||||
pub fn new(
|
||||
enabled: bool,
|
||||
control_port: u16,
|
||||
data_port: u16,
|
||||
kem_keys: HashMap<LPKEM, HashMap<LPHashFunction, Vec<u8>>>,
|
||||
signing_keys: HashMap<LPSignatureScheme, HashMap<LPHashFunction, Vec<u8>>>,
|
||||
) -> Self {
|
||||
pub fn new(enabled: bool, control_port: u16, data_port: u16) -> Self {
|
||||
LewesProtocol {
|
||||
enabled,
|
||||
control_port,
|
||||
data_port,
|
||||
kem_keys,
|
||||
signing_keys,
|
||||
kem_keys: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_kem_key_hashes(
|
||||
mut self,
|
||||
kem: LPKEM,
|
||||
hashes: HashMap<LPHashFunction, Vec<u8>>,
|
||||
) -> Self {
|
||||
self.kem_keys.insert(kem, hashes);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// explicitly redefine available HashFunctions and KEMs so that we would not
|
||||
@@ -124,8 +122,8 @@ impl From<LPHashFunction> for nym_kkt_ciphersuite::HashFunction {
|
||||
fn from(lp_hash_fnction: LPHashFunction) -> Self {
|
||||
match lp_hash_fnction {
|
||||
LPHashFunction::Blake3 => nym_kkt_ciphersuite::HashFunction::Blake3,
|
||||
LPHashFunction::Shake128 => nym_kkt_ciphersuite::HashFunction::Shake128,
|
||||
LPHashFunction::Shake256 => nym_kkt_ciphersuite::HashFunction::Shake256,
|
||||
LPHashFunction::Shake128 => nym_kkt_ciphersuite::HashFunction::SHAKE128,
|
||||
LPHashFunction::Shake256 => nym_kkt_ciphersuite::HashFunction::SHAKE256,
|
||||
LPHashFunction::Sha256 => nym_kkt_ciphersuite::HashFunction::SHA256,
|
||||
}
|
||||
}
|
||||
@@ -135,54 +133,16 @@ impl From<nym_kkt_ciphersuite::HashFunction> for LPHashFunction {
|
||||
fn from(kem: nym_kkt_ciphersuite::HashFunction) -> Self {
|
||||
match kem {
|
||||
nym_kkt_ciphersuite::HashFunction::Blake3 => LPHashFunction::Blake3,
|
||||
nym_kkt_ciphersuite::HashFunction::Shake128 => LPHashFunction::Shake128,
|
||||
nym_kkt_ciphersuite::HashFunction::Shake256 => LPHashFunction::Shake256,
|
||||
nym_kkt_ciphersuite::HashFunction::SHAKE128 => LPHashFunction::Shake128,
|
||||
nym_kkt_ciphersuite::HashFunction::SHAKE256 => LPHashFunction::Shake256,
|
||||
nym_kkt_ciphersuite::HashFunction::SHA256 => LPHashFunction::Sha256,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
JsonSchema,
|
||||
Hash,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Display,
|
||||
EnumString,
|
||||
EnumIter,
|
||||
)]
|
||||
#[strum(serialize_all = "lowercase")]
|
||||
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
||||
pub enum LPSignatureScheme {
|
||||
Ed25519,
|
||||
}
|
||||
|
||||
impl From<LPSignatureScheme> for nym_kkt_ciphersuite::SignatureScheme {
|
||||
fn from(lp_hash_fnction: LPSignatureScheme) -> Self {
|
||||
match lp_hash_fnction {
|
||||
LPSignatureScheme::Ed25519 => nym_kkt_ciphersuite::SignatureScheme::Ed25519,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nym_kkt_ciphersuite::SignatureScheme> for LPSignatureScheme {
|
||||
fn from(kem: nym_kkt_ciphersuite::SignatureScheme) -> Self {
|
||||
match kem {
|
||||
nym_kkt_ciphersuite::SignatureScheme::Ed25519 => LPSignatureScheme::Ed25519,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nym_kkt_ciphersuite::SignatureScheme;
|
||||
|
||||
#[test]
|
||||
fn kem_display() {
|
||||
@@ -199,9 +159,4 @@ mod tests {
|
||||
assert_eq!(LPHashFunction::Shake256.to_string(), "shake256");
|
||||
assert_eq!(LPHashFunction::Sha256.to_string(), "sha256");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn signature_scheme_display() {
|
||||
assert_eq!(SignatureScheme::Ed25519.to_string(), "ed25519");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,7 @@ use nym_network_requester::{
|
||||
};
|
||||
use nym_node_metrics::NymNodeMetrics;
|
||||
use nym_node_metrics::events::MetricEventsSender;
|
||||
use nym_node_requests::api::v1::lewes_protocol::models::{
|
||||
LPHashFunction, LPKEM, LPSignatureScheme,
|
||||
};
|
||||
use nym_node_requests::api::v1::lewes_protocol::models::{LPHashFunction, LPKEM};
|
||||
use nym_node_requests::api::v1::node::models::{AnnouncePorts, NodeDescription};
|
||||
use nym_noise::config::{NoiseConfig, NoiseNetworkView};
|
||||
use nym_noise_keys::VersionedNoiseKeyV1;
|
||||
@@ -402,6 +400,7 @@ pub(crate) struct NymNode {
|
||||
sphinx_key_manager: Option<SphinxKeyManager>,
|
||||
|
||||
// to be used when noise is integrated
|
||||
#[allow(dead_code)]
|
||||
x25519_noise_keys: Arc<x25519::KeyPair>,
|
||||
}
|
||||
|
||||
@@ -645,7 +644,6 @@ impl NymNode {
|
||||
let mut gateway_tasks_builder = GatewayTasksBuilder::new(
|
||||
config.gateway,
|
||||
self.ed25519_identity_keys.clone(),
|
||||
self.x25519_noise_keys.clone(),
|
||||
self.entry_gateway.psq_kem_key.clone(),
|
||||
self.entry_gateway.client_storage.clone(),
|
||||
mix_packet_sender,
|
||||
@@ -776,7 +774,7 @@ impl NymNode {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn compute_kem_key_hashes(&self) -> HashMap<LPKEM, HashMap<LPHashFunction, Vec<u8>>> {
|
||||
fn compute_kem_key_hashes(&self) -> (LPKEM, HashMap<LPHashFunction, Vec<u8>>) {
|
||||
let kem = LPKEM::X25519;
|
||||
|
||||
let kem_key_bytes = self.entry_gateway.psq_kem_key.public_key().as_bytes();
|
||||
@@ -787,27 +785,7 @@ impl NymNode {
|
||||
.map(|(f, d)| (f.into(), d))
|
||||
.collect();
|
||||
|
||||
let mut hashes = HashMap::new();
|
||||
hashes.insert(kem, digests);
|
||||
hashes
|
||||
}
|
||||
|
||||
fn compute_signing_key_hashes(
|
||||
&self,
|
||||
) -> HashMap<LPSignatureScheme, HashMap<LPHashFunction, Vec<u8>>> {
|
||||
let scheme = LPSignatureScheme::Ed25519;
|
||||
|
||||
let kem_key_bytes = self.ed25519_identity_keys.public_key().as_bytes();
|
||||
|
||||
// convert from `nym_kkt_ciphersuite` types into `nym_nodes_requests`
|
||||
let digests = produce_key_digests(kem_key_bytes.as_ref())
|
||||
.into_iter()
|
||||
.map(|(f, d)| (f.into(), d))
|
||||
.collect();
|
||||
|
||||
let mut hashes = HashMap::new();
|
||||
hashes.insert(scheme, digests);
|
||||
hashes
|
||||
(kem, digests)
|
||||
}
|
||||
|
||||
pub(crate) async fn build_http_server(&self) -> Result<NymNodeHttpServer, NymNodeError> {
|
||||
@@ -883,13 +861,13 @@ impl NymNode {
|
||||
policy: None,
|
||||
};
|
||||
|
||||
let (kem, hashes) = self.compute_kem_key_hashes();
|
||||
let lp_details = api_requests::v1::lewes_protocol::models::LewesProtocol::new(
|
||||
self.modes().entry,
|
||||
self.config.gateway_tasks.lp.announced_control_port(),
|
||||
self.config.gateway_tasks.lp.announced_data_port(),
|
||||
self.compute_kem_key_hashes(),
|
||||
self.compute_signing_key_hashes(),
|
||||
);
|
||||
)
|
||||
.with_kem_key_hashes(kem, hashes);
|
||||
|
||||
let mut config = HttpServerConfig::new()
|
||||
.with_landing_page_assets(self.config.http.landing_page_assets_path.as_ref())
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_registration_common::NymNodeInformation;
|
||||
use nym_registration_common::NymNode;
|
||||
use nym_sdk::{
|
||||
DebugConfig, NymNetworkDetails, RememberMe, TopologyProvider, UserAgent,
|
||||
mixnet::{
|
||||
@@ -25,7 +25,7 @@ const MIXNET_CLIENT_STARTUP_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NymNodeWithKeys {
|
||||
pub node: NymNodeInformation,
|
||||
pub node: NymNode,
|
||||
pub keys: Arc<x25519::KeyPair>,
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,9 @@ pub enum RegistrationClientError {
|
||||
#[source]
|
||||
source: Box<crate::lp_client::LpClientError>,
|
||||
},
|
||||
|
||||
#[error("failed to convert ed25519 pubkey into x25519 pubkey")]
|
||||
X25519PubkeyConversionFailure,
|
||||
}
|
||||
|
||||
impl RegistrationClientError {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::config::RegistrationClientConfig;
|
||||
use crate::lp_client::helpers::to_lp_remote_peer;
|
||||
use nym_authenticator_client::{AuthClientMixnetListener, AuthenticatorClient};
|
||||
use nym_bandwidth_controller::BandwidthTicketProvider;
|
||||
use nym_credentials_interface::TicketType;
|
||||
@@ -13,7 +14,12 @@ use nym_sdk::mixnet::{EventReceiver, MixnetClient, Recipient};
|
||||
use rand::rngs::OsRng;
|
||||
use std::sync::Arc;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
mod builder;
|
||||
mod config;
|
||||
mod error;
|
||||
mod lp_client;
|
||||
mod types;
|
||||
|
||||
pub use builder::RegistrationClientBuilder;
|
||||
pub use builder::config::{
|
||||
@@ -23,17 +29,11 @@ pub use builder::config::{
|
||||
pub use config::RegistrationMode;
|
||||
pub use error::RegistrationClientError;
|
||||
pub use lp_client::{LpConfig, LpRegistrationClient, NestedLpSession, error::LpClientError};
|
||||
use nym_crypto::aes::cipher::crypto_common::rand_core::{CryptoRng, RngCore};
|
||||
use nym_lp::peer::LpRemotePeer;
|
||||
pub use types::{
|
||||
LpRegistrationResult, MixnetRegistrationResult, RegistrationResult, WireguardRegistrationResult,
|
||||
};
|
||||
|
||||
mod builder;
|
||||
mod config;
|
||||
mod error;
|
||||
mod lp_client;
|
||||
mod types;
|
||||
|
||||
pub struct RegistrationClient {
|
||||
mixnet_client: MixnetClient,
|
||||
config: RegistrationClientConfig,
|
||||
@@ -154,14 +154,7 @@ impl RegistrationClient {
|
||||
)))
|
||||
}
|
||||
|
||||
// create dedicated method taking RNG instance for tests
|
||||
async fn register_lp_with_rng<R>(
|
||||
self,
|
||||
rng: &mut R,
|
||||
) -> Result<RegistrationResult, RegistrationClientError>
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
async fn register_lp(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
// Extract and validate LP data
|
||||
let entry_lp_data = self.config.entry.node.lp_data.ok_or(
|
||||
RegistrationClientError::LpRegistrationNotPossible {
|
||||
@@ -175,52 +168,69 @@ impl RegistrationClient {
|
||||
},
|
||||
)?;
|
||||
|
||||
let entry_address = entry_lp_data.address;
|
||||
let exit_address = exit_lp_data.address;
|
||||
|
||||
tracing::debug!("Entry gateway LP address: {entry_address}");
|
||||
tracing::debug!("Exit gateway LP address: {exit_address}");
|
||||
tracing::debug!("Entry gateway LP address: {}", entry_lp_data.address);
|
||||
tracing::debug!("Exit gateway LP address: {}", exit_lp_data.address);
|
||||
|
||||
// Generate fresh Ed25519 keypairs for LP registration
|
||||
// These are ephemeral and used only for the LP handshake protocol
|
||||
let entry_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut OsRng));
|
||||
let exit_lp_keypair = Arc::new(ed25519::KeyPair::new(&mut OsRng));
|
||||
|
||||
let entry_peer = to_lp_remote_peer(self.config.entry.node.identity, entry_lp_data);
|
||||
let exit_peer = to_lp_remote_peer(self.config.exit.node.identity, exit_lp_data);
|
||||
// Step 1: Derive X25519 keys from Ed25519 for the gateways
|
||||
let entry_x25519_public = self
|
||||
.config
|
||||
.entry
|
||||
.node
|
||||
.identity
|
||||
.to_x25519()
|
||||
.map_err(|_| RegistrationClientError::X25519PubkeyConversionFailure)?;
|
||||
|
||||
// STEP 1: Establish outer session with entry gateway
|
||||
let exit_x25519_public = self
|
||||
.config
|
||||
.exit
|
||||
.node
|
||||
.identity
|
||||
.to_x25519()
|
||||
.map_err(|_| RegistrationClientError::X25519PubkeyConversionFailure)?;
|
||||
|
||||
let entry_peer = LpRemotePeer::new(self.config.entry.node.identity, entry_x25519_public)
|
||||
.with_kem_key_digests(entry_lp_data.expected_kem_key_hashes);
|
||||
|
||||
let exit_peer = LpRemotePeer::new(self.config.exit.node.identity, exit_x25519_public)
|
||||
.with_kem_key_digests(exit_lp_data.expected_kem_key_hashes);
|
||||
|
||||
// STEP 2: Establish outer session with entry gateway
|
||||
// This creates the LP session that will be used to forward packets to exit.
|
||||
// Uses packet-per-connection model: each handshake packet on new TCP connection.
|
||||
tracing::info!("Establishing outer session with entry gateway");
|
||||
let mut entry_client = LpRegistrationClient::new_with_default_config(
|
||||
entry_lp_keypair.clone(),
|
||||
entry_peer,
|
||||
entry_address,
|
||||
entry_lp_data.address,
|
||||
self.config.entry.node.ip_address,
|
||||
);
|
||||
|
||||
// Perform handshake with entry gateway (outer session now established)
|
||||
entry_client.perform_handshake().await.map_err(|source| {
|
||||
RegistrationClientError::EntryGatewayRegisterLp {
|
||||
gateway_id: self.config.entry.node.identity.to_base58_string(),
|
||||
lp_address: entry_address,
|
||||
lp_address: entry_lp_data.address,
|
||||
source: Box::new(source),
|
||||
}
|
||||
})?;
|
||||
|
||||
tracing::info!("Outer session with entry gateway established");
|
||||
|
||||
// STEP 2: Use nested session to register with exit gateway via forwarding
|
||||
// STEP 3: Use nested session to register with exit gateway via forwarding
|
||||
// This hides the client's IP address from the exit gateway
|
||||
tracing::info!("Registering with exit gateway via entry forwarding");
|
||||
let mut nested_session =
|
||||
NestedLpSession::new(exit_address.to_string(), exit_lp_keypair, exit_peer);
|
||||
NestedLpSession::new(exit_lp_data.address.to_string(), exit_lp_keypair, exit_peer);
|
||||
|
||||
// Perform handshake and registration with exit gateway (all via entry forwarding)
|
||||
let exit_gateway_data = nested_session
|
||||
.handshake_and_register::<TcpStream, _>(
|
||||
.handshake_and_register::<TcpStream>(
|
||||
&mut entry_client,
|
||||
rng,
|
||||
&self.config.exit.keys,
|
||||
&self.config.exit.node.identity,
|
||||
&*self.bandwidth_controller,
|
||||
@@ -229,17 +239,16 @@ impl RegistrationClient {
|
||||
.await
|
||||
.map_err(|source| RegistrationClientError::ExitGatewayRegisterLp {
|
||||
gateway_id: self.config.exit.node.identity.to_base58_string(),
|
||||
lp_address: exit_address,
|
||||
lp_address: exit_lp_data.address,
|
||||
source: Box::new(source),
|
||||
})?;
|
||||
|
||||
tracing::info!("Exit gateway registration completed via forwarding");
|
||||
|
||||
// STEP 3: Register with entry gateway (packet-per-connection)
|
||||
// STEP 4: Register with entry gateway (packet-per-connection)
|
||||
tracing::info!("Registering with entry gateway");
|
||||
let entry_gateway_data = entry_client
|
||||
.register(
|
||||
rng,
|
||||
&self.config.entry.keys,
|
||||
&self.config.entry.node.identity,
|
||||
&*self.bandwidth_controller,
|
||||
@@ -248,7 +257,7 @@ impl RegistrationClient {
|
||||
.await
|
||||
.map_err(|source| RegistrationClientError::EntryGatewayRegisterLp {
|
||||
gateway_id: self.config.entry.node.identity.to_base58_string(),
|
||||
lp_address: entry_address,
|
||||
lp_address: entry_lp_data.address,
|
||||
source: Box::new(source),
|
||||
})?;
|
||||
|
||||
@@ -267,12 +276,6 @@ impl RegistrationClient {
|
||||
})))
|
||||
}
|
||||
|
||||
async fn register_lp(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
self.register_lp_with_rng(&mut rng).await
|
||||
}
|
||||
|
||||
pub async fn register(self) -> Result<RegistrationResult, RegistrationClientError> {
|
||||
self.cancel_token
|
||||
.clone()
|
||||
|
||||
@@ -19,10 +19,9 @@ use nym_lp::message::ForwardPacketData;
|
||||
use nym_lp::peer::{LpLocalPeer, LpRemotePeer};
|
||||
use nym_lp::state_machine::{LpAction, LpData, LpInput, LpStateMachine};
|
||||
use nym_lp_transport::traits::LpTransport;
|
||||
use nym_registration_common::{LpRegistrationRequest, WireguardConfiguration};
|
||||
use nym_registration_common::{GatewayData, LpRegistrationRequest};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::net::SocketAddr;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
@@ -55,6 +54,9 @@ pub struct LpRegistrationClient<S = TcpStream> {
|
||||
/// Created during handshake initiation. Persists across packet-per-connection calls.
|
||||
state_machine: Option<LpStateMachine>,
|
||||
|
||||
/// Client's IP address for registration metadata.
|
||||
client_ip: IpAddr,
|
||||
|
||||
/// Configuration for timeouts and TCP parameters.
|
||||
config: LpConfig,
|
||||
|
||||
@@ -73,6 +75,7 @@ where
|
||||
/// * `local_ed25519_keypair` - Client's Ed25519 identity keypair
|
||||
/// * `gateway_lp_peer` - Encapsulates all the gateway keys needed for the Lewes Protocol
|
||||
/// * `gateway_lp_address` - Gateway's LP listener socket address
|
||||
/// * `client_ip` - Client IP address for registration
|
||||
/// * `config` - Configuration for timeouts and TCP parameters (use `LpConfig::default()`)
|
||||
///
|
||||
/// # Note
|
||||
@@ -81,6 +84,7 @@ where
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
gateway_lp_address: SocketAddr,
|
||||
client_ip: IpAddr,
|
||||
config: LpConfig,
|
||||
) -> Self {
|
||||
let local_x25519_keypair = local_ed25519_keypair.to_x25519();
|
||||
@@ -90,6 +94,7 @@ where
|
||||
gateway_lp_peer,
|
||||
gateway_lp_address,
|
||||
state_machine: None,
|
||||
client_ip,
|
||||
config,
|
||||
stream: None,
|
||||
}
|
||||
@@ -110,11 +115,13 @@ where
|
||||
local_ed25519_keypair: Arc<ed25519::KeyPair>,
|
||||
gateway_lp_peer: LpRemotePeer,
|
||||
gateway_lp_address: SocketAddr,
|
||||
client_ip: IpAddr,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
local_ed25519_keypair,
|
||||
gateway_lp_peer,
|
||||
gateway_lp_address,
|
||||
client_ip,
|
||||
LpConfig::default(),
|
||||
)
|
||||
}
|
||||
@@ -133,6 +140,11 @@ where
|
||||
self.gateway_lp_address
|
||||
}
|
||||
|
||||
/// Returns the client's IP address.
|
||||
pub fn client_ip(&self) -> IpAddr {
|
||||
self.client_ip
|
||||
}
|
||||
|
||||
/// Returns reference to the established connection between the client and the gateway.
|
||||
pub fn connection(&self) -> &Option<S> {
|
||||
&self.stream
|
||||
@@ -663,7 +675,6 @@ where
|
||||
/// for that please use [`Self::register_with_retry`] instead
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `rng` - RNG instance for generating PSK
|
||||
/// * `wg_keypair` - Client's WireGuard x25519 keypair
|
||||
/// * `gateway_identity` - Gateway's ed25519 identity for credential verification
|
||||
/// * `bandwidth_controller` - Provider for bandwidth credentials
|
||||
@@ -680,17 +691,13 @@ where
|
||||
/// - Network communication fails
|
||||
/// - Gateway rejected the registration
|
||||
/// - Response times out (see LpConfig::registration_timeout)
|
||||
pub async fn register<R>(
|
||||
pub async fn register(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
wg_keypair: &x25519::KeyPair,
|
||||
gateway_identity: &ed25519::PublicKey,
|
||||
bandwidth_controller: &dyn BandwidthTicketProvider,
|
||||
ticket_type: TicketType,
|
||||
) -> Result<WireguardConfiguration>
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
) -> Result<GatewayData> {
|
||||
tracing::debug!("Acquiring bandwidth credential for registration");
|
||||
|
||||
// Get bandwidth credential from controller
|
||||
@@ -704,7 +711,7 @@ where
|
||||
})?
|
||||
.data;
|
||||
|
||||
self.register_with_credential(rng, wg_keypair, credential, ticket_type)
|
||||
self.register_with_credential(wg_keypair, credential, ticket_type)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -714,7 +721,6 @@ where
|
||||
/// Uses the persistent TCP connection established during handshake.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `rng` - RNG instance for generating PSK
|
||||
/// * `wg_keypair` - Client's WireGuard x25519 keypair
|
||||
/// * `credential` - Pre-generated bandwidth credential
|
||||
/// * `ticket_type` - Type of bandwidth ticket
|
||||
@@ -728,21 +734,17 @@ where
|
||||
///
|
||||
/// # Panics / Errors
|
||||
/// Returns error if handshake not completed or if connection was closed.
|
||||
pub async fn register_with_credential<R>(
|
||||
pub async fn register_with_credential(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
wg_keypair: &x25519::KeyPair,
|
||||
credential: CredentialSpendingData,
|
||||
ticket_type: TicketType,
|
||||
) -> Result<WireguardConfiguration>
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
) -> Result<GatewayData> {
|
||||
tracing::debug!("Sending registration request (persistent connection)");
|
||||
|
||||
// 1. Build registration request
|
||||
let wg_public_key = PeerPublicKey::new(wg_keypair.public_key().to_bytes().into());
|
||||
let request = LpRegistrationRequest::new_dvpn(rng, wg_public_key, credential, ticket_type);
|
||||
let request = LpRegistrationRequest::new_dvpn(wg_public_key, credential, ticket_type);
|
||||
|
||||
tracing::trace!("Built registration request: {:?}", request);
|
||||
|
||||
@@ -861,7 +863,6 @@ where
|
||||
/// will return the cached result instead of spending a new credential.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `rng` - RNG instance for generating PSK
|
||||
/// * `wg_keypair` - Client's WireGuard x25519 keypair (same key used for all retries)
|
||||
/// * `gateway_identity` - Gateway's ed25519 identity for credential verification
|
||||
/// * `bandwidth_controller` - Provider for bandwidth credentials
|
||||
@@ -877,18 +878,14 @@ where
|
||||
/// # Note
|
||||
/// Unlike `register()`, this method handles the full flow including handshake.
|
||||
/// Do NOT call `perform_handshake()` before this method.
|
||||
pub async fn register_with_retry<R>(
|
||||
pub async fn register_with_retry(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
wg_keypair: &x25519::KeyPair,
|
||||
gateway_identity: &ed25519::PublicKey,
|
||||
bandwidth_controller: &dyn BandwidthTicketProvider,
|
||||
ticket_type: TicketType,
|
||||
max_retries: u32,
|
||||
) -> Result<WireguardConfiguration>
|
||||
where
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
) -> Result<GatewayData> {
|
||||
tracing::debug!("Starting resilient registration (max_retries={max_retries})",);
|
||||
|
||||
// Acquire credential ONCE before any attempts
|
||||
@@ -932,7 +929,7 @@ where
|
||||
}
|
||||
|
||||
match self
|
||||
.register_with_credential(rng, wg_keypair, credential.clone(), ticket_type)
|
||||
.register_with_credential(wg_keypair, credential.clone(), ticket_type)
|
||||
.await
|
||||
{
|
||||
Ok(data) => {
|
||||
@@ -1182,14 +1179,17 @@ mod tests {
|
||||
let gateway_peer =
|
||||
LpRemotePeer::new(*gateway_ed_keys.public_key(), *gateway_x_keys.public_key());
|
||||
let address = "127.0.0.1:41264".parse().unwrap();
|
||||
let client_ip = "192.168.1.100".parse().unwrap();
|
||||
|
||||
let client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
keypair,
|
||||
gateway_peer,
|
||||
address,
|
||||
client_ip,
|
||||
);
|
||||
|
||||
assert!(!client.is_handshake_complete());
|
||||
assert_eq!(client.gateway_address(), address);
|
||||
assert_eq!(client.client_ip(), client_ip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::LpClientError;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_lp::message::ForwardPacketData;
|
||||
use nym_lp::peer::LpRemotePeer;
|
||||
use nym_lp::state_machine::{LpAction, LpData, LpDataKind, LpInput};
|
||||
use nym_registration_common::{
|
||||
LpRegistrationRequest, LpRegistrationResponse, NymNodeLPInformation,
|
||||
};
|
||||
use nym_registration_common::{LpRegistrationRequest, LpRegistrationResponse};
|
||||
|
||||
pub(crate) fn convert_registration_request(
|
||||
request: LpRegistrationRequest,
|
||||
@@ -85,13 +81,3 @@ pub(crate) fn try_convert_forward_response(action: LpAction) -> Result<Vec<u8>,
|
||||
|
||||
Ok(response_data.content.into())
|
||||
}
|
||||
|
||||
pub(crate) fn to_lp_remote_peer(
|
||||
identity: ed25519::PublicKey,
|
||||
data: NymNodeLPInformation,
|
||||
) -> LpRemotePeer {
|
||||
LpRemotePeer::new(identity, data.x25519).with_key_digests(
|
||||
data.expected_kem_key_hashes,
|
||||
data.expected_signing_key_hashes,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -30,9 +30,8 @@ use nym_lp::peer::{LpLocalPeer, LpRemotePeer};
|
||||
use nym_lp::state_machine::{LpAction, LpInput, LpStateMachine};
|
||||
use nym_lp::{LpMessage, LpPacket};
|
||||
use nym_lp_transport::traits::LpTransport;
|
||||
use nym_registration_common::{LpRegistrationRequest, WireguardConfiguration};
|
||||
use nym_registration_common::{GatewayData, LpRegistrationRequest};
|
||||
use nym_wireguard_types::PeerPublicKey;
|
||||
use rand::{CryptoRng, RngCore};
|
||||
use std::sync::Arc;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
@@ -288,17 +287,15 @@ impl NestedLpSession {
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Ok(GatewayData)` - Exit gateway configuration data on successful registration
|
||||
pub async fn handshake_and_register_with_credential<S, R>(
|
||||
pub async fn handshake_and_register_with_credential<S>(
|
||||
&mut self,
|
||||
outer_client: &mut LpRegistrationClient<S>,
|
||||
rng: &mut R,
|
||||
wg_keypair: &x25519::KeyPair,
|
||||
credential: nym_credentials_interface::CredentialSpendingData,
|
||||
ticket_type: TicketType,
|
||||
) -> Result<WireguardConfiguration>
|
||||
) -> Result<GatewayData>
|
||||
where
|
||||
S: LpTransport + Unpin,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
// Step 1: Perform handshake with exit gateway via forwarding
|
||||
self.perform_handshake(outer_client).await?;
|
||||
@@ -314,7 +311,7 @@ impl NestedLpSession {
|
||||
|
||||
// Step 3: Build registration request (credential already provided)
|
||||
let wg_public_key = PeerPublicKey::new(wg_keypair.public_key().to_bytes().into());
|
||||
let request = LpRegistrationRequest::new_dvpn(rng, wg_public_key, credential, ticket_type);
|
||||
let request = LpRegistrationRequest::new_dvpn(wg_public_key, credential, ticket_type);
|
||||
|
||||
tracing::trace!("Built registration request: {:?}", request);
|
||||
|
||||
@@ -428,18 +425,16 @@ impl NestedLpSession {
|
||||
/// - Forwarding through entry gateway fails
|
||||
/// - Response decryption/deserialization fails
|
||||
/// - Gateway rejects the registration
|
||||
pub async fn handshake_and_register<S, R>(
|
||||
pub async fn handshake_and_register<S>(
|
||||
&mut self,
|
||||
outer_client: &mut LpRegistrationClient<S>,
|
||||
rng: &mut R,
|
||||
wg_keypair: &x25519::KeyPair,
|
||||
gateway_identity: &ed25519::PublicKey,
|
||||
bandwidth_controller: &dyn BandwidthTicketProvider,
|
||||
ticket_type: TicketType,
|
||||
) -> Result<WireguardConfiguration>
|
||||
) -> Result<GatewayData>
|
||||
where
|
||||
S: LpTransport + Unpin,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
// Step 1: Perform handshake with exit gateway via forwarding
|
||||
self.perform_handshake(outer_client).await?;
|
||||
@@ -466,7 +461,7 @@ impl NestedLpSession {
|
||||
|
||||
// Step 4: Build registration request
|
||||
let wg_public_key = PeerPublicKey::new(wg_keypair.public_key().to_bytes().into());
|
||||
let request = LpRegistrationRequest::new_dvpn(rng, wg_public_key, credential, ticket_type);
|
||||
let request = LpRegistrationRequest::new_dvpn(wg_public_key, credential, ticket_type);
|
||||
|
||||
tracing::trace!("Built registration request: {:?}", request);
|
||||
|
||||
@@ -582,19 +577,17 @@ impl NestedLpSession {
|
||||
/// # Errors
|
||||
/// Returns an error if all retry attempts fail.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn handshake_and_register_with_retry<S, R>(
|
||||
pub async fn handshake_and_register_with_retry<S>(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
outer_client: &mut LpRegistrationClient<S>,
|
||||
wg_keypair: &x25519::KeyPair,
|
||||
gateway_identity: &ed25519::PublicKey,
|
||||
bandwidth_controller: &dyn BandwidthTicketProvider,
|
||||
ticket_type: TicketType,
|
||||
max_retries: u32,
|
||||
) -> Result<WireguardConfiguration>
|
||||
) -> Result<GatewayData>
|
||||
where
|
||||
S: LpTransport + Unpin,
|
||||
R: RngCore + CryptoRng,
|
||||
{
|
||||
tracing::debug!(
|
||||
"Starting resilient exit registration (max_retries={})",
|
||||
@@ -642,7 +635,6 @@ impl NestedLpSession {
|
||||
match self
|
||||
.handshake_and_register_with_credential(
|
||||
outer_client,
|
||||
rng,
|
||||
wg_keypair,
|
||||
credential.clone(),
|
||||
ticket_type,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use nym_authenticator_client::{AuthClientMixnetListenerHandle, AuthenticatorClient};
|
||||
use nym_bandwidth_controller::BandwidthTicketProvider;
|
||||
use nym_registration_common::{AssignedAddresses, WireguardConfiguration};
|
||||
use nym_registration_common::{AssignedAddresses, GatewayData};
|
||||
use nym_sdk::mixnet::{EventReceiver, MixnetClient};
|
||||
|
||||
pub enum RegistrationResult {
|
||||
@@ -21,8 +21,8 @@ pub struct MixnetRegistrationResult {
|
||||
pub struct WireguardRegistrationResult {
|
||||
pub entry_gateway_client: AuthenticatorClient,
|
||||
pub exit_gateway_client: AuthenticatorClient,
|
||||
pub entry_gateway_data: WireguardConfiguration,
|
||||
pub exit_gateway_data: WireguardConfiguration,
|
||||
pub entry_gateway_data: GatewayData,
|
||||
pub exit_gateway_data: GatewayData,
|
||||
pub authenticator_listener_handle: AuthClientMixnetListenerHandle,
|
||||
pub bw_controller: Box<dyn BandwidthTicketProvider>,
|
||||
}
|
||||
@@ -39,10 +39,10 @@ pub struct WireguardRegistrationResult {
|
||||
/// * `bw_controller` - Bandwidth ticket provider for credential management
|
||||
pub struct LpRegistrationResult {
|
||||
/// Gateway configuration data from entry gateway
|
||||
pub entry_gateway_data: WireguardConfiguration,
|
||||
pub entry_gateway_data: GatewayData,
|
||||
|
||||
/// Gateway configuration data from exit gateway
|
||||
pub exit_gateway_data: WireguardConfiguration,
|
||||
pub exit_gateway_data: GatewayData,
|
||||
|
||||
/// Bandwidth controller for credential management
|
||||
pub bw_controller: Box<dyn BandwidthTicketProvider>,
|
||||
|
||||
Generated
-1
@@ -4229,7 +4229,6 @@ dependencies = [
|
||||
"nym-crypto",
|
||||
"nym-ecash-signer-check-types",
|
||||
"nym-ecash-time",
|
||||
"nym-kkt-ciphersuite",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-network-defaults",
|
||||
"nym-node-requests",
|
||||
|
||||
@@ -28,7 +28,7 @@ url = { workspace = true }
|
||||
# Nym crates
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-crypto = { path = "../../common/crypto" }
|
||||
nym-kkt-ciphersuite = { workspace = true }
|
||||
nym-kkt-ciphersuite = { path = "../../common/nym-kkt-ciphersuite" }
|
||||
nym-http-api-client = { path = "../../common/http-api-client" }
|
||||
nym-kcp = { path = "../../common/nym-kcp" }
|
||||
nym-lp = { path = "../../common/nym-lp" }
|
||||
|
||||
@@ -117,16 +117,16 @@ impl SpeedtestClient {
|
||||
self.gateway.lp_address
|
||||
);
|
||||
|
||||
let client_ip = "0.0.0.0".parse()?;
|
||||
|
||||
let gw_peer = LpRemotePeer::new(self.gateway.identity, self.gateway.identity.to_x25519()?)
|
||||
.with_key_digests(
|
||||
self.gateway.kem_key_hashes.clone(),
|
||||
self.gateway.signing_key_hashes.clone(),
|
||||
);
|
||||
.with_kem_key_digests(self.gateway.kem_key_hashes.clone());
|
||||
|
||||
let mut lp_client = LpRegistrationClient::<TcpStream>::new_with_default_config(
|
||||
self.identity_keypair.clone(),
|
||||
gw_peer,
|
||||
self.gateway.lp_address,
|
||||
client_ip,
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
@@ -163,16 +163,16 @@ impl SpeedtestClient {
|
||||
self.gateway.lp_address
|
||||
);
|
||||
|
||||
let client_ip = "0.0.0.0".parse()?;
|
||||
|
||||
let gw_peer = LpRemotePeer::new(self.gateway.identity, self.gateway.identity.to_x25519()?)
|
||||
.with_key_digests(
|
||||
self.gateway.kem_key_hashes.clone(),
|
||||
self.gateway.signing_key_hashes.clone(),
|
||||
);
|
||||
.with_kem_key_digests(self.gateway.kem_key_hashes.clone());
|
||||
|
||||
let mut lp_client = LpRegistrationClient::new_with_default_config(
|
||||
self.identity_keypair.clone(),
|
||||
gw_peer,
|
||||
self.gateway.lp_address,
|
||||
client_ip,
|
||||
);
|
||||
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -10,7 +10,7 @@ use nym_api_requests::models::{LPHashFunction, LPKEM};
|
||||
use nym_api_requests::nym_nodes::SkimmedNode;
|
||||
use nym_crypto::asymmetric::ed25519;
|
||||
use nym_http_api_client::UserAgent;
|
||||
use nym_kkt_ciphersuite::{KEMKeyDigests, SignatureScheme, SigningKeyDigests, KEM};
|
||||
use nym_kkt_ciphersuite::{KEMKeyDigests, KEM};
|
||||
use nym_sphinx_types::Node as SphinxNode;
|
||||
use nym_topology::{NymRouteProvider, NymTopology, NymTopologyMetadata};
|
||||
use nym_validator_client::nym_api::NymApiClientExt;
|
||||
@@ -31,7 +31,6 @@ const LP_DATA_PORT: u16 = 51264;
|
||||
pub struct GatewayInfo {
|
||||
pub identity: ed25519::PublicKey,
|
||||
pub kem_key_hashes: HashMap<KEM, KEMKeyDigests>,
|
||||
pub signing_key_hashes: HashMap<SignatureScheme, SigningKeyDigests>,
|
||||
|
||||
pub sphinx_key: nym_crypto::asymmetric::x25519::PublicKey,
|
||||
/// Mix host (IP:port for Sphinx mixing)
|
||||
|
||||
Reference in New Issue
Block a user