Added last_minute_handshake_guard

- Added last_minute_handshake_guard and reverted keypair changes.
- Added comment explaining the state of Go in releation to handling
  cryptographic state in memory.
- Decreased logging level of netsh test
This commit is contained in:
Mathias Hall-Andersen 2017-09-20 09:26:08 +02:00
parent f212795e51
commit 47a21c8bb0
7 changed files with 61 additions and 92 deletions

View file

@ -2,38 +2,20 @@ package main
import ( import (
"crypto/cipher" "crypto/cipher"
"golang.org/x/crypto/chacha20poly1305"
"reflect"
"sync" "sync"
"time" "time"
) )
type safeAEAD struct { /* Due to limitations in Go and /x/crypto there is currently
mutex sync.RWMutex * no way to ensure that key material is securely ereased in memory.
aead cipher.AEAD *
} * Since this may harm the forward secrecy property,
* we plan to resolve this issue; whenever Go allows us to do so.
func (con *safeAEAD) clear() { */
// TODO: improve handling of key material
con.mutex.Lock()
if con.aead != nil {
val := reflect.ValueOf(con.aead)
elm := val.Elem()
typ := elm.Type()
elm.Set(reflect.Zero(typ))
con.aead = nil
}
con.mutex.Unlock()
}
func (con *safeAEAD) setKey(key *[chacha20poly1305.KeySize]byte) {
// TODO: improve handling of key material
con.aead, _ = chacha20poly1305.New(key[:])
}
type KeyPair struct { type KeyPair struct {
send safeAEAD send cipher.AEAD
receive safeAEAD receive cipher.AEAD
replayFilter ReplayFilter replayFilter ReplayFilter
sendNonce uint64 sendNonce uint64
isInitiator bool isInitiator bool
@ -56,7 +38,5 @@ func (kp *KeyPairs) Current() *KeyPair {
} }
func (device *Device) DeleteKeyPair(key *KeyPair) { func (device *Device) DeleteKeyPair(key *KeyPair) {
key.send.clear()
key.receive.clear()
device.indices.Delete(key.localIndex) device.indices.Delete(key.localIndex)
} }

View file

@ -502,8 +502,8 @@ func (peer *Peer) NewKeyPair() *KeyPair {
// create AEAD instances // create AEAD instances
keyPair := new(KeyPair) keyPair := new(KeyPair)
keyPair.send.setKey(&sendKey) keyPair.send, _ = chacha20poly1305.New(sendKey[:])
keyPair.receive.setKey(&recvKey) keyPair.receive, _ = chacha20poly1305.New(recvKey[:])
setZero(sendKey[:]) setZero(sendKey[:])
setZero(recvKey[:]) setZero(recvKey[:])
@ -530,30 +530,29 @@ func (peer *Peer) NewKeyPair() *KeyPair {
// rotate key pairs // rotate key pairs
kp := &peer.keyPairs kp := &peer.keyPairs
func() { kp.mutex.Lock()
kp.mutex.Lock()
defer kp.mutex.Unlock()
// TODO: Adapt kernel behavior noise.c:161
if isInitiator {
if kp.previous != nil {
device.DeleteKeyPair(kp.previous)
kp.previous = nil
}
if kp.next != nil { // TODO: Adapt kernel behavior noise.c:161
kp.previous = kp.next if isInitiator {
kp.next = keyPair if kp.previous != nil {
} else { device.DeleteKeyPair(kp.previous)
kp.previous = kp.current kp.previous = nil
kp.current = keyPair
signalSend(peer.signal.newKeyPair) // TODO: This more places (after confirming the key)
}
} else {
kp.next = keyPair
kp.previous = nil // TODO: Discuss why
} }
}()
if kp.next != nil {
kp.previous = kp.next
kp.next = keyPair
} else {
kp.previous = kp.current
kp.current = keyPair
signalSend(peer.signal.newKeyPair) // TODO: This more places (after confirming the key)
}
} else {
kp.next = keyPair
kp.previous = nil
}
kp.mutex.Unlock()
return keyPair return keyPair
} }

View file

@ -39,6 +39,8 @@ type Peer struct {
stop chan struct{} // (size 0) : close to stop all goroutines for peer stop chan struct{} // (size 0) : close to stop all goroutines for peer
} }
timer struct { timer struct {
// state related to WireGuard timers
keepalivePersistent *time.Timer // set for persistent keepalives keepalivePersistent *time.Timer // set for persistent keepalives
keepalivePassive *time.Timer // set upon recieving messages keepalivePassive *time.Timer // set upon recieving messages
newHandshake *time.Timer // begin a new handshake (after Keepalive + RekeyTimeout) newHandshake *time.Timer // begin a new handshake (after Keepalive + RekeyTimeout)
@ -49,7 +51,8 @@ type Peer struct {
pendingNewHandshake bool pendingNewHandshake bool
pendingZeroAllKeys bool pendingZeroAllKeys bool
needAnotherKeepalive bool needAnotherKeepalive bool
sendLastMinuteHandshake bool
} }
queue struct { queue struct {
nonce chan *QueueOutboundElement // nonce / pre-handshake queue nonce chan *QueueOutboundElement // nonce / pre-handshake queue

View file

@ -247,28 +247,20 @@ func (device *Device) RoutineDecryption() {
counter := elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent] counter := elem.packet[MessageTransportOffsetCounter:MessageTransportOffsetContent]
content := elem.packet[MessageTransportOffsetContent:] content := elem.packet[MessageTransportOffsetContent:]
// decrypt with key-pair // decrypt and release to consumer
var err error
copy(nonce[4:], counter) copy(nonce[4:], counter)
elem.counter = binary.LittleEndian.Uint64(counter) elem.counter = binary.LittleEndian.Uint64(counter)
elem.keyPair.receive.mutex.RLock() elem.packet, err = elem.keyPair.receive.Open(
if elem.keyPair.receive.aead == nil { elem.buffer[:0],
// very unlikely (the key was deleted during queuing) nonce[:],
content,
nil,
)
if err != nil {
elem.Drop() elem.Drop()
} else {
var err error
elem.packet, err = elem.keyPair.receive.aead.Open(
elem.buffer[:0],
nonce[:],
content,
nil,
)
if err != nil {
elem.Drop()
}
} }
elem.keyPair.receive.mutex.RUnlock()
elem.mutex.Unlock() elem.mutex.Unlock()
} }
} }
@ -433,8 +425,6 @@ func (device *Device) RoutineHandshake() {
case MessageResponseType: case MessageResponseType:
logDebug.Println("Process response")
// unmarshal // unmarshal
var msg MessageResponse var msg MessageResponse
@ -457,6 +447,8 @@ func (device *Device) RoutineHandshake() {
continue continue
} }
logDebug.Println("Received handshake initation from", peer)
peer.TimerEphemeralKeyCreated() peer.TimerEphemeralKeyCreated()
// update timers // update timers

View file

@ -303,27 +303,16 @@ func (device *Device) RoutineEncryption() {
} }
} }
// encrypt content (append to header) // encrypt content and release to consumer
binary.LittleEndian.PutUint64(nonce[4:], elem.nonce) binary.LittleEndian.PutUint64(nonce[4:], elem.nonce)
elem.keyPair.send.mutex.RLock() elem.packet = elem.keyPair.send.Seal(
if elem.keyPair.send.aead == nil { header,
// very unlikely (the key was deleted during queuing) nonce[:],
elem.Drop() elem.packet,
} else { nil,
elem.packet = elem.keyPair.send.aead.Seal( )
header,
nonce[:],
elem.packet,
nil,
)
}
elem.mutex.Unlock() elem.mutex.Unlock()
elem.keyPair.send.mutex.RUnlock()
// refresh key if necessary
elem.peer.KeepKeyFreshSending()
} }
} }
} }

View file

@ -28,7 +28,7 @@ netns0="wg-test-$$-0"
netns1="wg-test-$$-1" netns1="wg-test-$$-1"
netns2="wg-test-$$-2" netns2="wg-test-$$-2"
program="../wireguard-go" program="../wireguard-go"
export LOG_LEVEL="debug" export LOG_LEVEL="error"
pretty() { echo -e "\x1b[32m\x1b[1m[+] ${1:+NS$1: }${2}\x1b[0m" >&3; } pretty() { echo -e "\x1b[32m\x1b[1m[+] ${1:+NS$1: }${2}\x1b[0m" >&3; }
pp() { pretty "" "$*"; "$@"; } pp() { pretty "" "$*"; "$@"; }

View file

@ -27,9 +27,12 @@ func (peer *Peer) KeepKeyFreshSending() {
/* Called when a new authenticated message has been recevied /* Called when a new authenticated message has been recevied
* *
* NOTE: Not thread safe (called by sequential receiver)
*/ */
func (peer *Peer) KeepKeyFreshReceiving() { func (peer *Peer) KeepKeyFreshReceiving() {
// TODO: Add a guard, clear on handshake complete (clear in TimerHandshakeComplete) if peer.timer.sendLastMinuteHandshake {
return
}
kp := peer.keyPairs.Current() kp := peer.keyPairs.Current()
if kp == nil { if kp == nil {
return return
@ -40,7 +43,9 @@ func (peer *Peer) KeepKeyFreshReceiving() {
nonce := atomic.LoadUint64(&kp.sendNonce) nonce := atomic.LoadUint64(&kp.sendNonce)
send := nonce > RekeyAfterMessages || time.Now().Sub(kp.created) > RekeyAfterTimeReceiving send := nonce > RekeyAfterMessages || time.Now().Sub(kp.created) > RekeyAfterTimeReceiving
if send { if send {
// do a last minute attempt at initiating a new handshake
signalSend(peer.signal.handshakeBegin) signalSend(peer.signal.handshakeBegin)
peer.timer.sendLastMinuteHandshake = true
} }
} }
@ -311,6 +316,7 @@ func (peer *Peer) RoutineHandshakeInitiator() {
case <-peer.signal.handshakeCompleted: case <-peer.signal.handshakeCompleted:
<-timeout.C <-timeout.C
peer.timer.sendLastMinuteHandshake = false
break AttemptHandshakes break AttemptHandshakes
case <-peer.signal.handshakeReset: case <-peer.signal.handshakeReset: