mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-10 20:15:39 -04:00
[client] pqkem: make signalling bootstrap idempotent through awaitingRekey
The controller sends its KEM offer both on its own guard event and in reply to the responder's offer. SignalOffer was idempotent only while awaiting the answer; once the answer arrived (awaitingRekey) a repeat call started a fresh exchange with a different PSK, desyncing the two peers (one on the old PSK, one on the new) so WireGuard derived misaligned transport keys and dropped all data. Treat awaitingRekey as in-flight too and return the same offer.
This commit is contained in:
@@ -285,7 +285,13 @@ func (m *Manager) SignalOffer(remoteID RemoteID) ([]byte, error) {
|
||||
m.mu.Unlock()
|
||||
return nil, nil // peer does not run the KEM; do not offer (avoids a failure/reoffer loop)
|
||||
}
|
||||
if ex := m.exchanges[remoteID]; ex != nil && ex.viaSignal && ex.state == stateAwaitingAnswer {
|
||||
// Idempotent while a signalling bootstrap is in flight OR already derived a PSK but
|
||||
// not yet chained a rotation (awaitingRekey): return the SAME offer instead of
|
||||
// starting a new exchange. This matters when the controller both offers on its own
|
||||
// guard AND re-offers in response to the responder's offer — without this, the
|
||||
// second call would start a fresh exchange (a different PSK) and desync the peers.
|
||||
if ex := m.exchanges[remoteID]; ex != nil && ex.viaSignal &&
|
||||
(ex.state == stateAwaitingAnswer || ex.state == stateAwaitingRekey) {
|
||||
last := ex.lastSent
|
||||
m.mu.Unlock()
|
||||
return last, nil
|
||||
|
||||
Reference in New Issue
Block a user