* Add nymkkt with KKT convenience wrappers for nym-lp integration
Integrates nymkkt module from georgio/noise-psq branch to enable
post-quantum key distribution for nym-lp.
Changes:
- Add common/nymkkt from georgio/noise-psq (KKT protocol implementation)
- Add convenience wrapper layer (kkt.rs) with simplified API:
- request_kem_key() - Client requests gateway's KEM key
- validate_kem_response() - Client validates signed response
- handle_kem_request() - Gateway handles requests
- Add nymkkt to workspace members in root Cargo.toml
- Export kkt module in lib.rs
The KKT (Key Encapsulation Mechanism Transport) protocol enables efficient
distribution of post-quantum KEM public keys. Instead of storing large PQ
keys in the directory (1KB-500KB), we store 32-byte hashes and fetch actual
keys on-demand via this authenticated protocol.
Tests: All 5 unit tests passing (authenticated, anonymous, signature
verification, hash validation)
* feat(lp): add Ed25519 authentication to PSQ protocol
Replace basic PSQ v0 API with authenticated v1 API that includes
cryptographic authentication via Ed25519 signatures.
Changes:
- PSQ initiator now signs encapsulated keys with Ed25519 private key
- PSQ responder verifies Ed25519 signatures before deriving PSK
- Prevents MITM attacks through mutual authentication
- Fixed test helpers to use role-based Ed25519 keypair assignment
(initiator uses [1u8;32], responder uses [2u8;32])
Security: This adds a critical authentication layer to the post-quantum
PSK derivation protocol, ensuring both parties can verify each other's
identity during the handshake.
Tests: All 77 tests passing (was 11 failures, now 0)
* feat(lp): integrate PSQ post-quantum PSK derivation
Complete integration of Post-Quantum Secure (PSQ) protocol for PSK
derivation in the Lewes Protocol, replacing simple Blake3 derivation
with cryptographically secure DHKEM-based PSK establishment.
This commit encompasses three completed tasks:
- Add KKTRequest/KKTResponse message types to LpMessage enum
- Update codec to handle KKT message serialization/deserialization
- Add kkt_orchestrator.rs with high-level KKT API wrappers
- Enable key exchange orchestration for PSQ protocol
- Add set_psk() method to NoiseProtocol for dynamic PSK injection
- Integrate PSQ derivation into LpSession handshake flow
- PSQ payload embedded in first Noise message (ClientHello)
- Derive PSK using libcrux-psq before Noise handshake completion
- Add helper functions for X25519 to KEM conversions
- Add comprehensive PSQ integration tests in session_integration/
- Test PSQ handshake end-to-end flow
- Validate PSK derivation correctness between initiator/responder
- Test PSQ + Noise combined protocol operation
Dependencies:
- libcrux-psq: Post-quantum PSK protocol implementation
- libcrux-kem: Key Encapsulation Mechanism primitives
- nym-kkt: KKT key exchange protocol wrappers
- rand 0.9: Required for KKT compatibility
Security: This adds Harvest-Now-Decrypt-Later (HNDL) resistance by
combining classical ECDH with post-quantum KEM for PSK derivation.
Even if X25519 is broken by quantum computers, the PSK remains secure.
Tests: All 77 tests passing
* feat(lp): add PSQ error handling documentation and tests (nym-bbi)
Formalize the "always abort" error handling strategy for PSQ failures.
PSQ errors indicate attacks, misconfigurations, or protocol violations
that should not be silently ignored or worked around.
Changes:
- Add comprehensive error handling documentation to psk.rs module
- Add diagnostic logging with error categorization:
* CredError → warn about potential attack
* TimestampElapsed → warn about potential replay
* Other errors → log as errors
- Add 4 error scenario tests:
* test_psq_deserialization_failure
* test_handshake_abort_on_psq_failure
* test_psq_invalid_signature
* test_psq_state_unchanged_on_error
- Add log dependency to Cargo.toml
Error handling strategy: All PSQ failures abort the handshake cleanly
with no retry or fallback. This prevents silent security degradation
and ensures misconfigurations are detected early.
State guarantees: PSQ errors leave session in clean state - dummy PSK
remains, Noise HandshakeState unchanged, no partial data, no cleanup needed.
Tests: 81 tests passing (77 original + 4 new error tests)
Closes: nym-bbi
* feat(lp): add PSK injection tracking to prevent dummy PSK usage (nym-ep2)
Add safety mechanism to ensure real post-quantum PSK was injected before
allowing transport mode operations (encrypt/decrypt). This prevents
accidentally using the insecure dummy PSK [0u8; 32] if PSQ injection fails.
Changes:
- Add `psk_injected: AtomicBool` field to LpSession
- Initialize to `false` in LpSession::new()
- Set to `true` after successful PSK injection:
* Initiator: In prepare_handshake_message() after set_psk()
* Responder: In process_handshake_message() after set_psk()
- Add NoiseError::PskNotInjected error variant
- Add PSK injection checks in encrypt_data() and decrypt_data()
* Check happens before handshake completion check
* Returns PskNotInjected if flag is false
- Add comprehensive PSK injection lifecycle documentation to LpSession
- Add test_transport_fails_without_psk_injection test
- Update test_encrypt_decrypt_before_handshake to expect PskNotInjected
PSK Injection Lifecycle:
1. Session created with dummy PSK [0u8; 32] in Noise HandshakeState
2. During handshake, PSQ runs and derives real post-quantum PSK
3. Real PSK injected via set_psk() - psk_injected flag set to true
4. Handshake completes, transport mode available
5. Transport operations check psk_injected flag for safety
This is defensive programming - normal PSQ flow always injects the real PSK.
The safety check prevents transport mode if PSQ somehow fails silently or is
bypassed due to implementation bugs.
Tests: 82 tests passing (81 original + 1 new)
Closes: nym-ep2
* docs(lp): fix PSK state documentation inaccuracy
Correct error handling documentation to clarify that PSK slot 3
remains unmodified only on error, not in all cases.
Previous: "PSK slot 3 = dummy [0u8; 32] (never modified)"
Corrected: "PSK slot 3 = dummy [0u8; 32] (not modified on error)"
This is more accurate since:
- On error: PSK remains as dummy value (never injected)
- On success: PSK is replaced with real post-quantum PSK
Documentation-only change, no functional impact.
* feat(lp): add KKTExchange state to state machine for pre-handshake KEM key transfer (nym-4za)
Add KKTExchange state to LpStateMachine to properly orchestrate KKT (KEM Key Transfer)
protocol before Noise handshake begins. This enables dynamic KEM public key exchange,
allowing post-quantum KEM algorithms to be used without pre-published keys.
Changes:
- Add KKTExchange state and KKTComplete action to state machine
- Implement automatic KKT exchange on StartHandshake:
* Initiator: sends KKT request → waits for response → validates signature
* Responder: waits for request → validates → sends signed KEM key
- Update process_kkt_response() to accept Option<&[u8]> for hash validation:
* Some(hash): full KKT validation with directory hash (future)
* None: signature-only mode (current deployment)
- Add local_x25519_public() helper for responder KEM key derivation
- Update state flow: ReadyToHandshake → KKTExchange → Handshaking → Transport
- Add PSK handle storage (psk_handle) for future re-registration
- Export generate_fresh_salt() for session creation
- Update psq_responder_process_message() to return encrypted PSK handle (ctxt_B)
- Add comprehensive tests:
* test_kkt_exchange_initiator_flow
* test_kkt_exchange_responder_flow
* test_kkt_exchange_full_roundtrip
* test_kkt_exchange_close
* test_kkt_exchange_rejects_invalid_inputs
* Updated test_state_machine_simplified_flow for KKT phase
All tests passing. Ready for nym-8y5 (PSQ handshake KKT integration).
* docs(lp): add state machine and post-quantum security protocol documentation
Add comprehensive documentation of the Lewes Protocol state machine and
post-quantum security architecture to LP_PROTOCOL.md.
New sections:
- State Machine and Security Protocol overview
- Detailed state transition diagram (ReadyToHandshake → KKTExchange → Handshaking → Transport)
- Complete message sequence diagram showing KKT + PSQ + Noise flow
- KKT (KEM Key Transfer) protocol specification
- PSQ (Post-Quantum Secure PSK) protocol details
- Security guarantees and implementation status
- Algorithm choices (current X25519, future ML-KEM-768)
- Message type specifications for KKT
- Version 1.1 changelog entry documenting KKT/PSQ integration
Documentation includes:
- ASCII art state machine diagram
- Message sequence diagram with all protocol phases
- PSK derivation formulas
- Security properties checklist
- Migration path to post-quantum KEMs
- Integration details (PSQ embedded in Noise, no extra round-trips)
Related to nym-4za (KKTExchange state implementation).
* feat(lp): use KKT-authenticated KEM key in PSQ handshake (nym-8y5)
Replace direct X25519→KEM conversion with KKT-derived authenticated key
in PSQ initiator flow. This ensures PSQ uses the responder's authenticated
KEM public key obtained via KKT protocol instead of blindly converting
their X25519 key, properly completing the post-quantum security chain.
Changes:
- session.rs: Extract KEM key from KKTState::Completed in prepare_handshake_message()
- session.rs: Add set_kkt_completed_for_test() helper for test initialization
- session.rs: Update create_handshake_test_session() to initialize KKT state
- session.rs: Fix test_handshake_abort_on_psq_failure and test_psq_invalid_signature
- session_manager.rs: Add init_kkt_for_test() for integration test setup
- session_integration/mod.rs: Update tests for KKT-first flow (6 rounds total)
- session_integration/mod.rs: Fix state machine test expectations for KKTExchange state
All 87 tests passing. Unblocks nym-w8f (KKT tests) and nym-m15 (production integration).
* feat(lp): simplify API to Ed25519-only, derive X25519 internally
Refactored LP state machine to use Ed25519 keys exclusively in the public
API, with X25519 keys derived internally via RFC 7748. This simplifies the
API from 6 parameters to 4 while maintaining protocol security.
**Core API Changes:**
- LpStateMachine::new(): Removed explicit X25519 keypair parameters
- Old: new(is_initiator, local_keypair, local_ed25519_keypair,
remote_public_key, remote_ed25519_key, salt)
- New: new(is_initiator, local_ed25519_keypair, remote_ed25519_key, salt)
- X25519 keys now derived internally from Ed25519 using RFC 7748
- lp_id calculation moved inside state machine (uses derived X25519 keys)
**Protocol Changes:**
- ClientHello message extended from 65 to 97 bytes
- Now includes client_ed25519_public_key field (32 bytes)
- Required for PSQ authentication in KKT + PSQ handshake flow
- Breaking change: gateway must extract Ed25519 from ClientHello
**Gateway Updates:**
- receive_client_hello() now extracts Ed25519 public key
- LpGatewayHandshake::new_responder() accepts Ed25519 keys only
- Removed manual X25519 conversion (handled by state machine)
**Registration Client Updates:**
- LpRegistrationClient now uses Ed25519 keypairs
- Generate fresh ephemeral Ed25519 keys for LP registration
- ClientHello includes Ed25519 public key for gateway authentication
- Fixed 7 pre-existing build errors:
* mixnet_client_startup_timeout field removal
* IprClientConnect API change (async → sync)
* Error variant renames (use helper function)
* LP client key type mismatches (X25519 → Ed25519)
**Test Suite:**
- Updated 16+ test functions to use new 4-parameter constructor
- Fixed 5 integration test failures caused by lp_id mismatch
- Tests now derive X25519 from Ed25519 (matching production behavior)
- Added missing PublicKey imports in test modules
- All 87 tests passing (100% success rate)
**Implementation Details:**
- Added Ed25519RecoveryError variant to LpError enum
- Type conversion: nym_crypto X25519 → nym_lp keypair types
- Maintained backward compatibility for PSQ/KKT protocol flow
- Session manager updated to use new API signature
This change completes the Ed25519-only API migration, hiding X25519 as an
implementation detail while preserving all security properties of the
KKT-authenticated PSQ handshake protocol.
* chore: run cargo fmt
* chore: run cargo clippy --fix to resolve simple linter issues
* Basic handshake working
* Final tweaks
* Wrap PR comments, 2024
---------
Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
* added new dkg execute methods for ownership transfer and announce address update
* cherry-pick TestableNymContract for the dkg contract from #5091
* tests
* schema fixes
* removed old queued migrations
* feat: unify HTTP client creation and enable domain fronting
Enhanced the base nym_http_api_client to reduce fragmentation and enable domain fronting:
- Added SerializationFormat enum for explicit JSON/bincode choice (no auto-detection)
- Added from_network() method to create clients from NymNetworkDetails with domain fronting
- Added with_bincode() builder method for explicit serialization configuration
- Set Accept header based on serialization preference
- Added deprecation paths for NymApiClient wrapper and nym_api::Client re-export
- Enabled domain fronting support via network defaults feature
This is part of a broader effort to consolidate HTTP client implementations across the codebase,
reducing ~500 lines of wrapper code and providing automatic domain fronting for censorship resistance.
* feat: migrate NymApiClient usage to unified HTTP client
- Wire up domain fronting configuration in NymNetworkDetails
- Implement NymApiClientExt trait for base nym_http_api_client::Client
- Migrate direct NymApiClient usage in multiple components:
- nym-network-monitor
- verloc measurements
- connection tester
- coconut/ecash client
- validator rewarder
- Add Copy derive to ApiUrlConst to enable iteration
- Update error handling and Display implementations
This enables automatic domain fronting for all Nym API calls via the configured CDN front hosts.
* fix: resolve all compilation errors after NymApiClient migration
- Add missing nym-http-api-client dependencies to multiple crates
- Add NymApiClientExt trait imports where needed
- Fix type mismatches from NymApiClient to unified Client
- Add error conversions for NymAPIError in various error enums
- Implement missing trait methods (get_current_rewarded_set, get_all_basic_nodes_with_metadata, get_all_described_nodes)
- Fix type conversions for RewardedSetResponse in network monitor
- Update all API client instantiation to use new unified HTTP client
* feat: complete migration to unified HTTP client and fix all compilation errors
- Added missing NymApiClientExt trait methods (get_all_expanded_nodes, change_base_urls)
- Fixed all compilation errors across the workspace
- Updated nym-node to use unified client instead of deprecated NymApiClient
- Fixed type conversions for RewardedSetResponse → EpochRewardedSet
- Added nym-http-api-client dependency where needed
- Updated all examples and documentation to use new client API
* fix: provide all API URLs for automatic failover in endpoint rotation
Previously, when rotating API endpoints, only a single URL was provided to the
HTTP client, defeating the purpose of having multiple URLs for resilience.
Changes:
- NymApiTopologyProvider now provides all URLs in rotated order when switching endpoints
- NymApisClient similarly provides all URLs starting from the working endpoint
- Added clarifying comments for broadcast/exhaustive query methods where single URLs are intentionally used
- This enables the HTTP client's built-in failover mechanism while maintaining endpoint rotation behavior
The fix ensures that if the primary endpoint fails, the client can automatically
failover to alternative endpoints without manual intervention, improving overall
network resilience.
* Update common/client-core/src/client/base_client/mod.rs
Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
* Remove error generics, address PR comments
* Explicit warning on missing fronting configuration
* Assorted CI fixes
* Registry proc-macro
* Rename macro
* Syn workspace version
* Where do we need to put inventory
* Ergonomics and call sites, incept the builder
* fix: Address critical issues in client configuration registry implementation
- Fixed HeaderMapInit parsing bug that would cause compilation errors
- Added comprehensive documentation with usage examples and DSL reference
- Improved error handling with better error messages for invalid headers
- Added test coverage for both macro and registry functionality
- Added debug inspection capabilities for registered configurations
- Fixed module name conflicts in tests by using separate modules
All tests now passing:
- 7 macro tests validating DSL parsing and code generation
- 4 registry tests verifying configuration collection and application
* Use default value for the ports until api is deployed
* Feature/improved http error (#6025)
* use display impl for urls
* feat: attempt to add more details to reqwest errors
* temporarily restored GenericRequestFailure variant
* another restoration
* cleanup
* Some debug tooling, and default timeout fix
* Fix user-agent override
* Fix various wasm things
---------
Co-authored-by: Jędrzej Stuczyński <jedrzej.stuczynski@gmail.com>
Co-authored-by: Bogdan-Ștefan Neacşu <bogdan@nymtech.net>
* squashing work on using cancellation in nym crates
making nym-task wasm compilable
removed sending of status messages
replaced TaskManager with ShutdownManager in the validator rewarder
additional helpers for ShutdownManager
simplified ShutdownToken by removing the name field
TaskClient => ShutdownToken within all client tasks
wip: remove TaskHandle
* track all long-living client tasks
* add task tracking for most top level tasks within nym-node
* improved default builder
* split up cancellation module
* module documentation and unit tests
* nym node fixes and naming consistency
* wasm fixes
* assert_eq => assert
* wasm fixes and made 'run_until_shutdown' take reference instead of ownership
* linux-specific fixes to IpPacketRouter
* post rebasing fixes for signing monitor
* add ShutdownManager constructor to build it from an external token
* applying PR review suggestions
* moved storage and deposits buffer to the common lib
* move more of the state into the shared lib
* extracted the rest of the features into the shared lib
* fixed test imports
* clippy
* define storage item for holding historical DKG state
* make all epoch storage operations go through proxy functions
* make each saving action also apply to the historical item
* removed usage of update_epoch function
* test correct save heights
* exposed query for the epoch state at specified height
* regenerated contract schema
* restored default cw-plus behaviour as in hindsight it makes more sense
* feat: add GetEpochDealers and GetEpochDealersAddresses queries to the DKG contract
* extended DkgQueryClient with new queries
* updated contract schema
* unit tests
* renamed nym-api config fields
* decouple rewarder startup from network monitor
* additional sections in nym-api config
* removed vesting queries in circulating supply calculator
* added memoized field for last submitted performance measurement
* wip: performance contract refresher
* cleaned up various contract caches
* modified cache refresher to allow passing update fn
* implement performance cache refreshing
* updated lefthook.yml to run cargo fmt
* impl NodePerformanceProvider trait
* dynamically using specific performance provider
* pre warm up performance contract cache and forbid the mode if its empty
* clippy
* introduce fallback setting for performance contract if value for given epoch is not available
* move some functions around
* initialised basic structure for the performance contract
* shared code for contract testing
* unified common testing methods between performance and nym pool contracts
* impl of ExecuteMsg for the contract
* impl of QueryMsg for the contract
* setting initial authorised NMs during instantiation
* additional tests and fixes
* ibid
* scaffolding for client traits
* completed client traits
* clippy
* naive add performance contract to testnet manager
* placeholder values for the performance contract address
* introduced admin messages to purge old measurements from the storage
* introduced check ensuring performance data is only added to bonded nodes
* wip
* wip: wrap node's sphinx key with a manager
* wip: choosing correct key for packet processing
* further propagation of key rotation information
* attaching key rotation information to reply surbs
* added basic key rotation information to mixnet contract
* wip: introducing cached queries for key rotation info from nym api
* unified nym-api contract cache refreshing
* finish packet decoding
* multi api client + retrieving rotation id
* rotating sphinx key files
* logic for migrating config file
* wip: putting new sphinx keys to self described endpoints
* processing loop of KeyRotationController
* fixed sphinx key loading
* rotating bloomfilters
* wired up KeyRotationController
* flushing bloomfilters to disk and loading
* most of nym-node changes
* post rebase fixes
* fixes due to backwards compatible hostkeys
* split http state.rs file
* dont use deprecated fields
* fixed backwards compatible deserialisation of host information
* split up node describe cache
* added a dedicated CacheRefresher listener to perform full refresh outside the set interval
* controlling announced sphinx keys within nym-api
* retrieving rotation id when pulling topology
* split nym-nodes http handlers
* v2 nym-api endpoints to retrieve nodes with additional metadata information
* bug fixes...
* additional bugfixes and guards against stuck epoch
* testnet manager: set first nym-api as the rewarder
* fixed host information deserialisation
* fixed panic during first key rotation
* post rebase fixes
* clippy
* more guards against stuck epochs
* added helper method to reset node's sphinx key
* instantiate mixnet contract with custom key rotation validity
* additional bugfixes and debugging nym-api deadlock
* passing shutdown to nym apis client
* remove dead test
* post rebasing fixes
* missing MixnetQueryClient variants
* remove usage of deprecated methods in sdk example
* fix: incorrect method signature
* post rebasing fixes
* attempt to retrieve key rotation id before doing any config migration work
* ignore tests relying on networking behaviour
* allow networking failures in certain tests
* squashed nym-pool commits
initialised nym-pool contract and updated all bls12_381 to make it possible
create scaffolding for tests
ability to control the contract admin
introducing contract grants
grant type validation
basic grant operations + stubs for other messages
added queries
use transaction stubs
added expiration information to grant queries
setting initial grant state based on the current environment
allowance logic for attempting to spend part of a grant
implemented all remaining transactions
made public api for coin locking perform validation
tests for locked tokens storage
nympool storage tests
added messages for changing granter set
tests and fixes for sufficient tokens when inserting grants
tests for initial state + more bugfixes
queries tests
additional tests for transactions and fixes
post rebase fixes
updated contract dependencies
removed redundant wasm constructor
dont ask me why this suddenly became an issue - no clue
removed redundant wasm constructor
dont ask me why this suddenly became an issue - no clue
* missing schema + added nym_pool to the main Makefile
* updated contracts to cosmwasm2.2 and fixed build issues
* removed old coconut contract code + additional dkg fixes
* replace deprecated to_binary and from_binary functions
* mixnet contract tests compiling
some are failing due to incorrect addresses
* made other contract tests compile
* fixed remaining tests
* allow usage of manually dispatching contract replies
* nym-api test fixes
* removed old toolchain from contracts CI
* linter fixes
* regenerated contract schema
* fixed easy_addr
* further license fixes
* post rebase fixes + update to 2.2.2
* change ci runner
* minor CI adjustments
* change wallet CI to use node 20
* more CI changes...
* run cosmwasm-check against release contracts
* test ci changes
* wip...
* wip
* changed minor/patch weights and introduced full release chain history for more accurate calculations
* clippy
* updated contract schema
* added nym-api endpoint for current rewarded set nodes
* added nym-api endpoint for internal config score data
* guard mixnet contract against decreasing semver
* fixed config score calculation if there are skipped versions
* fixed pagination for querying for validators
* wip: decoupling block signing from ticketbook issuance
* added ecash contract query for latest deposit
* parking the branch: wrappers for merkle tree for issued ticketbooks
* make nym-api store merkle trees of issued ticketbooks
* nym-api route for returning all deposits alongside merkle root
* return index alongside deposit id
* persisting merkle index alongside issued ticketbook details
* wip
* responses for issued deposit challenges
* nym-api cleanup
* verification of issued partial ticketbooks
* cleanup of rewarder code
* make the rest of codebase compile
* updated config file
* improved logging
* fixed division by zero if there were no ticketbooks issued in a day
* using correct budget when rewarding operators
* fixed routes for issued data
* fixed ecash test fixture
* fixed incorrect deserialisation of expiration_date param
* additional bugfixes for ticketbook issuance
* more fixes and updated tests
* fixed formatting after rebasing
* updated schema
* fixed edge case unit test
* added config-score related parameters to the mixnet contract
* weaved in described_cache into NodeStatusCacheRefresher
* adding config score annotation
* using new updated performance for updating rewarded set
* using new values for rewarding
* clippy
* updated contract schema
* wallet fixes
* fixed wasm build
* bugfix: introduce 'LegacyPendingMixNodeChanges' that does not contain 'cost_params_change'
* updated schema files due to removal of '#[serde(deny_unknown_fields)]'