forked from quic-go/quic-go
count number of failed decryptions, error when lifetime-limit is reached
This commit is contained in:
@@ -3,6 +3,7 @@ package handshake
|
|||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -18,7 +19,7 @@ import (
|
|||||||
|
|
||||||
// By setting this environment variable, the key update interval can be adjusted.
|
// By setting this environment variable, the key update interval can be adjusted.
|
||||||
// This is not needed in production, but useful for integration and interop testing.
|
// This is not needed in production, but useful for integration and interop testing.
|
||||||
// Note that no mattter what value is set, a key update is only initiated once it is
|
// Note that no matter what value is set, a key update is only initiated once it is
|
||||||
// permitted (i.e. once an ACK for a packet sent at the current key phase has been received).
|
// permitted (i.e. once an ACK for a packet sent at the current key phase has been received).
|
||||||
const keyUpdateEnv = "QUIC_GO_KEY_UPDATE_INTERVAL"
|
const keyUpdateEnv = "QUIC_GO_KEY_UPDATE_INTERVAL"
|
||||||
|
|
||||||
@@ -47,7 +48,10 @@ type updatableAEAD struct {
|
|||||||
keyPhase protocol.KeyPhase
|
keyPhase protocol.KeyPhase
|
||||||
largestAcked protocol.PacketNumber
|
largestAcked protocol.PacketNumber
|
||||||
firstPacketNumber protocol.PacketNumber
|
firstPacketNumber protocol.PacketNumber
|
||||||
keyUpdateInterval uint64
|
|
||||||
|
keyUpdateInterval uint64
|
||||||
|
invalidPacketLimit uint64
|
||||||
|
invalidPacketCount uint64
|
||||||
|
|
||||||
// Time when the keys should be dropped. Keys are dropped on the next call to Open().
|
// Time when the keys should be dropped. Keys are dropped on the next call to Open().
|
||||||
prevRcvAEADExpiry time.Time
|
prevRcvAEADExpiry time.Time
|
||||||
@@ -127,9 +131,7 @@ func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret [
|
|||||||
a.rcvAEAD = createAEAD(suite, trafficSecret)
|
a.rcvAEAD = createAEAD(suite, trafficSecret)
|
||||||
a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false)
|
a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false)
|
||||||
if a.suite == nil {
|
if a.suite == nil {
|
||||||
a.nonceBuf = make([]byte, a.rcvAEAD.NonceSize())
|
a.setAEADParameters(a.rcvAEAD, suite)
|
||||||
a.aeadOverhead = a.rcvAEAD.Overhead()
|
|
||||||
a.suite = suite
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
||||||
@@ -142,16 +144,39 @@ func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret
|
|||||||
a.sendAEAD = createAEAD(suite, trafficSecret)
|
a.sendAEAD = createAEAD(suite, trafficSecret)
|
||||||
a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false)
|
a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false)
|
||||||
if a.suite == nil {
|
if a.suite == nil {
|
||||||
a.nonceBuf = make([]byte, a.sendAEAD.NonceSize())
|
a.setAEADParameters(a.sendAEAD, suite)
|
||||||
a.aeadOverhead = a.sendAEAD.Overhead()
|
|
||||||
a.suite = suite
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret)
|
||||||
a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret)
|
a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) {
|
||||||
|
a.nonceBuf = make([]byte, aead.NonceSize())
|
||||||
|
a.aeadOverhead = aead.Overhead()
|
||||||
|
a.suite = suite
|
||||||
|
switch suite.ID {
|
||||||
|
case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
|
||||||
|
a.invalidPacketLimit = protocol.InvalidPacketLimitAES
|
||||||
|
case tls.TLS_CHACHA20_POLY1305_SHA256:
|
||||||
|
a.invalidPacketLimit = protocol.InvalidPacketLimitChaCha
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknown cipher suite %d", suite.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) {
|
func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) {
|
||||||
|
dec, err := a.open(dst, src, rcvTime, pn, kp, ad)
|
||||||
|
if err == ErrDecryptionFailed {
|
||||||
|
a.invalidPacketCount++
|
||||||
|
if a.invalidPacketCount >= a.invalidPacketLimit {
|
||||||
|
return nil, qerr.AEADLimitReached
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dec, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) {
|
||||||
if a.prevRcvAEAD != nil && !a.prevRcvAEADExpiry.IsZero() && rcvTime.After(a.prevRcvAEADExpiry) {
|
if a.prevRcvAEAD != nil && !a.prevRcvAEADExpiry.IsZero() && rcvTime.After(a.prevRcvAEADExpiry) {
|
||||||
a.prevRcvAEAD = nil
|
a.prevRcvAEAD = nil
|
||||||
a.logger.Debugf("Dropping key phase %d", a.keyPhase-1)
|
a.logger.Debugf("Dropping key phase %d", a.keyPhase-1)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging"
|
mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging"
|
||||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||||
|
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||||
"github.com/lucas-clemente/quic-go/internal/qtls"
|
"github.com/lucas-clemente/quic-go/internal/qtls"
|
||||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||||
|
|
||||||
@@ -116,6 +117,16 @@ var _ = Describe("Updatable AEAD", func() {
|
|||||||
Expect(err).To(MatchError(ErrDecryptionFailed))
|
Expect(err).To(MatchError(ErrDecryptionFailed))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("returns an AEAD_LIMIT_REACHED error when reaching the AEAD limit", func() {
|
||||||
|
client.invalidPacketLimit = 10
|
||||||
|
for i := 0; i < 9; i++ {
|
||||||
|
_, err := client.Open(nil, []byte("foobar"), time.Now(), protocol.PacketNumber(i), protocol.KeyPhaseZero, []byte("ad"))
|
||||||
|
Expect(err).To(MatchError(ErrDecryptionFailed))
|
||||||
|
}
|
||||||
|
_, err := client.Open(nil, []byte("foobar"), time.Now(), 10, protocol.KeyPhaseZero, []byte("ad"))
|
||||||
|
Expect(err).To(MatchError(qerr.AEADLimitReached))
|
||||||
|
})
|
||||||
|
|
||||||
Context("key updates", func() {
|
Context("key updates", func() {
|
||||||
Context("receiving key updates", func() {
|
Context("receiving key updates", func() {
|
||||||
It("updates keys", func() {
|
It("updates keys", func() {
|
||||||
|
|||||||
@@ -75,3 +75,10 @@ const MaxMaxAckDelay = (1<<14 - 1) * time.Millisecond
|
|||||||
|
|
||||||
// MaxConnIDLen is the maximum length of the connection ID
|
// MaxConnIDLen is the maximum length of the connection ID
|
||||||
const MaxConnIDLen = 20
|
const MaxConnIDLen = 20
|
||||||
|
|
||||||
|
// InvalidPacketLimitAES is the maximum number of packets that we can fail to decrypt when using
|
||||||
|
// AEAD_AES_128_GCM or AEAD_AES_265_GCM.
|
||||||
|
const InvalidPacketLimitAES = 1 << 54
|
||||||
|
|
||||||
|
// InvalidPacketLimitChaCha is the maximum number of packets that we can fail to decrypt when using AEAD_CHACHA20_POLY1305.
|
||||||
|
const InvalidPacketLimitChaCha = 1 << 36
|
||||||
|
|||||||
Reference in New Issue
Block a user