Bit of renaming

peer -> peerAddrs
have types for remoteID and localID
t.Close log error
Manager SetTransport -> Start
This commit is contained in:
riccardom
2026-07-27 17:09:36 +02:00
parent f91e1e34ce
commit 1bccd71954
7 changed files with 71 additions and 62 deletions
+2 -2
View File
@@ -663,8 +663,8 @@ func (e *Engine) Start(netbirdConfig *mgmProto.NetbirdConfig, mgmtURL *url.URL)
if pqErr != nil { if pqErr != nil {
log.Errorf("ML-KEM PQ transport bind failed, PQ exchange disabled: %v", pqErr) log.Errorf("ML-KEM PQ transport bind failed, PQ exchange disabled: %v", pqErr)
} else { } else {
e.pqkemManager = pqkem.NewManager(publicKey.String(), pqCallbackHandler{wg: e.wgInterface}, nil) e.pqkemManager = pqkem.NewManager(pqkem.LocalID(publicKey.String()), pqCallbackHandler{wg: e.wgInterface}, nil)
e.pqkemManager.SetTransport(tr) e.pqkemManager.Start(tr)
log.Infof("ML-KEM post-quantum exchange enabled (udp port %d on overlay %s)", e.pqkemManager.LocalPort(), e.config.WgAddr.IP) log.Infof("ML-KEM post-quantum exchange enabled (udp port %d on overlay %s)", e.pqkemManager.LocalPort(), e.config.WgAddr.IP)
} }
} }
+2 -2
View File
@@ -9,10 +9,10 @@ type CallbackHandler interface {
// and must be programmed into the consumer's secure channel. It is invoked at // and must be programmed into the consumer's secure channel. It is invoked at
// the commit point of each side: the initiator on receiving the answer, the // the commit point of each side: the initiator on receiving the answer, the
// responder on receiving the confirm. // responder on receiving the confirm.
OnNewPSKReady(remoteID string, psk PSK) error OnNewPSKReady(remoteID RemoteID, psk PSK) error
// OnRekeyFailed fires when an exchange fails to converge within the allotted // OnRekeyFailed fires when an exchange fails to converge within the allotted
// time. The host should tear the peer connection down so it re-establishes, and // time. The host should tear the peer connection down so it re-establishes, and
// log a WARN. The library reports the event; it does not dictate the reaction. // log a WARN. The library reports the event; it does not dictate the reaction.
OnRekeyFailed(remoteID string) error OnRekeyFailed(remoteID RemoteID) error
} }
+7 -7
View File
@@ -9,7 +9,7 @@ import (
// bootstrap) and returns the framed offer for the caller to send — pushed over the // bootstrap) and returns the framed offer for the caller to send — pushed over the
// data path for a chained rekey, or handed to the host for signalling when viaSignal // data path for a chained rekey, or handed to the host for signalling when viaSignal
// is set. Any previous in-flight exchange for the peer is cancelled. // is set. Any previous in-flight exchange for the peer is cancelled.
func (m *Manager) startExchange(remoteID string, viaSignal bool, ackID ExchangeID) ([]byte, error) { func (m *Manager) startExchange(remoteID RemoteID, viaSignal bool, ackID ExchangeID) ([]byte, error) {
init, err := NewInitiator() init, err := NewInitiator()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -48,7 +48,7 @@ func (m *Manager) startExchange(remoteID string, viaSignal bool, ackID ExchangeI
// (that offer riding the data path under the freshly adopted key proves it worked), // (that offer riding the data path under the freshly adopted key proves it worked),
// then derives the PSK for the new offer, commits it optimistically, and returns the // then derives the PSK for the new offer, commits it optimistically, and returns the
// framed answer. A duplicate offer returns the cached answer without re-deriving. // framed answer. A duplicate offer returns the cached answer without re-deriving.
func (m *Manager) processOffer(remoteID string, o *OfferMsg) ([]byte, error) { func (m *Manager) processOffer(remoteID RemoteID, o *OfferMsg) ([]byte, error) {
if o.AckID != (ExchangeID{}) { if o.AckID != (ExchangeID{}) {
m.ackConverged(remoteID, o.AckID) m.ackConverged(remoteID, o.AckID)
} }
@@ -97,7 +97,7 @@ func (m *Manager) processOffer(remoteID string, o *OfferMsg) ([]byte, error) {
// stateAwaitingRekey; the next offer (chained from OnDataPathRekeyed) will acknowledge // stateAwaitingRekey; the next offer (chained from OnDataPathRekeyed) will acknowledge
// this exchange. Only valid in stateAwaitingAnswer; advancing the state under the // this exchange. Only valid in stateAwaitingAnswer; advancing the state under the
// lock makes a concurrent/duplicate answer bail. // lock makes a concurrent/duplicate answer bail.
func (m *Manager) processAnswer(remoteID string, a *AnswerMsg) error { func (m *Manager) processAnswer(remoteID RemoteID, a *AnswerMsg) error {
m.mu.Lock() m.mu.Lock()
ex := m.exchanges[remoteID] ex := m.exchanges[remoteID]
if ex == nil || ex.id != a.ExchangeID || ex.state != stateAwaitingAnswer { if ex == nil || ex.id != a.ExchangeID || ex.state != stateAwaitingAnswer {
@@ -126,7 +126,7 @@ func (m *Manager) processAnswer(remoteID string, a *AnswerMsg) error {
// ackConverged (responder) records convergence of the exchange named by ackID: a // ackConverged (responder) records convergence of the exchange named by ackID: a
// later offer acknowledging it proves both sides operate on that exchange's key. Only // later offer acknowledging it proves both sides operate on that exchange's key. Only
// acts on a matching stateAwaitingAck exchange; anything else is ignored. // acts on a matching stateAwaitingAck exchange; anything else is ignored.
func (m *Manager) ackConverged(remoteID string, ackID ExchangeID) { func (m *Manager) ackConverged(remoteID RemoteID, ackID ExchangeID) {
m.mu.Lock() m.mu.Lock()
ex := m.exchanges[remoteID] ex := m.exchanges[remoteID]
if ex == nil || ex.id != ackID || ex.state != stateAwaitingAck { if ex == nil || ex.id != ackID || ex.state != stateAwaitingAck {
@@ -146,7 +146,7 @@ func (m *Manager) ackConverged(remoteID string, ackID ExchangeID) {
// here). Exhausting the deadline before the answer arrives is a failure. Once the // here). Exhausting the deadline before the answer arrives is a failure. Once the
// answer is in (state past awaitingAnswer) the loop exits: the next rotation is driven // answer is in (state past awaitingAnswer) the loop exits: the next rotation is driven
// by OnDataPathRekeyed, and the idle wait for it has no deadline. // by OnDataPathRekeyed, and the idle wait for it has no deadline.
func (m *Manager) initiatorLoop(ctx context.Context, remoteID string, id ExchangeID) { func (m *Manager) initiatorLoop(ctx context.Context, remoteID RemoteID, id ExchangeID) {
defer m.wait.Done() defer m.wait.Done()
t := time.NewTicker(m.retryInterval) t := time.NewTicker(m.retryInterval)
defer t.Stop() defer t.Stop()
@@ -199,7 +199,7 @@ func (m *Manager) initiatorLoop(ctx context.Context, remoteID string, id Exchang
// an initial exchange (peer never established) fails immediately; a rekey tolerates // an initial exchange (peer never established) fails immediately; a rekey tolerates
// up to maxRekeyFailures consecutive misses (we stay on the still-valid previous // up to maxRekeyFailures consecutive misses (we stay on the still-valid previous
// PSK) before failing. Assumes m.mu is held. // PSK) before failing. Assumes m.mu is held.
func (m *Manager) registerFailureLocked(remoteID string) bool { func (m *Manager) registerFailureLocked(remoteID RemoteID) bool {
if !m.established[remoteID] { if !m.established[remoteID] {
return true return true
} }
@@ -211,7 +211,7 @@ func (m *Manager) registerFailureLocked(remoteID string) bool {
return false return false
} }
func (m *Manager) raiseFailure(remoteID string, fail bool) { func (m *Manager) raiseFailure(remoteID RemoteID, fail bool) {
if !fail { if !fail {
m.logger.Warn("pqkem rekey attempt timed out, will retry next cycle", "peer", remoteID) m.logger.Warn("pqkem rekey attempt timed out, will retry next cycle", "peer", remoteID)
return return
+1 -1
View File
@@ -25,7 +25,7 @@ func failedCount(f *fakeWG) int {
func TestManager_InitialTimeoutFailsImmediately(t *testing.T) { func TestManager_InitialTimeoutFailsImmediately(t *testing.T) {
wg := newFakeWG() wg := newFakeWG()
d := NewManager("bbbb", wg, nil) // bbbb > aaaa -> initiator d := NewManager("bbbb", wg, nil) // bbbb > aaaa -> initiator
d.SetTransport(dropTransport{}) d.Start(dropTransport{})
d.retryInterval = 5 * time.Millisecond d.retryInterval = 5 * time.Millisecond
d.maxRetries = 3 d.maxRetries = 3
defer d.Stop() defer d.Stop()
+47 -38
View File
@@ -22,11 +22,18 @@ const (
DefaultMaxRekeyFailures = 3 DefaultMaxRekeyFailures = 3
) )
// LocalID and RemoteID are peer identity keys (e.g. WireGuard public keys). They are
// distinct types so the local and a remote identity cannot be mixed up.
type (
LocalID string
RemoteID string
)
// Transport is the data-path socket the Manager drives (the analogue of // Transport is the data-path socket the Manager drives (the analogue of
// go-rosenpass's Conn). It is a dumb mover of bytes to/from endpoints: the Manager // go-rosenpass's Conn). It is a dumb mover of bytes to/from endpoints: the Manager
// owns the remoteID<->endpoint routing and hands the transport a resolved endpoint // owns the remoteID<->endpoint routing and hands the transport a resolved endpoint
// to Send, and reverse-resolves the source of each inbound datagram. Its lifecycle // to Send, and reverse-resolves the source of each inbound datagram. Its lifecycle
// belongs to the Manager (Run at SetTransport, Close at Stop). // belongs to the Manager (Run at Start, Close at Stop).
type Transport interface { type Transport interface {
// Send delivers msg to the given data-path endpoint. // Send delivers msg to the given data-path endpoint.
Send(endpoint netip.AddrPort, msg []byte) error Send(endpoint netip.AddrPort, msg []byte) error
@@ -75,7 +82,7 @@ type exchangeCtl struct {
// (SignalOffer) and each rotation is clocked by OnDataPathRekeyed. The cryptography is // (SignalOffer) and each rotation is clocked by OnDataPathRekeyed. The cryptography is
// the pure kem.go primitives; all state lives here under one lock. // the pure kem.go primitives; all state lives here under one lock.
type Manager struct { type Manager struct {
localID string localID LocalID
cbHandler CallbackHandler cbHandler CallbackHandler
logger *slog.Logger logger *slog.Logger
@@ -88,18 +95,18 @@ type Manager struct {
mu sync.Mutex mu sync.Mutex
transport Transport transport Transport
exchanges map[string]*exchangeCtl // in-flight exchange per peer exchanges map[RemoteID]*exchangeCtl // in-flight exchange per peer
established map[string]bool // peer has completed at least one exchange established map[RemoteID]bool // peer has completed at least one exchange
failures map[string]int // consecutive rekey failures per peer failures map[RemoteID]int // consecutive rekey failures per peer
peers map[string]netip.AddrPort // remoteID -> data-path endpoint (send routing) peerAddrs map[RemoteID]netip.AddrPort // remoteID -> data-path endpoint (send routing)
peersByAddr map[netip.AddrPort]string // reverse: source endpoint -> remoteID (inbound) peersByAddr map[netip.AddrPort]RemoteID // reverse: source endpoint -> remoteID (inbound)
wait sync.WaitGroup wait sync.WaitGroup
} }
// NewManager builds a manager for the local peer identified by its peer identity key // NewManager builds a manager for the local peer identified by its peer identity key
// (used for the deterministic initiator role and the identity binding). A nil logger // (used for the deterministic initiator role and the identity binding). A nil logger
// falls back to slog.Default(). Set the data-path transport with SetTransport. // falls back to slog.Default(). Install the data-path transport with Start.
func NewManager(localID string, h CallbackHandler, logger *slog.Logger) *Manager { func NewManager(localID LocalID, h CallbackHandler, logger *slog.Logger) *Manager {
if logger == nil { if logger == nil {
logger = slog.Default() logger = slog.Default()
} }
@@ -113,17 +120,17 @@ func NewManager(localID string, h CallbackHandler, logger *slog.Logger) *Manager
maxRekeyFailures: DefaultMaxRekeyFailures, maxRekeyFailures: DefaultMaxRekeyFailures,
rootCtx: ctx, rootCtx: ctx,
rootCancel: cancel, rootCancel: cancel,
exchanges: make(map[string]*exchangeCtl), exchanges: make(map[RemoteID]*exchangeCtl),
established: make(map[string]bool), established: make(map[RemoteID]bool),
failures: make(map[string]int), failures: make(map[RemoteID]int),
peers: make(map[string]netip.AddrPort), peerAddrs: make(map[RemoteID]netip.AddrPort),
peersByAddr: make(map[netip.AddrPort]string), peersByAddr: make(map[netip.AddrPort]RemoteID),
} }
} }
// SetTransport installs the data-path transport and starts its inbound delivery. The // Start installs the data-path transport and begins its inbound delivery. The Manager
// Manager owns it from here: Stop closes it. // owns it from here; Stop closes it. Start/Stop are the transport lifecycle pair.
func (m *Manager) SetTransport(t Transport) { func (m *Manager) Start(t Transport) {
m.mu.Lock() m.mu.Lock()
m.transport = t m.transport = t
m.mu.Unlock() m.mu.Unlock()
@@ -147,27 +154,27 @@ func (m *Manager) LocalPort() int {
// IsInitiator reports whether the local peer drives the exchange for this remote // IsInitiator reports whether the local peer drives the exchange for this remote
// peer. Roles are deterministic (lexicographic identity-key compare) so exactly one // peer. Roles are deterministic (lexicographic identity-key compare) so exactly one
// side initiates, mirroring how Rosenpass picks its handshake initiator. // side initiates, mirroring how Rosenpass picks its handshake initiator.
func (m *Manager) IsInitiator(remoteID string) bool { func (m *Manager) IsInitiator(remoteID RemoteID) bool {
return m.localID > remoteID return string(m.localID) > string(remoteID)
} }
// AddPeer registers where a peer's data-path messages are sent and received: its // AddPeer registers where a peer's data-path messages are sent and received: its
// overlay endpoint (IP:port). Re-adding updates the endpoint. // overlay endpoint (IP:port). Re-adding updates the endpoint.
func (m *Manager) AddPeer(remoteID string, endpoint netip.AddrPort) { func (m *Manager) AddPeer(remoteID RemoteID, endpoint netip.AddrPort) {
if !endpoint.IsValid() { if !endpoint.IsValid() {
return return
} }
m.mu.Lock() m.mu.Lock()
if old, ok := m.peers[remoteID]; ok { if old, ok := m.peerAddrs[remoteID]; ok {
delete(m.peersByAddr, old) delete(m.peersByAddr, old)
} }
m.peers[remoteID] = endpoint m.peerAddrs[remoteID] = endpoint
m.peersByAddr[endpoint] = remoteID m.peersByAddr[endpoint] = remoteID
m.mu.Unlock() m.mu.Unlock()
} }
// RemovePeer stops any in-flight exchange for a peer and drops its state and routing. // RemovePeer stops any in-flight exchange for a peer and drops its state and routing.
func (m *Manager) RemovePeer(remoteID string) { func (m *Manager) RemovePeer(remoteID RemoteID) {
m.mu.Lock() m.mu.Lock()
if ex, ok := m.exchanges[remoteID]; ok { if ex, ok := m.exchanges[remoteID]; ok {
if ex.cancel != nil { if ex.cancel != nil {
@@ -177,9 +184,9 @@ func (m *Manager) RemovePeer(remoteID string) {
} }
delete(m.established, remoteID) delete(m.established, remoteID)
delete(m.failures, remoteID) delete(m.failures, remoteID)
if ep, ok := m.peers[remoteID]; ok { if ep, ok := m.peerAddrs[remoteID]; ok {
delete(m.peersByAddr, ep) delete(m.peersByAddr, ep)
delete(m.peers, remoteID) delete(m.peerAddrs, remoteID)
} }
m.mu.Unlock() m.mu.Unlock()
} }
@@ -192,10 +199,12 @@ func (m *Manager) Stop() {
m.mu.Lock() m.mu.Lock()
t := m.transport t := m.transport
m.transport = nil m.transport = nil
m.exchanges = make(map[string]*exchangeCtl) m.exchanges = make(map[RemoteID]*exchangeCtl)
m.mu.Unlock() m.mu.Unlock()
if t != nil { if t != nil {
_ = t.Close() if err := t.Close(); err != nil {
m.logger.Warn("pqkem: closing data-path transport", "err", err)
}
} }
} }
@@ -205,7 +214,7 @@ func (m *Manager) Stop() {
// remoteID (bootstrap). It returns (nil, nil) when the local peer is not the // remoteID (bootstrap). It returns (nil, nil) when the local peer is not the
// initiator. It is idempotent for an in-flight bootstrap: a repeat call returns the // initiator. It is idempotent for an in-flight bootstrap: a repeat call returns the
// same offer rather than starting a new exchange. // same offer rather than starting a new exchange.
func (m *Manager) SignalOffer(remoteID string) ([]byte, error) { func (m *Manager) SignalOffer(remoteID RemoteID) ([]byte, error) {
if !m.IsInitiator(remoteID) { if !m.IsInitiator(remoteID) {
return nil, nil return nil, nil
} }
@@ -222,7 +231,7 @@ func (m *Manager) SignalOffer(remoteID string) ([]byte, error) {
// SignalOnOffer processes a KEM offer the host extracted from an incoming offer and // SignalOnOffer processes a KEM offer the host extracted from an incoming offer and
// returns the KEM answer for the host to embed in its outgoing answer. // returns the KEM answer for the host to embed in its outgoing answer.
func (m *Manager) SignalOnOffer(remoteID string, offer []byte) ([]byte, error) { func (m *Manager) SignalOnOffer(remoteID RemoteID, offer []byte) ([]byte, error) {
typ, msg, err := Decode(offer) typ, msg, err := Decode(offer)
if err != nil { if err != nil {
return nil, fmt.Errorf("decode signal offer from %s: %w", remoteID, err) return nil, fmt.Errorf("decode signal offer from %s: %w", remoteID, err)
@@ -235,7 +244,7 @@ func (m *Manager) SignalOnOffer(remoteID string, offer []byte) ([]byte, error) {
// SignalOnAnswer processes a KEM answer the host extracted from an incoming answer. // SignalOnAnswer processes a KEM answer the host extracted from an incoming answer.
// There is no reply: the next offer (over the data path) acknowledges this exchange. // There is no reply: the next offer (over the data path) acknowledges this exchange.
func (m *Manager) SignalOnAnswer(remoteID string, answer []byte) error { func (m *Manager) SignalOnAnswer(remoteID RemoteID, answer []byte) error {
typ, msg, err := Decode(answer) typ, msg, err := Decode(answer)
if err != nil { if err != nil {
return fmt.Errorf("decode signal answer from %s: %w", remoteID, err) return fmt.Errorf("decode signal answer from %s: %w", remoteID, err)
@@ -252,9 +261,9 @@ func (m *Manager) SignalOnAnswer(remoteID string, answer []byte) error {
// source endpoint to a peer and dispatches. Unknown sources are dropped. // source endpoint to a peer and dispatches. Unknown sources are dropped.
func (m *Manager) onDataPathInbound(src netip.AddrPort, msg []byte) { func (m *Manager) onDataPathInbound(src netip.AddrPort, msg []byte) {
m.mu.Lock() m.mu.Lock()
remoteID := m.peersByAddr[src] remoteID, ok := m.peersByAddr[src]
m.mu.Unlock() m.mu.Unlock()
if remoteID == "" { if !ok {
return return
} }
if err := m.OnDataPathMessage(remoteID, msg); err != nil { if err := m.OnDataPathMessage(remoteID, msg); err != nil {
@@ -264,7 +273,7 @@ func (m *Manager) onDataPathInbound(src netip.AddrPort, msg []byte) {
// OnDataPathMessage handles a KEM message received over the data path from remoteID // OnDataPathMessage handles a KEM message received over the data path from remoteID
// and pushes any reply back over the data path. // and pushes any reply back over the data path.
func (m *Manager) OnDataPathMessage(remoteID string, raw []byte) error { func (m *Manager) OnDataPathMessage(remoteID RemoteID, raw []byte) error {
typ, msg, err := Decode(raw) typ, msg, err := Decode(raw)
if err != nil { if err != nil {
return fmt.Errorf("decode data-path msg from %s: %w", remoteID, err) return fmt.Errorf("decode data-path msg from %s: %w", remoteID, err)
@@ -291,7 +300,7 @@ func (m *Manager) OnDataPathMessage(remoteID string, raw []byte) error {
// initiator that just derived a PSK, it chains the next exchange: a fresh offer over // initiator that just derived a PSK, it chains the next exchange: a fresh offer over
// the data path that acknowledges the just-completed one (its arrival under the new // the data path that acknowledges the just-completed one (its arrival under the new
// key proves to the responder that the key works). // key proves to the responder that the key works).
func (m *Manager) OnDataPathRekeyed(remoteID string) { func (m *Manager) OnDataPathRekeyed(remoteID RemoteID) {
m.mu.Lock() m.mu.Lock()
ex := m.exchanges[remoteID] ex := m.exchanges[remoteID]
chain := ex != nil && ex.state == stateAwaitingRekey chain := ex != nil && ex.state == stateAwaitingRekey
@@ -317,15 +326,15 @@ func (m *Manager) OnDataPathRekeyed(remoteID string) {
// OnDataPathDown notifies that the peer's data path went down. Rotations resume once // OnDataPathDown notifies that the peer's data path went down. Rotations resume once
// the host re-bootstraps over signalling on reconnect; in-flight data-path sends will // the host re-bootstraps over signalling on reconnect; in-flight data-path sends will
// simply fail until then. Reserved as an explicit hook. // simply fail until then. Reserved as an explicit hook.
func (m *Manager) OnDataPathDown(remoteID string) {} func (m *Manager) OnDataPathDown(remoteID RemoteID) {}
// ---- internals ---- // ---- internals ----
// pushDataPath resolves the peer's endpoint and sends over the data-path transport, // pushDataPath resolves the peer's endpoint and sends over the data-path transport,
// erroring if the peer is unknown or no transport is set. // erroring if the peer is unknown or no transport is set.
func (m *Manager) pushDataPath(remoteID string, msg []byte) error { func (m *Manager) pushDataPath(remoteID RemoteID, msg []byte) error {
m.mu.Lock() m.mu.Lock()
ep, ok := m.peers[remoteID] ep, ok := m.peerAddrs[remoteID]
t := m.transport t := m.transport
m.mu.Unlock() m.mu.Unlock()
if !ok { if !ok {
@@ -337,7 +346,7 @@ func (m *Manager) pushDataPath(remoteID string, msg []byte) error {
return t.Send(ep, msg) return t.Send(ep, msg)
} }
func (m *Manager) binding(remoteID string) Binding { func (m *Manager) binding(remoteID RemoteID) Binding {
return Binding{LocalID: []byte(m.localID), RemoteID: []byte(remoteID)} return Binding{LocalID: []byte(m.localID), RemoteID: []byte(remoteID)}
} }
+9 -9
View File
@@ -59,27 +59,27 @@ func (l *loopback) Close() error { return nil }
type fakeWG struct { type fakeWG struct {
mu sync.Mutex mu sync.Mutex
psks map[string]PSK psks map[RemoteID]PSK
failed []string failed []RemoteID
} }
func newFakeWG() *fakeWG { return &fakeWG{psks: map[string]PSK{}} } func newFakeWG() *fakeWG { return &fakeWG{psks: map[RemoteID]PSK{}} }
func (f *fakeWG) OnNewPSKReady(remoteID string, psk PSK) error { func (f *fakeWG) OnNewPSKReady(remoteID RemoteID, psk PSK) error {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
f.psks[remoteID] = psk f.psks[remoteID] = psk
return nil return nil
} }
func (f *fakeWG) OnRekeyFailed(remoteID string) error { func (f *fakeWG) OnRekeyFailed(remoteID RemoteID) error {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
f.failed = append(f.failed, remoteID) f.failed = append(f.failed, remoteID)
return nil return nil
} }
func (f *fakeWG) psk(peer string) PSK { func (f *fakeWG) psk(peer RemoteID) PSK {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
return f.psks[peer] return f.psks[peer]
@@ -100,9 +100,9 @@ func pair(t *testing.T) (dA, dB *Manager, wgA, wgB *fakeWG, lbB *loopback) {
wgB = newFakeWG() wgB = newFakeWG()
dA = NewManager("aaaa", wgA, nil) dA = NewManager("aaaa", wgA, nil)
dB = NewManager("bbbb", wgB, nil) dB = NewManager("bbbb", wgB, nil)
dA.SetTransport(&loopback{ep: epA, sw: sw}) dA.Start(&loopback{ep: epA, sw: sw})
lbB = &loopback{ep: epB, sw: sw} lbB = &loopback{ep: epB, sw: sw}
dB.SetTransport(lbB) dB.Start(lbB)
dA.AddPeer("bbbb", epB) dA.AddPeer("bbbb", epB)
dB.AddPeer("aaaa", epA) dB.AddPeer("aaaa", epA)
return dA, dB, wgA, wgB, lbB return dA, dB, wgA, wgB, lbB
@@ -163,7 +163,7 @@ func TestManager_NonInitiatorReturnsNoOffer(t *testing.T) {
func TestManager_StopIsIdempotent(t *testing.T) { func TestManager_StopIsIdempotent(t *testing.T) {
dA := NewManager("aaaa", newFakeWG(), nil) dA := NewManager("aaaa", newFakeWG(), nil)
dA.SetTransport(&loopback{ep: epA, sw: newSwitch()}) dA.Start(&loopback{ep: epA, sw: newSwitch()})
dA.Stop() dA.Stop()
dA.Stop() // must not panic or hang dA.Stop() // must not panic or hang
} }
+3 -3
View File
@@ -21,13 +21,13 @@ type pqCallbackHandler struct {
// OnNewPSKReady programs the freshly derived PSK for the peer (updateOnly: a no-op // OnNewPSKReady programs the freshly derived PSK for the peer (updateOnly: a no-op
// if the peer is not present, mirroring Rosenpass). remoteID is the peer's WG pubkey. // if the peer is not present, mirroring Rosenpass). remoteID is the peer's WG pubkey.
func (h pqCallbackHandler) OnNewPSKReady(remoteID string, psk pqkem.PSK) error { func (h pqCallbackHandler) OnNewPSKReady(remoteID pqkem.RemoteID, psk pqkem.PSK) error {
return h.wg.SetPresharedKey(remoteID, wgtypes.Key(psk), true) return h.wg.SetPresharedKey(string(remoteID), wgtypes.Key(psk), true)
} }
// OnRekeyFailed reports a failed PQ (re)key convergence. // OnRekeyFailed reports a failed PQ (re)key convergence.
// TODO(NET-1406): tear the peer connection down / trigger ICE reconnect. // TODO(NET-1406): tear the peer connection down / trigger ICE reconnect.
func (h pqCallbackHandler) OnRekeyFailed(remoteID string) error { 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
} }