Merge pull request #3745 from quic-go/aggressive-key-updates

initiate the first key update after sending / receiving 100 packets
This commit is contained in:
Marten Seemann
2023-04-19 16:28:33 +02:00
committed by GitHub
4 changed files with 100 additions and 29 deletions

View File

@@ -19,6 +19,10 @@ import (
// It's a package-level variable to allow modifying it for testing purposes.
var KeyUpdateInterval uint64 = protocol.KeyUpdateInterval
// FirstKeyUpdateInterval is the maximum number of packets we send or receive before initiating the first key update.
// It's a package-level variable to allow modifying it for testing purposes.
var FirstKeyUpdateInterval uint64 = 100
type updatableAEAD struct {
suite *qtls.CipherSuiteTLS13
@@ -27,7 +31,6 @@ type updatableAEAD struct {
firstPacketNumber protocol.PacketNumber
handshakeConfirmed bool
keyUpdateInterval uint64
invalidPacketLimit uint64
invalidPacketCount uint64
@@ -74,7 +77,6 @@ func newUpdatableAEAD(rttStats *utils.RTTStats, tracer logging.ConnectionTracer,
largestAcked: protocol.InvalidPacketNumber,
firstRcvdWithCurrentKey: protocol.InvalidPacketNumber,
firstSentWithCurrentKey: protocol.InvalidPacketNumber,
keyUpdateInterval: KeyUpdateInterval,
rttStats: rttStats,
tracer: tracer,
logger: logger,
@@ -116,6 +118,7 @@ func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte
return hkdfExpandLabel(hash, ts, []byte{}, "quic ku", hash.Size())
}
// SetReadKey sets the read key.
// For the client, this function is called before SetWriteKey.
// For the server, this function is called after SetWriteKey.
func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
@@ -129,6 +132,7 @@ func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret [
a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret, a.version)
}
// SetWriteKey sets the write key.
// For the client, this function is called after SetReadKey.
// For the server, this function is called before SetWriteKey.
func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) {
@@ -284,11 +288,17 @@ func (a *updatableAEAD) shouldInitiateKeyUpdate() bool {
if !a.updateAllowed() {
return false
}
if a.numRcvdWithCurrentKey >= a.keyUpdateInterval {
// Initiate the first key update shortly after the handshake, in order to exercise the key update mechanism.
if a.keyPhase == 0 {
if a.numRcvdWithCurrentKey >= FirstKeyUpdateInterval || a.numSentWithCurrentKey >= FirstKeyUpdateInterval {
return true
}
}
if a.numRcvdWithCurrentKey >= KeyUpdateInterval {
a.logger.Debugf("Received %d packets with current key phase. Initiating key update to the next key phase: %d", a.numRcvdWithCurrentKey, a.keyPhase+1)
return true
}
if a.numSentWithCurrentKey >= a.keyUpdateInterval {
if a.numSentWithCurrentKey >= KeyUpdateInterval {
a.logger.Debugf("Sent %d packets with current key phase. Initiating key update to the next key phase: %d", a.numSentWithCurrentKey, a.keyPhase+1)
return true
}

View File

@@ -4,15 +4,15 @@ import (
"crypto/rand"
"crypto/tls"
"fmt"
"testing"
"time"
"github.com/golang/mock/gomock"
mocklogging "github.com/quic-go/quic-go/internal/mocks/logging"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/qerr"
"github.com/quic-go/quic-go/internal/utils"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -282,16 +282,25 @@ var _ = Describe("Updatable AEAD", func() {
})
Context("initiating key updates", func() {
const firstKeyUpdateInterval = 5
const keyUpdateInterval = 20
var origKeyUpdateInterval, origFirstKeyUpdateInterval uint64
BeforeEach(func() {
Expect(server.keyUpdateInterval).To(BeEquivalentTo(protocol.KeyUpdateInterval))
server.keyUpdateInterval = keyUpdateInterval
origKeyUpdateInterval = KeyUpdateInterval
origFirstKeyUpdateInterval = FirstKeyUpdateInterval
KeyUpdateInterval = keyUpdateInterval
FirstKeyUpdateInterval = firstKeyUpdateInterval
server.SetHandshakeConfirmed()
})
AfterEach(func() {
KeyUpdateInterval = origKeyUpdateInterval
FirstKeyUpdateInterval = origFirstKeyUpdateInterval
})
It("initiates a key update after sealing the maximum number of packets, for the first update", func() {
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -323,7 +332,7 @@ var _ = Describe("Updatable AEAD", func() {
It("errors if the peer acknowledges a packet sent in the next key phase using the old key phase", func() {
// First make sure that we update our keys.
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -331,7 +340,7 @@ var _ = Describe("Updatable AEAD", func() {
serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne))
// Now that our keys are updated, send a packet using the new keys.
const nextPN = keyUpdateInterval + 1
const nextPN = firstKeyUpdateInterval + 1
server.Seal(nil, msg, nextPN, ad)
// We haven't decrypted any packet in the new key phase yet.
// This means that the ACK must have been sent in the old key phase.
@@ -343,7 +352,7 @@ var _ = Describe("Updatable AEAD", func() {
It("doesn't error before actually sending a packet in the new key phase", func() {
// First make sure that we update our keys.
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -361,7 +370,7 @@ var _ = Describe("Updatable AEAD", func() {
})
It("initiates a key update after opening the maximum number of packets, for the first update", func() {
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
encrypted := client.Seal(nil, msg, pn, ad)
@@ -394,7 +403,7 @@ var _ = Describe("Updatable AEAD", func() {
It("drops keys 3 PTOs after a key update", func() {
now := time.Now()
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -430,7 +439,7 @@ var _ = Describe("Updatable AEAD", func() {
data1 := client.Seal(nil, msg, 1, ad)
_, err := server.Open(nil, data1, now, 1, protocol.KeyPhaseZero, ad)
Expect(err).ToNot(HaveOccurred())
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -446,7 +455,7 @@ var _ = Describe("Updatable AEAD", func() {
})
It("drops keys early when the peer forces initiates a key update within the 3 PTO period", func() {
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -483,7 +492,7 @@ var _ = Describe("Updatable AEAD", func() {
It("drops keys early when we initiate another key update within the 3 PTO period", func() {
server.SetHandshakeConfirmed()
// send so many packets that we initiate the first key update
for i := 0; i < keyUpdateInterval; i++ {
for i := 0; i < firstKeyUpdateInterval; i++ {
pn := protocol.PacketNumber(i)
Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero))
server.Seal(nil, msg, pn, ad)
@@ -525,3 +534,60 @@ var _ = Describe("Updatable AEAD", func() {
})
}
})
func getClientAndServer() (client, server *updatableAEAD) {
trafficSecret1 := make([]byte, 16)
trafficSecret2 := make([]byte, 16)
rand.Read(trafficSecret1)
rand.Read(trafficSecret2)
cs := cipherSuites[0]
rttStats := utils.NewRTTStats()
client = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, protocol.Version1)
server = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, protocol.Version1)
client.SetReadKey(cs, trafficSecret2)
client.SetWriteKey(cs, trafficSecret1)
server.SetReadKey(cs, trafficSecret1)
server.SetWriteKey(cs, trafficSecret2)
return
}
func BenchmarkPacketEncryption(b *testing.B) {
client, _ := getClientAndServer()
const l = 1200
src := make([]byte, l)
rand.Read(src)
ad := make([]byte, 32)
rand.Read(ad)
for i := 0; i < b.N; i++ {
src = client.Seal(src[:0], src[:l], protocol.PacketNumber(i), ad)
}
}
func BenchmarkPacketDecryption(b *testing.B) {
client, server := getClientAndServer()
const l = 1200
src := make([]byte, l)
dst := make([]byte, l)
rand.Read(src)
ad := make([]byte, 32)
rand.Read(ad)
src = client.Seal(src[:0], src[:l], 1337, ad)
for i := 0; i < b.N; i++ {
if _, err := server.Open(dst[:0], src, time.Time{}, 1337, protocol.KeyPhaseZero, ad); err != nil {
b.Fatalf("opening failed: %v", err)
}
}
}
func BenchmarkRollKeys(b *testing.B) {
client, _ := getClientAndServer()
for i := 0; i < b.N; i++ {
client.rollKeys()
}
if int(client.keyPhase) != b.N {
b.Fatal("didn't roll keys often enough")
}
}