Bind Noise sessions to claimed peer identities; require signed leaves (#1432)

Adds remote-static-key->peerID binding at Noise handshake completion (closes a mesh impersonation/MITM hole where a peer could complete a handshake under another peer's ID). Also hardens LEAVE handling to require a verified signature and suppresses relay of unverifiable leaves.
This commit is contained in:
jack
2026-07-26 10:30:55 +02:00
committed by GitHub
parent cd727c6867
commit 16324c819f
10 changed files with 637 additions and 76 deletions
@@ -8,6 +8,7 @@ struct BLENoisePacketHandlerTests {
private final class Recorder {
var handshakeResult: Result<Data?, Error> = .success(nil)
var handshakeAuthenticated = false
var hasSession = false
var decryptResult: Result<Data, Error> = .success(Data())
@@ -38,7 +39,11 @@ struct BLENoisePacketHandlerTests {
now: { now },
processHandshakeMessage: { peerID, message in
recorder.processedHandshakes.append((peerID, message))
return try recorder.handshakeResult.get()
return NoiseHandshakeProcessingResult(
response: try recorder.handshakeResult.get(),
didEstablishAuthenticatedSession:
recorder.handshakeAuthenticated
)
},
hasNoiseSession: { peerID in
recorder.hasSessionQueries.append(peerID)
@@ -110,6 +115,24 @@ struct BLENoisePacketHandlerTests {
#expect(recorder.initiatedHandshakes.isEmpty)
}
@Test
func handshakeResultPreservesExactCandidateAuthentication() {
let recorder = Recorder()
recorder.handshakeAuthenticated = true
let handler = makeHandler(recorder: recorder)
let packet = makeHandshakePacket(
recipientID: Data(hexString: localPeerID.id)
)
let result = handler.handleHandshakeWithResult(
packet,
from: remotePeerID
)
#expect(result.processed)
#expect(result.didEstablishAuthenticatedSession)
}
@Test
func handshakeForAnotherPeerIsIgnored() {
let recorder = Recorder()
@@ -152,6 +175,21 @@ struct BLENoisePacketHandlerTests {
#expect(recorder.initiatedHandshakes.isEmpty)
}
@Test
func peerIdentityMismatchDoesNotRecreateHandshakeState() {
let recorder = Recorder()
recorder.handshakeResult = .failure(NoiseSessionError.peerIdentityMismatch)
recorder.hasSession = false
let handler = makeHandler(recorder: recorder)
let packet = makeHandshakePacket(recipientID: Data(hexString: localPeerID.id))
#expect(!handler.handleHandshake(packet, from: remotePeerID))
#expect(recorder.hasSessionQueries.isEmpty)
#expect(recorder.initiatedHandshakes.isEmpty)
#expect(recorder.broadcastPackets.isEmpty)
}
// MARK: Encrypted
@Test