mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-28 18:41:30 +02:00
Introduces a forced imparity on MLKEM bootstrap.
To ensure two peers agree on a key, we need asymmetry. one peer is
the controller ("initiator") the other is the "responder".
Otherwise imagine two offers in parallel driving two answers at the same time
A B
| <----B-OFFER----- |
| -----A-OFFER----> |
| |
| |
---------------------------------
|****** ICE + WG Handshake ****** |
---------------------------------
| |
| <----B-ANSWER---- |
| -----A-ANSWER---> |
PSK is derived on receive of offer, so A and B derive different PSKs.
When WG handshake takes place it picks misaligned PSKs.
So we impair the two nodes and only the offer of one of the two (the controller/initiator)
is allowed to progress and drive the answer (and carry the KEM material).
If a responder initiates an offer, we redo the offer towards it. This is oK
since the ICEworker don't treat offer/answers differently.
This commit is contained in:
@@ -137,6 +137,24 @@ func (h *Handshaker) Listen(ctx context.Context) {
|
||||
|
||||
h.pqRegisterEndpoint(remoteOfferAnswer.MlkemPort)
|
||||
|
||||
// Post-quantum: the KEM material rides only the controller's offer, so the two
|
||||
// peers derive a single shared PSK (a bidirectional KEM would yield two
|
||||
// different PSKs and WireGuard would pick misaligned ones). If we are the
|
||||
// controller and receive the responder's (KEM-less) offer, we must NOT let
|
||||
// that KEM-less transaction drive the connection: it would bring WireGuard up
|
||||
// on a pre-PQ key before the KEM completes (the race). Instead we reply with
|
||||
// OUR offer, which carries the KEM, so the only transaction that establishes
|
||||
// the tunnel is the one that also derives the PSK. This also guarantees a
|
||||
// responder-initiated wake still triggers a KEM offer (no stuck responder).
|
||||
// The re-offer reuses our stable ICE session id, so the peer dedups repeats.
|
||||
if h.config.PQ != nil && isController(h.config) {
|
||||
h.log.Debugf("pqkem: controller received a responder offer, replying with our KEM offer instead of an answer")
|
||||
if err := h.sendOffer(); err != nil {
|
||||
h.log.Errorf("failed to send KEM offer in response to peer offer: %s", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Derive+store the KEM PSK (inside sendAnswer's AnswerPayload) BEFORE bringing
|
||||
// up the connection: the relay/ICE workers configure the WG endpoint, which
|
||||
// pulls the PSK for the first handshake. Notifying them first would race the
|
||||
|
||||
Reference in New Issue
Block a user