Skip to content

Commit

Permalink
introduce a type for the stateless reset key (#3621)
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-seemann committed Nov 16, 2022
1 parent b844704 commit 8d496eb
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 27 deletions.
5 changes: 3 additions & 2 deletions client_test.go
Expand Up @@ -449,14 +449,15 @@ 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,
MaxIdleTimeout: 42 * time.Hour,
MaxIncomingStreams: 1234,
MaxIncomingUniStreams: 4321,
ConnectionIDLength: 13,
StatelessResetKey: []byte("foobar"),
StatelessResetKey: srk,
TokenStore: tokenStore,
EnableDatagrams: true,
}
Expand All @@ -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())
})
Expand Down
2 changes: 1 addition & 1 deletion config_test.go
Expand Up @@ -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":
Expand Down
6 changes: 3 additions & 3 deletions integrationtests/self/stateless_reset_test.go
Expand Up @@ -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())
Expand Down
5 changes: 4 additions & 1 deletion interface.go
Expand Up @@ -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.
Expand Down Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion mock_multiplexer_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions multiplexer.go
@@ -1,7 +1,6 @@
package quic

import (
"bytes"
"fmt"
"net"
"sync"
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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()
Expand All @@ -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 {
Expand Down
11 changes: 7 additions & 4 deletions multiplexer_test.go
Expand Up @@ -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))
})
Expand All @@ -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"))
})

Expand Down
8 changes: 5 additions & 3 deletions packet_handler_map.go
Expand Up @@ -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) {
Expand All @@ -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()

Expand Down
8 changes: 4 additions & 4 deletions packet_handler_map_test.go
Expand Up @@ -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 {
Expand Down Expand Up @@ -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() {
Expand Down
4 changes: 2 additions & 2 deletions server_test.go
Expand Up @@ -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)
Expand All @@ -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())
})
Expand Down

0 comments on commit 8d496eb

Please sign in to comment.