mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-09 15:09:08 +02:00
[client] pqkem: signal an offer-build failure with a marker, not an empty offer
The error-vs-"no KEM" ambiguity fixed on the answer path also existed on the offer path: when SignalOffer failed to build the bootstrap offer (e.g. an entropy failure in startExchangeLocked), OfferPayload logged it and still sent an empty MlkemPayload. The responder read that as "peer does not run the KEM", marked the initiator non-capable, and answered empty — which made the initiator mark the responder non-capable too, disabling the KEM for the session over one transient error. Return the MsgError marker from SignalOffer on failure, and have the responder's SignalOnOffer answer a received marker with its own marker rather than an empty answer. A genuinely non-KEM peer still sends no payload at all, so the empty capability signal is unchanged. Found in cubic review on #7098 (client/internal/pqkem_adapter.go:130).
This commit is contained in:
@@ -339,7 +339,14 @@ func (m *Manager) SignalOffer(remoteID RemoteID) ([]byte, error) {
|
||||
// for the same peer can't also start an exchange. bootstrap offer acks nothing.
|
||||
raw, err := m.startExchangeLocked(remoteID, true, ExchangeID{})
|
||||
m.mu.Unlock()
|
||||
return raw, err
|
||||
if err != nil {
|
||||
// Return an error marker, not an empty offer: an empty MlkemPayload is the "peer
|
||||
// does not run the KEM" capability signal, so the responder would wrongly mark us
|
||||
// non-capable (and answer empty, making us mark it non-capable in turn). The marker
|
||||
// is benign on the far side; the host retries the offer.
|
||||
return (&ErrorMsg{}).Encode(), err
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// ShouldSendBootstrapOffer reports whether we should emit a fresh KEM offer to kick a
|
||||
@@ -367,6 +374,12 @@ func (m *Manager) SignalOnOffer(remoteID RemoteID, offer []byte) ([]byte, error)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode signal offer from %s: %w", remoteID, err)
|
||||
}
|
||||
if typ == MsgError {
|
||||
// The initiator failed to build its offer and sent a marker. Answer with our own
|
||||
// marker, not an empty answer, so it is not read as "peer does not run the KEM".
|
||||
m.trace("pqkem: peer reported an error building its offer", "peer", remoteID)
|
||||
return (&ErrorMsg{}).Encode(), nil
|
||||
}
|
||||
if typ != MsgOffer {
|
||||
return nil, fmt.Errorf("expected offer from %s, got type %d", remoteID, typ)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user