mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-14 09:29:07 +02:00
pqkem: carry KEM offer/answer over the signalling exchange
This commit is contained in:
@@ -1911,6 +1911,9 @@ func (e *Engine) createPeerConn(pubKey string, allowedIPs []netip.Prefix, agentV
|
|||||||
},
|
},
|
||||||
ICEConfig: e.createICEConfig(),
|
ICEConfig: e.createICEConfig(),
|
||||||
}
|
}
|
||||||
|
if e.pqkemManager != nil {
|
||||||
|
config.PQ = pqHandshaker{mgr: e.pqkemManager}
|
||||||
|
}
|
||||||
|
|
||||||
serviceDependencies := peer.ServiceDependencies{
|
serviceDependencies := peer.ServiceDependencies{
|
||||||
StatusRecorder: e.statusRecorder,
|
StatusRecorder: e.statusRecorder,
|
||||||
|
|||||||
@@ -74,6 +74,21 @@ type RosenpassConfig struct {
|
|||||||
PermissiveMode bool
|
PermissiveMode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PQHandshaker attaches post-quantum ML-KEM material to signalling offers/answers and
|
||||||
|
// feeds received material back. It is implemented by the engine over the pqkem
|
||||||
|
// manager and is nil when the PQ exchange is disabled. remoteKey is the peer's
|
||||||
|
// WireGuard public key.
|
||||||
|
type PQHandshaker interface {
|
||||||
|
// OfferPayload returns the KEM offer to embed in an outgoing offer (nil if this
|
||||||
|
// peer is not the KEM initiator) and the local PQ data-path port to announce.
|
||||||
|
OfferPayload(remoteKey string) (payload []byte, port int)
|
||||||
|
// AnswerPayload processes a received KEM offer (nil if absent) and returns the KEM
|
||||||
|
// answer to embed in the outgoing answer (nil if none) and the local PQ port.
|
||||||
|
AnswerPayload(remoteKey string, recvOffer []byte) (payload []byte, port int)
|
||||||
|
// OnAnswer feeds a received KEM answer (nil if absent).
|
||||||
|
OnAnswer(remoteKey string, recvAnswer []byte)
|
||||||
|
}
|
||||||
|
|
||||||
// ConnConfig is a peer Connection configuration
|
// ConnConfig is a peer Connection configuration
|
||||||
type ConnConfig struct {
|
type ConnConfig struct {
|
||||||
// Key is a public key of a remote peer
|
// Key is a public key of a remote peer
|
||||||
@@ -91,6 +106,9 @@ type ConnConfig struct {
|
|||||||
|
|
||||||
RosenpassConfig RosenpassConfig
|
RosenpassConfig RosenpassConfig
|
||||||
|
|
||||||
|
// PQ carries post-quantum ML-KEM material on offers/answers; nil when disabled.
|
||||||
|
PQ PQHandshaker
|
||||||
|
|
||||||
// ICEConfig ICE protocol configuration
|
// ICEConfig ICE protocol configuration
|
||||||
ICEConfig icemaker.Config
|
ICEConfig icemaker.Config
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ func (h *Handshaker) Listen(ctx context.Context) {
|
|||||||
h.iceListener(&remoteOfferAnswer)
|
h.iceListener(&remoteOfferAnswer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := h.sendAnswer(); err != nil {
|
if err := h.sendAnswer(&remoteOfferAnswer); err != nil {
|
||||||
h.log.Errorf("failed to send remote offer confirmation: %s", err)
|
h.log.Errorf("failed to send remote offer confirmation: %s", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -159,6 +159,10 @@ func (h *Handshaker) Listen(ctx context.Context) {
|
|||||||
if h.iceListener != nil && h.RemoteICESupported() {
|
if h.iceListener != nil && h.RemoteICESupported() {
|
||||||
h.iceListener(&remoteOfferAnswer)
|
h.iceListener(&remoteOfferAnswer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.config.PQ != nil {
|
||||||
|
h.config.PQ.OnAnswer(h.config.Key, remoteOfferAnswer.MlkemPayload)
|
||||||
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
h.log.Infof("stop listening for remote offers and answers")
|
h.log.Infof("stop listening for remote offers and answers")
|
||||||
return
|
return
|
||||||
@@ -205,13 +209,23 @@ func (h *Handshaker) sendOffer() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
offer := h.buildOfferAnswer()
|
offer := h.buildOfferAnswer()
|
||||||
|
if h.config.PQ != nil {
|
||||||
|
offer.MlkemPayload, offer.MlkemPort = h.config.PQ.OfferPayload(h.config.Key)
|
||||||
|
}
|
||||||
h.log.Debugf("sending offer with serial: %s", offer.SessionIDString())
|
h.log.Debugf("sending offer with serial: %s", offer.SessionIDString())
|
||||||
|
|
||||||
return h.signaler.SignalOffer(offer, h.config.Key)
|
return h.signaler.SignalOffer(offer, h.config.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handshaker) sendAnswer() error {
|
func (h *Handshaker) sendAnswer(remoteOffer *OfferAnswer) error {
|
||||||
answer := h.buildOfferAnswer()
|
answer := h.buildOfferAnswer()
|
||||||
|
if h.config.PQ != nil {
|
||||||
|
var recvOffer []byte
|
||||||
|
if remoteOffer != nil {
|
||||||
|
recvOffer = remoteOffer.MlkemPayload
|
||||||
|
}
|
||||||
|
answer.MlkemPayload, answer.MlkemPort = h.config.PQ.AnswerPayload(h.config.Key, recvOffer)
|
||||||
|
}
|
||||||
h.log.Debugf("sending answer with serial: %s", answer.SessionIDString())
|
h.log.Debugf("sending answer with serial: %s", answer.SessionIDString())
|
||||||
|
|
||||||
return h.signaler.SignalAnswer(answer, h.config.Key)
|
return h.signaler.SignalAnswer(answer, h.config.Key)
|
||||||
|
|||||||
@@ -31,3 +31,37 @@ func (h pqCallbackHandler) OnRekeyFailed(remoteID pqkem.RemoteID) error {
|
|||||||
log.Warnf("pqkem: post-quantum rekey failed for peer %s", remoteID)
|
log.Warnf("pqkem: post-quantum rekey failed for peer %s", remoteID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pqHandshaker adapts the pqkem manager to peer.PQHandshaker (string peer keys),
|
||||||
|
// wiring the host's signalling offers/answers to the KEM exchange.
|
||||||
|
type pqHandshaker struct {
|
||||||
|
mgr *pqkem.Manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pqHandshaker) OfferPayload(remoteKey string) ([]byte, int) {
|
||||||
|
payload, err := p.mgr.SignalOffer(pqkem.RemoteID(remoteKey))
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("pqkem: build offer for %s: %v", remoteKey, err)
|
||||||
|
}
|
||||||
|
return payload, p.mgr.LocalPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pqHandshaker) AnswerPayload(remoteKey string, recvOffer []byte) ([]byte, int) {
|
||||||
|
if len(recvOffer) == 0 {
|
||||||
|
return nil, p.mgr.LocalPort()
|
||||||
|
}
|
||||||
|
payload, err := p.mgr.SignalOnOffer(pqkem.RemoteID(remoteKey), recvOffer)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("pqkem: build answer for %s: %v", remoteKey, err)
|
||||||
|
}
|
||||||
|
return payload, p.mgr.LocalPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p pqHandshaker) OnAnswer(remoteKey string, recvAnswer []byte) {
|
||||||
|
if len(recvAnswer) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := p.mgr.SignalOnAnswer(pqkem.RemoteID(remoteKey), recvAnswer); err != nil {
|
||||||
|
log.Warnf("pqkem: process answer from %s: %v", remoteKey, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user