fix a race condition in CryptoSetup

CryptoSetup's AEADs were changed between calls to
DiversificationNonce() and Seal()
This commit is contained in:
Lucas Clemente
2016-06-02 16:13:06 +02:00
parent 60611a0176
commit 5ed0182b67
3 changed files with 23 additions and 14 deletions

View File

@@ -169,11 +169,8 @@ func (h *CryptoSetup) Open(packetNumber protocol.PacketNumber, associatedData []
return (&crypto.NullAEAD{}).Open(packetNumber, associatedData, ciphertext) return (&crypto.NullAEAD{}).Open(packetNumber, associatedData, ciphertext)
} }
// Seal a message // Seal a message, call LockForSealing() before!
func (h *CryptoSetup) Seal(packetNumber protocol.PacketNumber, associatedData []byte, plaintext []byte) []byte { func (h *CryptoSetup) Seal(packetNumber protocol.PacketNumber, associatedData []byte, plaintext []byte) []byte {
h.mutex.RLock()
defer h.mutex.RUnlock()
if h.receivedForwardSecurePacket { if h.receivedForwardSecurePacket {
return h.forwardSecureAEAD.Seal(packetNumber, associatedData, plaintext) return h.forwardSecureAEAD.Seal(packetNumber, associatedData, plaintext)
} else if h.secureAEAD != nil { } else if h.secureAEAD != nil {
@@ -308,7 +305,7 @@ func (h *CryptoSetup) handleCHLO(sni string, data []byte, cryptoData map[Tag][]b
return reply.Bytes(), nil return reply.Bytes(), nil
} }
// DiversificationNonce returns a diversification nonce if required in the next packet to be Seal'ed // DiversificationNonce returns a diversification nonce if required in the next packet to be Seal'ed. See LockForSealing()!
func (h *CryptoSetup) DiversificationNonce() []byte { func (h *CryptoSetup) DiversificationNonce() []byte {
if h.version < protocol.VersionNumber(33) { if h.version < protocol.VersionNumber(33) {
return nil return nil
@@ -319,6 +316,16 @@ func (h *CryptoSetup) DiversificationNonce() []byte {
return h.diversificationNonce return h.diversificationNonce
} }
// LockForSealing should be called before Seal(). It is needed so that diversification nonces can be obtained before packets are sealed, and the AEADs are not changed in the meantime.
func (h *CryptoSetup) LockForSealing() {
h.mutex.RLock()
}
// UnlockForSealing should be called after Seal() is complete, see LockForSealing().
func (h *CryptoSetup) UnlockForSealing() {
h.mutex.RUnlock()
}
func (h *CryptoSetup) verifyOrCreateSTK(token []byte) ([]byte, error) { func (h *CryptoSetup) verifyOrCreateSTK(token []byte) ([]byte, error) {
err := h.scfg.stkSource.VerifyToken(h.ip, token) err := h.scfg.stkSource.VerifyToken(h.ip, token)
if err != nil { if err != nil {

View File

@@ -6,7 +6,6 @@ import (
"sync/atomic" "sync/atomic"
"github.com/lucas-clemente/quic-go/ackhandler" "github.com/lucas-clemente/quic-go/ackhandler"
"github.com/lucas-clemente/quic-go/crypto"
"github.com/lucas-clemente/quic-go/frames" "github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/handshake" "github.com/lucas-clemente/quic-go/handshake"
"github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/protocol"
@@ -23,7 +22,7 @@ type packedPacket struct {
type packetPacker struct { type packetPacker struct {
connectionID protocol.ConnectionID connectionID protocol.ConnectionID
version protocol.VersionNumber version protocol.VersionNumber
aead crypto.AEAD cryptoSetup *handshake.CryptoSetup
sentPacketHandler ackhandler.SentPacketHandler sentPacketHandler ackhandler.SentPacketHandler
connectionParametersManager *handshake.ConnectionParametersManager connectionParametersManager *handshake.ConnectionParametersManager
@@ -35,9 +34,9 @@ type packetPacker struct {
lastPacketNumber protocol.PacketNumber lastPacketNumber protocol.PacketNumber
} }
func newPacketPacker(connectionID protocol.ConnectionID, aead crypto.AEAD, sentPacketHandler ackhandler.SentPacketHandler, connectionParametersHandler *handshake.ConnectionParametersManager, blockedManager *blockedManager, version protocol.VersionNumber) *packetPacker { func newPacketPacker(connectionID protocol.ConnectionID, cryptoSetup *handshake.CryptoSetup, sentPacketHandler ackhandler.SentPacketHandler, connectionParametersHandler *handshake.ConnectionParametersManager, blockedManager *blockedManager, version protocol.VersionNumber) *packetPacker {
return &packetPacker{ return &packetPacker{
aead: aead, cryptoSetup: cryptoSetup,
connectionID: connectionID, connectionID: connectionID,
connectionParametersManager: connectionParametersHandler, connectionParametersManager: connectionParametersHandler,
version: version, version: version,
@@ -89,13 +88,18 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, con
1, 1,
)) ))
// cryptoSetup needs to be locked here, so that the AEADs are not changed between
// calling DiversificationNonce() and Seal().
p.cryptoSetup.LockForSealing()
defer p.cryptoSetup.UnlockForSealing()
packetNumberLen := protocol.GetPacketNumberLengthForPublicHeader(currentPacketNumber, p.sentPacketHandler.GetLargestObserved()) packetNumberLen := protocol.GetPacketNumberLengthForPublicHeader(currentPacketNumber, p.sentPacketHandler.GetLargestObserved())
responsePublicHeader := &publicHeader{ responsePublicHeader := &publicHeader{
ConnectionID: p.connectionID, ConnectionID: p.connectionID,
PacketNumber: currentPacketNumber, PacketNumber: currentPacketNumber,
PacketNumberLen: packetNumberLen, PacketNumberLen: packetNumberLen,
TruncateConnectionID: p.connectionParametersManager.TruncateConnectionID(), TruncateConnectionID: p.connectionParametersManager.TruncateConnectionID(),
DiversificationNonce: p.aead.DiversificationNonce(), DiversificationNonce: p.cryptoSetup.DiversificationNonce(),
} }
publicHeaderLength, err := responsePublicHeader.GetLength() publicHeaderLength, err := responsePublicHeader.GetLength()
@@ -136,7 +140,7 @@ func (p *packetPacker) packPacket(stopWaitingFrame *frames.StopWaitingFrame, con
return nil, err return nil, err
} }
ciphertext := p.aead.Seal(currentPacketNumber, raw.Bytes(), payload) ciphertext := p.cryptoSetup.Seal(currentPacketNumber, raw.Bytes(), payload)
raw.Write(ciphertext) raw.Write(ciphertext)
if protocol.ByteCount(raw.Len()) > protocol.MaxPacketSize { if protocol.ByteCount(raw.Len()) > protocol.MaxPacketSize {

View File

@@ -5,7 +5,6 @@ import (
"time" "time"
"github.com/lucas-clemente/quic-go/ackhandler" "github.com/lucas-clemente/quic-go/ackhandler"
"github.com/lucas-clemente/quic-go/crypto"
"github.com/lucas-clemente/quic-go/frames" "github.com/lucas-clemente/quic-go/frames"
"github.com/lucas-clemente/quic-go/handshake" "github.com/lucas-clemente/quic-go/handshake"
"github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/protocol"
@@ -36,9 +35,8 @@ var _ = Describe("Packet packer", func() {
) )
BeforeEach(func() { BeforeEach(func() {
aead := &crypto.NullAEAD{}
packer = &packetPacker{ packer = &packetPacker{
aead: aead, cryptoSetup: &handshake.CryptoSetup{},
connectionParametersManager: handshake.NewConnectionParamatersManager(), connectionParametersManager: handshake.NewConnectionParamatersManager(),
sentPacketHandler: newMockSentPacketHandler(), sentPacketHandler: newMockSentPacketHandler(),
blockedManager: newBlockedManager(), blockedManager: newBlockedManager(),