introduce a type for the stateless reset key (#3621)

This commit is contained in:
Marten Seemann 2022-11-16 16:11:25 -06:00 committed by GitHub
parent b8447041bb
commit 8d496ebb5e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 35 additions and 27 deletions

View file

@ -449,6 +449,7 @@ var _ = Describe("Client", func() {
Context("quic.Config", func() {
It("setups with the right values", func() {
srk := &StatelessResetKey{'f', 'o', 'o', 'b', 'a', 'r'}
tokenStore := NewLRUTokenStore(10, 4)
config := &Config{
HandshakeIdleTimeout: 1337 * time.Minute,
@ -456,7 +457,7 @@ var _ = Describe("Client", func() {
MaxIncomingStreams: 1234,
MaxIncomingUniStreams: 4321,
ConnectionIDLength: 13,
StatelessResetKey: []byte("foobar"),
StatelessResetKey: srk,
TokenStore: tokenStore,
EnableDatagrams: true,
}
@ -466,7 +467,7 @@ var _ = Describe("Client", func() {
Expect(c.MaxIncomingStreams).To(BeEquivalentTo(1234))
Expect(c.MaxIncomingUniStreams).To(BeEquivalentTo(4321))
Expect(c.ConnectionIDLength).To(Equal(13))
Expect(c.StatelessResetKey).To(Equal([]byte("foobar")))
Expect(c.StatelessResetKey).To(Equal(srk))
Expect(c.TokenStore).To(Equal(tokenStore))
Expect(c.EnableDatagrams).To(BeTrue())
})

View file

@ -76,7 +76,7 @@ var _ = Describe("Config", func() {
case "MaxIncomingUniStreams":
f.Set(reflect.ValueOf(int64(12)))
case "StatelessResetKey":
f.Set(reflect.ValueOf([]byte{1, 2, 3, 4}))
f.Set(reflect.ValueOf(&StatelessResetKey{1, 2, 3, 4}))
case "KeepAlivePeriod":
f.Set(reflect.ValueOf(time.Second))
case "EnableDatagrams":

View file

@ -23,9 +23,9 @@ var _ = Describe("Stateless Resets", func() {
connIDLen := connIDLens[i]
It(fmt.Sprintf("sends and recognizes stateless resets, for %d byte connection IDs", connIDLen), func() {
statelessResetKey := make([]byte, 32)
rand.Read(statelessResetKey)
serverConfig := getQuicConfig(&quic.Config{StatelessResetKey: statelessResetKey})
var statelessResetKey quic.StatelessResetKey
rand.Read(statelessResetKey[:])
serverConfig := getQuicConfig(&quic.Config{StatelessResetKey: &statelessResetKey})
ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig)
Expect(err).ToNot(HaveOccurred())

View file

@ -201,6 +201,9 @@ type EarlyConnection interface {
NextConnection() Connection
}
// StatelessResetKey is a key used to derive stateless reset tokens.
type StatelessResetKey [32]byte
// A ConnectionID is a QUIC Connection ID, as defined in RFC 9000.
// It is not able to handle QUIC Connection IDs longer than 20 bytes,
// as they are allowed by RFC 8999.
@ -309,7 +312,7 @@ type Config struct {
MaxIncomingUniStreams int64
// The StatelessResetKey is used to generate stateless reset tokens.
// If no key is configured, sending of stateless resets is disabled.
StatelessResetKey []byte
StatelessResetKey *StatelessResetKey
// KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive.
// If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most
// every half of MaxIdleTimeout, whichever is smaller).

View file

@ -36,7 +36,7 @@ func (m *MockMultiplexer) EXPECT() *MockMultiplexerMockRecorder {
}
// AddConn mocks base method.
func (m *MockMultiplexer) AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer) (packetHandlerManager, error) {
func (m *MockMultiplexer) AddConn(c net.PacketConn, connIDLen int, statelessResetKey *StatelessResetKey, tracer logging.Tracer) (packetHandlerManager, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddConn", c, connIDLen, statelessResetKey, tracer)
ret0, _ := ret[0].(packetHandlerManager)

View file

@ -1,7 +1,6 @@
package quic
import (
"bytes"
"fmt"
"net"
"sync"
@ -20,13 +19,13 @@ type indexableConn interface {
}
type multiplexer interface {
AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer) (packetHandlerManager, error)
AddConn(c net.PacketConn, connIDLen int, statelessResetKey *StatelessResetKey, tracer logging.Tracer) (packetHandlerManager, error)
RemoveConn(indexableConn) error
}
type connManager struct {
connIDLen int
statelessResetKey []byte
statelessResetKey *StatelessResetKey
tracer logging.Tracer
manager packetHandlerManager
}
@ -37,7 +36,7 @@ type connMultiplexer struct {
mutex sync.Mutex
conns map[string] /* LocalAddr().String() */ connManager
newPacketHandlerManager func(net.PacketConn, int, []byte, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
newPacketHandlerManager func(net.PacketConn, int, *StatelessResetKey, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests
logger utils.Logger
}
@ -58,7 +57,7 @@ func getMultiplexer() multiplexer {
func (m *connMultiplexer) AddConn(
c net.PacketConn,
connIDLen int,
statelessResetKey []byte,
statelessResetKey *StatelessResetKey,
tracer logging.Tracer,
) (packetHandlerManager, error) {
m.mutex.Lock()
@ -83,7 +82,7 @@ func (m *connMultiplexer) AddConn(
if p.connIDLen != connIDLen {
return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen)
}
if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) {
if statelessResetKey != nil && p.statelessResetKey != statelessResetKey {
return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn")
}
if tracer != p.tracer {

View file

@ -25,15 +25,16 @@ var _ = Describe("Multiplexer", func() {
})
It("recognizes when the same connection is added twice", func() {
srk := &StatelessResetKey{'f', 'o', 'o', 'b', 'a', 'r'}
pconn := NewMockPacketConn(mockCtrl)
pconn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 4321}).Times(2)
pconn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1)
conn := testConn{PacketConn: pconn}
tracer := mocklogging.NewMockTracer(mockCtrl)
_, err := getMultiplexer().AddConn(conn, 8, []byte("foobar"), tracer)
_, err := getMultiplexer().AddConn(conn, 8, srk, tracer)
Expect(err).ToNot(HaveOccurred())
conn.counter++
_, err = getMultiplexer().AddConn(conn, 8, []byte("foobar"), tracer)
_, err = getMultiplexer().AddConn(conn, 8, srk, tracer)
Expect(err).ToNot(HaveOccurred())
Expect(getMultiplexer().(*connMultiplexer).conns).To(HaveLen(1))
})
@ -49,12 +50,14 @@ var _ = Describe("Multiplexer", func() {
})
It("errors when adding an existing conn with a different stateless rest key", func() {
srk1 := &StatelessResetKey{'f', 'o', 'o'}
srk2 := &StatelessResetKey{'b', 'a', 'r'}
conn := NewMockPacketConn(mockCtrl)
conn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1)
conn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}).Times(2)
_, err := getMultiplexer().AddConn(conn, 7, []byte("foobar"), nil)
_, err := getMultiplexer().AddConn(conn, 7, srk1, nil)
Expect(err).ToNot(HaveOccurred())
_, err = getMultiplexer().AddConn(conn, 7, []byte("raboof"), nil)
_, err = getMultiplexer().AddConn(conn, 7, srk2, nil)
Expect(err).To(MatchError("cannot use different stateless reset keys on the same packet conn"))
})

View file

@ -105,7 +105,7 @@ var receiveBufferWarningOnce sync.Once
func newPacketHandlerMap(
c net.PacketConn,
connIDLen int,
statelessResetKey []byte,
statelessResetKey *StatelessResetKey,
tracer logging.Tracer,
logger utils.Logger,
) (packetHandlerManager, error) {
@ -132,11 +132,13 @@ func newPacketHandlerMap(
deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout,
zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration,
closeQueue: make(chan closePacket, 4),
statelessResetEnabled: len(statelessResetKey) > 0,
statelessResetHasher: hmac.New(sha256.New, statelessResetKey),
statelessResetEnabled: statelessResetKey != nil,
tracer: tracer,
logger: logger,
}
if m.statelessResetEnabled {
m.statelessResetHasher = hmac.New(sha256.New, statelessResetKey[:])
}
go m.listen()
go m.runCloseQueue()

View file

@ -33,7 +33,7 @@ var _ = Describe("Packet Handler Map", func() {
packetChan chan packetToRead
connIDLen int
statelessResetKey []byte
statelessResetKey *StatelessResetKey
)
getPacketWithPacketType := func(connID protocol.ConnectionID, t protocol.PacketType, length protocol.ByteCount) []byte {
@ -440,9 +440,9 @@ var _ = Describe("Packet Handler Map", func() {
Context("generating", func() {
BeforeEach(func() {
key := make([]byte, 32)
rand.Read(key)
statelessResetKey = key
var key StatelessResetKey
rand.Read(key[:])
statelessResetKey = &key
})
It("generates stateless reset tokens", func() {

View file

@ -140,7 +140,7 @@ var _ = Describe("Server", func() {
HandshakeIdleTimeout: 1337 * time.Hour,
MaxIdleTimeout: 42 * time.Minute,
KeepAlivePeriod: 5 * time.Second,
StatelessResetKey: []byte("foobar"),
StatelessResetKey: &StatelessResetKey{'f', 'o', 'o', 'b', 'a', 'r'},
RequireAddressValidation: requireAddrVal,
}
ln, err := Listen(conn, tlsConf, &config)
@ -152,7 +152,7 @@ var _ = Describe("Server", func() {
Expect(server.config.MaxIdleTimeout).To(Equal(42 * time.Minute))
Expect(reflect.ValueOf(server.config.RequireAddressValidation)).To(Equal(reflect.ValueOf(requireAddrVal)))
Expect(server.config.KeepAlivePeriod).To(Equal(5 * time.Second))
Expect(server.config.StatelessResetKey).To(Equal([]byte("foobar")))
Expect(server.config.StatelessResetKey).To(Equal(&StatelessResetKey{'f', 'o', 'o', 'b', 'a', 'r'}))
// stop the listener
Expect(ln.Close()).To(Succeed())
})