aboutsummaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2026-10-06 18:45:05 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2026-10-06 18:45:05 +0200
commit2631ce99a06f27120d581611cf125d68bc6aa565 (patch)
tree1e1449d5c60efde2f7427a307802bda85dc02e60 /device
parenttun: clear virtioNetHdr when deleting item from tcpGROTable (diff)
downloadwireguard-go-master.tar.xz
wireguard-go-master.zip
device: noise: reject response consumption after intermediate initiationHEADmaster
Two threads begin processing the identical response message, received twice. The first thread, A, runs. While it's running, the second one, B, gets partway through, and during that slow calculation, A completes and then also a handshake initiation that's already been queued up runs in thread C. The handshake initiation creation succeeds, and sets the state back to waiting-for-response, at which point thread B resumes. Thread B then copies the state back to the peer, and begins a new session, using that state, which is the same session as the one made in thread A. This seems basically impossible to hit in a meaningful way in practice, but ensure that it absolutely cannot happen by comparing the ephemeral private key that's on the stack with the latest one that the peer's handshake state has. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'device')
-rw-r--r--device/noise-protocol.go27
1 files changed, 18 insertions, 9 deletions
diff --git a/device/noise-protocol.go b/device/noise-protocol.go
index 5cf1702..a0aca36 100644
--- a/device/noise-protocol.go
+++ b/device/noise-protocol.go
@@ -509,7 +509,7 @@ func (device *Device) CreateMessageResponse(peer *Peer) (*MessageResponse, error
return &msg, nil
}
-func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
+func (device *Device) ConsumeMessageResponse(msg *MessageResponse) (peer *Peer) {
if msg.Type != MessageResponseType {
return nil
}
@@ -523,8 +523,9 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
}
var (
- hash [blake2s.Size]byte
- chainKey [blake2s.Size]byte
+ hash [blake2s.Size]byte
+ chainKey [blake2s.Size]byte
+ localEphemeral NoisePrivateKey
)
ok := func() bool {
@@ -582,6 +583,10 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
return false
}
mixHash(&hash, &hash, msg.Empty[:])
+
+ // allow unlocking temporarily
+ copy(localEphemeral[:], handshake.localEphemeral[:])
+
return true
}()
@@ -592,18 +597,22 @@ func (device *Device) ConsumeMessageResponse(msg *MessageResponse) *Peer {
// update handshake state
handshake.mutex.Lock()
-
- handshake.hash = hash
- handshake.chainKey = chainKey
- handshake.remoteIndex = msg.Sender
- handshake.state = handshakeResponseConsumed
+ if handshake.state == handshakeInitiationCreated &&
+ handshake.localEphemeral.Equals(localEphemeral) {
+ handshake.hash = hash
+ handshake.chainKey = chainKey
+ handshake.remoteIndex = msg.Sender
+ handshake.state = handshakeResponseConsumed
+ peer = lookup.peer
+ }
handshake.mutex.Unlock()
setZero(hash[:])
setZero(chainKey[:])
+ setZero(localEphemeral[:])
- return lookup.peer
+ return
}
/* Derives a new keypair from the current handshake state