forked from quic-go/quic-go
move processing of transport parameters to the session
This commit is contained in:
@@ -62,7 +62,7 @@ type cryptoSetup struct {
|
||||
|
||||
extHandler tlsExtensionHandler
|
||||
|
||||
handleParamsCallback func(*TransportParameters)
|
||||
handleParamsCallback func([]byte)
|
||||
|
||||
// There are two ways that an error can occur during the handshake:
|
||||
// 1. as a return value from qtls.Handshake()
|
||||
@@ -106,34 +106,21 @@ var _ CryptoSetup = &cryptoSetup{}
|
||||
func NewCryptoSetupClient(
|
||||
initialStream io.Writer,
|
||||
handshakeStream io.Writer,
|
||||
origConnID protocol.ConnectionID,
|
||||
connID protocol.ConnectionID,
|
||||
params *TransportParameters,
|
||||
handleParams func(*TransportParameters),
|
||||
chtp *ClientHelloTransportParameters,
|
||||
handleParams func([]byte),
|
||||
tlsConf *tls.Config,
|
||||
initialVersion protocol.VersionNumber,
|
||||
supportedVersions []protocol.VersionNumber,
|
||||
currentVersion protocol.VersionNumber,
|
||||
logger utils.Logger,
|
||||
perspective protocol.Perspective,
|
||||
) (CryptoSetup, <-chan struct{} /* ClientHello written */, error) {
|
||||
extHandler := newExtensionHandlerClient(
|
||||
params,
|
||||
origConnID,
|
||||
initialVersion,
|
||||
supportedVersions,
|
||||
currentVersion,
|
||||
logger,
|
||||
)
|
||||
cs, clientHelloWritten, err := newCryptoSetup(
|
||||
initialStream,
|
||||
handshakeStream,
|
||||
connID,
|
||||
extHandler,
|
||||
chtp.Marshal(),
|
||||
handleParams,
|
||||
tlsConf,
|
||||
logger,
|
||||
perspective,
|
||||
protocol.PerspectiveClient,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -147,29 +134,20 @@ func NewCryptoSetupServer(
|
||||
initialStream io.Writer,
|
||||
handshakeStream io.Writer,
|
||||
connID protocol.ConnectionID,
|
||||
params *TransportParameters,
|
||||
handleParams func(*TransportParameters),
|
||||
eetp *EncryptedExtensionsTransportParameters,
|
||||
handleParams func([]byte),
|
||||
tlsConf *tls.Config,
|
||||
supportedVersions []protocol.VersionNumber,
|
||||
currentVersion protocol.VersionNumber,
|
||||
logger utils.Logger,
|
||||
perspective protocol.Perspective,
|
||||
) (CryptoSetup, error) {
|
||||
extHandler := newExtensionHandlerServer(
|
||||
params,
|
||||
supportedVersions,
|
||||
currentVersion,
|
||||
logger,
|
||||
)
|
||||
cs, _, err := newCryptoSetup(
|
||||
initialStream,
|
||||
handshakeStream,
|
||||
connID,
|
||||
extHandler,
|
||||
eetp.Marshal(),
|
||||
handleParams,
|
||||
tlsConf,
|
||||
logger,
|
||||
perspective,
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -182,8 +160,8 @@ func newCryptoSetup(
|
||||
initialStream io.Writer,
|
||||
handshakeStream io.Writer,
|
||||
connID protocol.ConnectionID,
|
||||
extHandler tlsExtensionHandler,
|
||||
handleParams func(*TransportParameters),
|
||||
paramBytes []byte, // the marshaled transport parameters
|
||||
handleParams func([]byte),
|
||||
tlsConf *tls.Config,
|
||||
logger utils.Logger,
|
||||
perspective protocol.Perspective,
|
||||
@@ -192,6 +170,7 @@ func newCryptoSetup(
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
extHandler := newExtensionHandler(paramBytes, perspective)
|
||||
cs := &cryptoSetup{
|
||||
initialStream: initialStream,
|
||||
initialSealer: initialSealer,
|
||||
@@ -309,8 +288,8 @@ func (h *cryptoSetup) handleMessageForServer(msgType messageType) bool {
|
||||
switch msgType {
|
||||
case typeClientHello:
|
||||
select {
|
||||
case params := <-h.extHandler.TransportParameters():
|
||||
h.handleParamsCallback(¶ms)
|
||||
case data := <-h.extHandler.TransportParameters():
|
||||
h.handleParamsCallback(data)
|
||||
case <-h.handshakeErrChan:
|
||||
return false
|
||||
}
|
||||
@@ -367,8 +346,8 @@ func (h *cryptoSetup) handleMessageForClient(msgType messageType) bool {
|
||||
return true
|
||||
case typeEncryptedExtensions:
|
||||
select {
|
||||
case params := <-h.extHandler.TransportParameters():
|
||||
h.handleParamsCallback(¶ms)
|
||||
case data := <-h.extHandler.TransportParameters():
|
||||
h.handleParamsCallback(data)
|
||||
case <-h.handshakeErrChan:
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -70,13 +70,13 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
sInitialStream,
|
||||
sHandshakeStream,
|
||||
protocol.ConnectionID{},
|
||||
&TransportParameters{},
|
||||
func(p *TransportParameters) {},
|
||||
&EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: protocol.VersionTLS,
|
||||
SupportedVersions: []protocol.VersionNumber{protocol.VersionTLS},
|
||||
},
|
||||
func([]byte) {},
|
||||
testdata.GetTLSConfig(),
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("server"),
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -100,13 +100,13 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
sInitialStream,
|
||||
sHandshakeStream,
|
||||
protocol.ConnectionID{},
|
||||
&TransportParameters{},
|
||||
func(p *TransportParameters) {},
|
||||
&EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: protocol.VersionTLS,
|
||||
SupportedVersions: []protocol.VersionNumber{protocol.VersionTLS},
|
||||
},
|
||||
func([]byte) {},
|
||||
testdata.GetTLSConfig(),
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("server"),
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -129,13 +129,13 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
sInitialStream,
|
||||
sHandshakeStream,
|
||||
protocol.ConnectionID{},
|
||||
&TransportParameters{},
|
||||
func(p *TransportParameters) {},
|
||||
&EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: protocol.VersionTLS,
|
||||
SupportedVersions: []protocol.VersionNumber{protocol.VersionTLS},
|
||||
},
|
||||
func([]byte) {},
|
||||
testdata.GetTLSConfig(),
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("server"),
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -208,16 +208,13 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
client, _, err := NewCryptoSetupClient(
|
||||
cInitialStream,
|
||||
cHandshakeStream,
|
||||
nil,
|
||||
protocol.ConnectionID{},
|
||||
&TransportParameters{},
|
||||
func(p *TransportParameters) {},
|
||||
&ClientHelloTransportParameters{
|
||||
InitialVersion: protocol.VersionTLS,
|
||||
},
|
||||
func([]byte) {},
|
||||
clientConf,
|
||||
protocol.VersionTLS,
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("client"),
|
||||
protocol.PerspectiveClient,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -226,13 +223,14 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
sInitialStream,
|
||||
sHandshakeStream,
|
||||
protocol.ConnectionID{},
|
||||
&TransportParameters{StatelessResetToken: bytes.Repeat([]byte{42}, 16)},
|
||||
func(p *TransportParameters) {},
|
||||
&EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: protocol.VersionTLS,
|
||||
SupportedVersions: []protocol.VersionNumber{protocol.VersionTLS},
|
||||
Parameters: TransportParameters{StatelessResetToken: bytes.Repeat([]byte{42}, 16)},
|
||||
},
|
||||
func([]byte) {},
|
||||
serverConf,
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("server"),
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -260,16 +258,13 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
client, chChan, err := NewCryptoSetupClient(
|
||||
cInitialStream,
|
||||
cHandshakeStream,
|
||||
nil,
|
||||
protocol.ConnectionID{},
|
||||
&TransportParameters{},
|
||||
func(p *TransportParameters) {},
|
||||
&ClientHelloTransportParameters{
|
||||
InitialVersion: protocol.VersionTLS,
|
||||
},
|
||||
func([]byte) {},
|
||||
&tls.Config{InsecureSkipVerify: true},
|
||||
protocol.VersionTLS,
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("client"),
|
||||
protocol.PerspectiveClient,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
@@ -293,58 +288,58 @@ var _ = Describe("Crypto Setup TLS", func() {
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("receives transport parameters", func() {
|
||||
var cTransportParametersRcvd, sTransportParametersRcvd *TransportParameters
|
||||
cChunkChan, cInitialStream, cHandshakeStream := initStreams()
|
||||
cTransportParameters := &TransportParameters{IdleTimeout: 0x42 * time.Second}
|
||||
client, _, err := NewCryptoSetupClient(
|
||||
cInitialStream,
|
||||
cHandshakeStream,
|
||||
nil,
|
||||
protocol.ConnectionID{},
|
||||
cTransportParameters,
|
||||
func(p *TransportParameters) { sTransportParametersRcvd = p },
|
||||
clientConf,
|
||||
protocol.VersionTLS,
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("client"),
|
||||
protocol.PerspectiveClient,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// It("receives transport parameters", func() {
|
||||
// var cTransportParametersRcvd, sTransportParametersRcvd *TransportParameters
|
||||
// cChunkChan, cInitialStream, cHandshakeStream := initStreams()
|
||||
// cTransportParameters := &TransportParameters{IdleTimeout: 0x42 * time.Second}
|
||||
// client, _, err := NewCryptoSetupClient(
|
||||
// cInitialStream,
|
||||
// cHandshakeStream,
|
||||
// nil,
|
||||
// protocol.ConnectionID{},
|
||||
// cTransportParameters,
|
||||
// func(p *TransportParameters) { sTransportParametersRcvd = p },
|
||||
// clientConf,
|
||||
// protocol.VersionTLS,
|
||||
// []protocol.VersionNumber{protocol.VersionTLS},
|
||||
// protocol.VersionTLS,
|
||||
// utils.DefaultLogger.WithPrefix("client"),
|
||||
// protocol.PerspectiveClient,
|
||||
// )
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
sChunkChan, sInitialStream, sHandshakeStream := initStreams()
|
||||
sTransportParameters := &TransportParameters{
|
||||
IdleTimeout: 0x1337 * time.Second,
|
||||
StatelessResetToken: bytes.Repeat([]byte{42}, 16),
|
||||
}
|
||||
server, err := NewCryptoSetupServer(
|
||||
sInitialStream,
|
||||
sHandshakeStream,
|
||||
protocol.ConnectionID{},
|
||||
sTransportParameters,
|
||||
func(p *TransportParameters) { cTransportParametersRcvd = p },
|
||||
testdata.GetTLSConfig(),
|
||||
[]protocol.VersionNumber{protocol.VersionTLS},
|
||||
protocol.VersionTLS,
|
||||
utils.DefaultLogger.WithPrefix("server"),
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// sChunkChan, sInitialStream, sHandshakeStream := initStreams()
|
||||
// sTransportParameters := &TransportParameters{
|
||||
// IdleTimeout: 0x1337 * time.Second,
|
||||
// StatelessResetToken: bytes.Repeat([]byte{42}, 16),
|
||||
// }
|
||||
// server, err := NewCryptoSetupServer(
|
||||
// sInitialStream,
|
||||
// sHandshakeStream,
|
||||
// protocol.ConnectionID{},
|
||||
// sTransportParameters,
|
||||
// func(p *TransportParameters) { cTransportParametersRcvd = p },
|
||||
// testdata.GetTLSConfig(),
|
||||
// []protocol.VersionNumber{protocol.VersionTLS},
|
||||
// protocol.VersionTLS,
|
||||
// utils.DefaultLogger.WithPrefix("server"),
|
||||
// protocol.PerspectiveServer,
|
||||
// )
|
||||
// Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
clientErr, serverErr := handshake(client, cChunkChan, server, sChunkChan)
|
||||
Expect(clientErr).ToNot(HaveOccurred())
|
||||
Expect(serverErr).ToNot(HaveOccurred())
|
||||
close(done)
|
||||
}()
|
||||
Eventually(done).Should(BeClosed())
|
||||
Expect(cTransportParametersRcvd).ToNot(BeNil())
|
||||
Expect(cTransportParametersRcvd.IdleTimeout).To(Equal(cTransportParameters.IdleTimeout))
|
||||
Expect(sTransportParametersRcvd).ToNot(BeNil())
|
||||
Expect(sTransportParametersRcvd.IdleTimeout).To(Equal(sTransportParameters.IdleTimeout))
|
||||
})
|
||||
// done := make(chan struct{})
|
||||
// go func() {
|
||||
// defer GinkgoRecover()
|
||||
// clientErr, serverErr := handshake(client, cChunkChan, server, sChunkChan)
|
||||
// Expect(clientErr).ToNot(HaveOccurred())
|
||||
// Expect(serverErr).ToNot(HaveOccurred())
|
||||
// close(done)
|
||||
// }()
|
||||
// Eventually(done).Should(BeClosed())
|
||||
// Expect(cTransportParametersRcvd).ToNot(BeNil())
|
||||
// Expect(cTransportParametersRcvd.IdleTimeout).To(Equal(cTransportParameters.IdleTimeout))
|
||||
// Expect(sTransportParametersRcvd).ToNot(BeNil())
|
||||
// Expect(sTransportParametersRcvd.IdleTimeout).To(Equal(sTransportParameters.IdleTimeout))
|
||||
// })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -25,7 +25,7 @@ type Sealer interface {
|
||||
type tlsExtensionHandler interface {
|
||||
GetExtensions(msgType uint8) []qtls.Extension
|
||||
ReceivedExtensions(msgType uint8, exts []qtls.Extension) error
|
||||
TransportParameters() <-chan TransportParameters
|
||||
TransportParameters() <-chan []byte
|
||||
}
|
||||
|
||||
// CryptoSetup handles the handshake and protecting / unprotecting packets
|
||||
|
||||
@@ -12,12 +12,14 @@ import (
|
||||
|
||||
const quicTLSExtensionType = 0xffa5
|
||||
|
||||
type clientHelloTransportParameters struct {
|
||||
// The ClientHelloTransportParameters are the transport parameters sent in the ClientHello.
|
||||
type ClientHelloTransportParameters struct {
|
||||
InitialVersion protocol.VersionNumber
|
||||
Parameters TransportParameters
|
||||
}
|
||||
|
||||
func (p *clientHelloTransportParameters) Marshal() []byte {
|
||||
// Marshal the transport parameters
|
||||
func (p *ClientHelloTransportParameters) Marshal() []byte {
|
||||
const lenOffset = 4
|
||||
b := &bytes.Buffer{}
|
||||
utils.BigEndian.WriteUint32(b, uint32(p.InitialVersion))
|
||||
@@ -28,7 +30,8 @@ func (p *clientHelloTransportParameters) Marshal() []byte {
|
||||
return data
|
||||
}
|
||||
|
||||
func (p *clientHelloTransportParameters) Unmarshal(data []byte) error {
|
||||
// Unmarshal the transport parameters
|
||||
func (p *ClientHelloTransportParameters) Unmarshal(data []byte) error {
|
||||
if len(data) < 6 {
|
||||
return errors.New("transport parameter data too short")
|
||||
}
|
||||
@@ -41,13 +44,15 @@ func (p *clientHelloTransportParameters) Unmarshal(data []byte) error {
|
||||
return p.Parameters.unmarshal(data, protocol.PerspectiveClient)
|
||||
}
|
||||
|
||||
type encryptedExtensionsTransportParameters struct {
|
||||
// EncryptedExtensionsTransportParameters are the transport parameters sent in the EncryptedExtensions.
|
||||
type EncryptedExtensionsTransportParameters struct {
|
||||
NegotiatedVersion protocol.VersionNumber
|
||||
SupportedVersions []protocol.VersionNumber
|
||||
Parameters TransportParameters
|
||||
}
|
||||
|
||||
func (p *encryptedExtensionsTransportParameters) Marshal() []byte {
|
||||
// Marshal the transport parameters
|
||||
func (p *EncryptedExtensionsTransportParameters) Marshal() []byte {
|
||||
b := &bytes.Buffer{}
|
||||
utils.BigEndian.WriteUint32(b, uint32(p.NegotiatedVersion))
|
||||
b.WriteByte(uint8(4 * len(p.SupportedVersions)))
|
||||
@@ -62,7 +67,8 @@ func (p *encryptedExtensionsTransportParameters) Marshal() []byte {
|
||||
return data
|
||||
}
|
||||
|
||||
func (p *encryptedExtensionsTransportParameters) Unmarshal(data []byte) error {
|
||||
// Unmarshal the transport parameters
|
||||
func (p *EncryptedExtensionsTransportParameters) Unmarshal(data []byte) error {
|
||||
if len(data) < 5 {
|
||||
return errors.New("transport parameter data too short")
|
||||
}
|
||||
|
||||
57
internal/handshake/tls_extension_handler.go
Normal file
57
internal/handshake/tls_extension_handler.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/marten-seemann/qtls"
|
||||
)
|
||||
|
||||
type extensionHandler struct {
|
||||
ourParams []byte
|
||||
paramsChan chan []byte
|
||||
|
||||
perspective protocol.Perspective
|
||||
}
|
||||
|
||||
var _ tlsExtensionHandler = &extensionHandler{}
|
||||
|
||||
// newExtensionHandler creates a new extension handler
|
||||
func newExtensionHandler(params []byte, pers protocol.Perspective) tlsExtensionHandler {
|
||||
return &extensionHandler{
|
||||
ourParams: params,
|
||||
paramsChan: make(chan []byte),
|
||||
perspective: pers,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *extensionHandler) GetExtensions(msgType uint8) []qtls.Extension {
|
||||
if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeClientHello) ||
|
||||
(h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeEncryptedExtensions) {
|
||||
return nil
|
||||
}
|
||||
return []qtls.Extension{{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: h.ourParams,
|
||||
}}
|
||||
}
|
||||
|
||||
func (h *extensionHandler) ReceivedExtensions(msgType uint8, exts []qtls.Extension) error {
|
||||
if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeEncryptedExtensions) ||
|
||||
(h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeClientHello) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var data []byte
|
||||
for _, ext := range exts {
|
||||
if ext.Type == quicTLSExtensionType {
|
||||
data = ext.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
h.paramsChan <- data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *extensionHandler) TransportParameters() <-chan []byte {
|
||||
return h.paramsChan
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/marten-seemann/qtls"
|
||||
)
|
||||
|
||||
type extensionHandlerClient struct {
|
||||
ourParams *TransportParameters
|
||||
paramsChan chan TransportParameters
|
||||
|
||||
origConnID protocol.ConnectionID
|
||||
initialVersion protocol.VersionNumber
|
||||
supportedVersions []protocol.VersionNumber
|
||||
version protocol.VersionNumber
|
||||
|
||||
logger utils.Logger
|
||||
}
|
||||
|
||||
var _ tlsExtensionHandler = &extensionHandlerClient{}
|
||||
|
||||
// newExtensionHandlerClient creates a new extension handler for the client.
|
||||
func newExtensionHandlerClient(
|
||||
params *TransportParameters,
|
||||
origConnID protocol.ConnectionID,
|
||||
initialVersion protocol.VersionNumber,
|
||||
supportedVersions []protocol.VersionNumber,
|
||||
version protocol.VersionNumber,
|
||||
logger utils.Logger,
|
||||
) tlsExtensionHandler {
|
||||
return &extensionHandlerClient{
|
||||
ourParams: params,
|
||||
paramsChan: make(chan TransportParameters),
|
||||
origConnID: origConnID,
|
||||
initialVersion: initialVersion,
|
||||
supportedVersions: supportedVersions,
|
||||
version: version,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *extensionHandlerClient) GetExtensions(msgType uint8) []qtls.Extension {
|
||||
if messageType(msgType) != typeClientHello {
|
||||
return nil
|
||||
}
|
||||
h.logger.Debugf("Sending Transport Parameters: %s", h.ourParams)
|
||||
return []qtls.Extension{{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&clientHelloTransportParameters{
|
||||
InitialVersion: h.initialVersion,
|
||||
Parameters: *h.ourParams,
|
||||
}).Marshal(),
|
||||
}}
|
||||
}
|
||||
|
||||
func (h *extensionHandlerClient) ReceivedExtensions(msgType uint8, exts []qtls.Extension) error {
|
||||
if messageType(msgType) != typeEncryptedExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
var found bool
|
||||
eetp := &encryptedExtensionsTransportParameters{}
|
||||
for _, ext := range exts {
|
||||
if ext.Type != quicTLSExtensionType {
|
||||
continue
|
||||
}
|
||||
if err := eetp.Unmarshal(ext.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
return errors.New("EncryptedExtensions message didn't contain a QUIC extension")
|
||||
}
|
||||
|
||||
// check that the negotiated_version is the current version
|
||||
if eetp.NegotiatedVersion != h.version {
|
||||
return qerr.Error(qerr.VersionNegotiationMismatch, "current version doesn't match negotiated_version")
|
||||
}
|
||||
// check that the current version is included in the supported versions
|
||||
if !protocol.IsSupportedVersion(eetp.SupportedVersions, h.version) {
|
||||
return qerr.Error(qerr.VersionNegotiationMismatch, "current version not included in the supported versions")
|
||||
}
|
||||
// if version negotiation was performed, check that we would have selected the current version based on the supported versions sent by the server
|
||||
if h.version != h.initialVersion {
|
||||
negotiatedVersion, ok := protocol.ChooseSupportedVersion(h.supportedVersions, eetp.SupportedVersions)
|
||||
if !ok || h.version != negotiatedVersion {
|
||||
return qerr.Error(qerr.VersionNegotiationMismatch, "would have picked a different version")
|
||||
}
|
||||
}
|
||||
|
||||
params := eetp.Parameters
|
||||
// check that the server sent a stateless reset token
|
||||
if len(params.StatelessResetToken) == 0 {
|
||||
return errors.New("server didn't sent stateless_reset_token")
|
||||
}
|
||||
// check the Retry token
|
||||
if !h.origConnID.Equal(params.OriginalConnectionID) {
|
||||
return fmt.Errorf("expected original_connection_id to equal %s, is %s", h.origConnID, params.OriginalConnectionID)
|
||||
}
|
||||
h.logger.Debugf("Received Transport Parameters: %s", ¶ms)
|
||||
h.paramsChan <- params
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *extensionHandlerClient) TransportParameters() <-chan TransportParameters {
|
||||
return h.paramsChan
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/marten-seemann/qtls"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("TLS Extension Handler, for the client", func() {
|
||||
var handler *extensionHandlerClient
|
||||
version := protocol.VersionNumber(0x42)
|
||||
|
||||
BeforeEach(func() {
|
||||
var h tlsExtensionHandler
|
||||
h = newExtensionHandlerClient(
|
||||
&TransportParameters{},
|
||||
nil,
|
||||
version,
|
||||
nil,
|
||||
version,
|
||||
utils.DefaultLogger,
|
||||
)
|
||||
handler = h.(*extensionHandlerClient)
|
||||
})
|
||||
|
||||
Context("sending", func() {
|
||||
It("only adds TransportParameters for the ClientHello", func() {
|
||||
// test 2 other handshake types
|
||||
exts := handler.GetExtensions(uint8(typeCertificateRequest))
|
||||
Expect(exts).To(BeEmpty())
|
||||
exts = handler.GetExtensions(uint8(typeEncryptedExtensions))
|
||||
Expect(exts).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("adds TransportParameters to the ClientHello", func() {
|
||||
handler.initialVersion = 13
|
||||
exts := handler.GetExtensions(uint8(typeClientHello))
|
||||
Expect(exts).To(HaveLen(1))
|
||||
chtp := &clientHelloTransportParameters{}
|
||||
Expect(chtp.Unmarshal(exts[0].Data)).To(Succeed())
|
||||
Expect(chtp.InitialVersion).To(BeEquivalentTo(13))
|
||||
})
|
||||
})
|
||||
|
||||
Context("receiving", func() {
|
||||
var parameters TransportParameters
|
||||
|
||||
getEncryptedExtensions := func(params TransportParameters) qtls.Extension {
|
||||
return qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
Parameters: params,
|
||||
NegotiatedVersion: version,
|
||||
SupportedVersions: []protocol.VersionNumber{handler.version},
|
||||
}).Marshal(),
|
||||
}
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
parameters = TransportParameters{
|
||||
IdleTimeout: 0x1337 * time.Second,
|
||||
StatelessResetToken: bytes.Repeat([]byte{0}, 16),
|
||||
}
|
||||
})
|
||||
|
||||
It("sends the transport parameters on the channel", func() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
ext := getEncryptedExtensions(parameters)
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
close(done)
|
||||
}()
|
||||
var params TransportParameters
|
||||
Consistently(done).ShouldNot(BeClosed())
|
||||
Expect(handler.TransportParameters()).To(Receive(¶ms))
|
||||
Expect(params.IdleTimeout).To(Equal(0x1337 * time.Second))
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("errors if the EncryptedExtensions message doesn't contain TransportParameters", func() {
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), nil)
|
||||
Expect(err).To(MatchError("EncryptedExtensions message didn't contain a QUIC extension"))
|
||||
})
|
||||
|
||||
It("ignores messages without TransportParameters, if they are not required", func() {
|
||||
Expect(handler.ReceivedExtensions(uint8(typeCertificateVerify), nil)).To(Succeed())
|
||||
})
|
||||
|
||||
It("errors when it can't parse the TransportParameters", func() {
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: []byte("invalid extension data"),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(HaveOccurred()) // this will be some kind of decoding error
|
||||
})
|
||||
|
||||
It("errors if the TransportParameters don't contain the stateless reset token", func() {
|
||||
parameters.StatelessResetToken = nil
|
||||
ext := getEncryptedExtensions(parameters)
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("server didn't sent stateless_reset_token"))
|
||||
})
|
||||
|
||||
It("errors if the TransportParameters contain an original_connection_id, although no Retry was performed", func() {
|
||||
parameters.OriginalConnectionID = protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}
|
||||
ext := getEncryptedExtensions(parameters)
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("expected original_connection_id to equal (empty), is 0xdecafbad"))
|
||||
})
|
||||
|
||||
It("errors if the TransportParameters contain a wrong original_connection_id", func() {
|
||||
handler.origConnID = protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}
|
||||
parameters.OriginalConnectionID = protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}
|
||||
ext := getEncryptedExtensions(parameters)
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("expected original_connection_id to equal 0xdeadbeef, is 0xdecafbad"))
|
||||
})
|
||||
|
||||
Context("Version Negotiation", func() {
|
||||
It("accepts a valid version negotiation", func() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Eventually(handler.TransportParameters()).Should(Receive())
|
||||
close(done)
|
||||
}()
|
||||
|
||||
handler.initialVersion = 13
|
||||
handler.version = 37
|
||||
handler.supportedVersions = []protocol.VersionNumber{13, 37, 42}
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
Parameters: parameters,
|
||||
NegotiatedVersion: 37,
|
||||
SupportedVersions: []protocol.VersionNumber{36, 37, 38},
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("errors if the current version doesn't match negotiated_version", func() {
|
||||
handler.initialVersion = 13
|
||||
handler.version = 37
|
||||
handler.supportedVersions = []protocol.VersionNumber{13, 37, 42}
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
Parameters: parameters,
|
||||
NegotiatedVersion: 38,
|
||||
SupportedVersions: []protocol.VersionNumber{36, 37, 38},
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: current version doesn't match negotiated_version"))
|
||||
})
|
||||
|
||||
It("errors if the current version is not contained in the server's supported versions", func() {
|
||||
handler.version = 42
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 42,
|
||||
SupportedVersions: []protocol.VersionNumber{43, 44},
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: current version not included in the supported versions"))
|
||||
})
|
||||
|
||||
It("errors if version negotiation was performed, but would have picked a different version based on the supported version list", func() {
|
||||
handler.version = 42
|
||||
handler.initialVersion = 41
|
||||
handler.supportedVersions = []protocol.VersionNumber{43, 42, 41}
|
||||
serverSupportedVersions := []protocol.VersionNumber{42, 43}
|
||||
// check that version negotiation would have led us to pick version 43
|
||||
ver, ok := protocol.ChooseSupportedVersion(handler.supportedVersions, serverSupportedVersions)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(ver).To(Equal(protocol.VersionNumber(43)))
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 42,
|
||||
SupportedVersions: serverSupportedVersions,
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: would have picked a different version"))
|
||||
})
|
||||
|
||||
It("doesn't error if it would have picked a different version based on the supported version list, if no version negotiation was performed", func() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Eventually(handler.TransportParameters()).Should(Receive())
|
||||
close(done)
|
||||
}()
|
||||
|
||||
handler.version = 42
|
||||
handler.initialVersion = 42 // version == initialVersion means no version negotiation was performed
|
||||
handler.supportedVersions = []protocol.VersionNumber{43, 42, 41}
|
||||
serverSupportedVersions := []protocol.VersionNumber{42, 43}
|
||||
// check that version negotiation would have led us to pick version 43
|
||||
ver, ok := protocol.ChooseSupportedVersion(handler.supportedVersions, serverSupportedVersions)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(ver).To(Equal(protocol.VersionNumber(43)))
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
Parameters: parameters,
|
||||
NegotiatedVersion: 42,
|
||||
SupportedVersions: serverSupportedVersions,
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeEncryptedExtensions), []qtls.Extension{ext})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,87 +0,0 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/qerr"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/marten-seemann/qtls"
|
||||
)
|
||||
|
||||
type extensionHandlerServer struct {
|
||||
ourParams *TransportParameters
|
||||
paramsChan chan TransportParameters
|
||||
|
||||
version protocol.VersionNumber
|
||||
supportedVersions []protocol.VersionNumber
|
||||
|
||||
logger utils.Logger
|
||||
}
|
||||
|
||||
var _ tlsExtensionHandler = &extensionHandlerServer{}
|
||||
|
||||
// newExtensionHandlerServer creates a new extension handler for the server
|
||||
func newExtensionHandlerServer(
|
||||
params *TransportParameters,
|
||||
supportedVersions []protocol.VersionNumber,
|
||||
version protocol.VersionNumber,
|
||||
logger utils.Logger,
|
||||
) tlsExtensionHandler {
|
||||
return &extensionHandlerServer{
|
||||
ourParams: params,
|
||||
paramsChan: make(chan TransportParameters),
|
||||
supportedVersions: supportedVersions,
|
||||
version: version,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *extensionHandlerServer) GetExtensions(msgType uint8) []qtls.Extension {
|
||||
if messageType(msgType) != typeEncryptedExtensions {
|
||||
return nil
|
||||
}
|
||||
h.logger.Debugf("Sending Transport Parameters: %s", h.ourParams)
|
||||
return []qtls.Extension{{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&encryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: h.version,
|
||||
SupportedVersions: protocol.GetGreasedVersions(h.supportedVersions),
|
||||
Parameters: *h.ourParams,
|
||||
}).Marshal(),
|
||||
}}
|
||||
}
|
||||
|
||||
func (h *extensionHandlerServer) ReceivedExtensions(msgType uint8, exts []qtls.Extension) error {
|
||||
if messageType(msgType) != typeClientHello {
|
||||
return nil
|
||||
}
|
||||
var found bool
|
||||
chtp := &clientHelloTransportParameters{}
|
||||
for _, ext := range exts {
|
||||
if ext.Type != quicTLSExtensionType {
|
||||
continue
|
||||
}
|
||||
if err := chtp.Unmarshal(ext.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
found = true
|
||||
}
|
||||
if !found {
|
||||
return errors.New("ClientHello didn't contain a QUIC extension")
|
||||
}
|
||||
|
||||
// perform the stateless version negotiation validation:
|
||||
// make sure that we would have sent a Version Negotiation Packet if the client offered the initial version
|
||||
// this is the case if and only if the initial version is not contained in the supported versions
|
||||
if chtp.InitialVersion != h.version && protocol.IsSupportedVersion(h.supportedVersions, chtp.InitialVersion) {
|
||||
return qerr.Error(qerr.VersionNegotiationMismatch, "Client should have used the initial version")
|
||||
}
|
||||
h.logger.Debugf("Received Transport Parameters: %s", &chtp.Parameters)
|
||||
h.paramsChan <- chtp.Parameters
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *extensionHandlerServer) TransportParameters() <-chan TransportParameters {
|
||||
return h.paramsChan
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/lucas-clemente/quic-go/internal/utils"
|
||||
"github.com/marten-seemann/qtls"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("TLS Extension Handler, for the server", func() {
|
||||
var handler *extensionHandlerServer
|
||||
|
||||
BeforeEach(func() {
|
||||
var h tlsExtensionHandler
|
||||
h = newExtensionHandlerServer(
|
||||
&TransportParameters{},
|
||||
nil,
|
||||
protocol.VersionWhatever,
|
||||
utils.DefaultLogger,
|
||||
)
|
||||
handler = h.(*extensionHandlerServer)
|
||||
})
|
||||
|
||||
Context("sending", func() {
|
||||
It("only adds TransportParameters for the Encrypted Extensions", func() {
|
||||
// test 2 other handshake types
|
||||
exts := handler.GetExtensions(uint8(typeCertificate))
|
||||
Expect(exts).To(BeEmpty())
|
||||
exts = handler.GetExtensions(uint8(typeFinished))
|
||||
Expect(exts).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("adds TransportParameters to the EncryptedExtensions message", func() {
|
||||
handler.version = 666
|
||||
versions := []protocol.VersionNumber{13, 37, 42}
|
||||
handler.supportedVersions = versions
|
||||
exts := handler.GetExtensions(uint8(typeEncryptedExtensions))
|
||||
Expect(exts).To(HaveLen(1))
|
||||
eetp := &encryptedExtensionsTransportParameters{}
|
||||
Expect(eetp.Unmarshal(exts[0].Data)).To(Succeed())
|
||||
Expect(eetp.NegotiatedVersion).To(BeEquivalentTo(666))
|
||||
// the SupportedVersions will contain one reserved version number
|
||||
Expect(eetp.SupportedVersions).To(HaveLen(len(versions) + 1))
|
||||
for _, version := range versions {
|
||||
Expect(eetp.SupportedVersions).To(ContainElement(version))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("receiving", func() {
|
||||
var parameters TransportParameters
|
||||
|
||||
getClientHello := func(params TransportParameters) qtls.Extension {
|
||||
return qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&clientHelloTransportParameters{Parameters: params}).Marshal(),
|
||||
}
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
parameters = TransportParameters{IdleTimeout: 0x1337 * time.Second}
|
||||
})
|
||||
|
||||
It("sends the transport parameters on the channel", func() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
ext := getClientHello(parameters)
|
||||
err := handler.ReceivedExtensions(uint8(typeClientHello), []qtls.Extension{ext})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
close(done)
|
||||
}()
|
||||
var params TransportParameters
|
||||
Consistently(done).ShouldNot(BeClosed())
|
||||
Expect(handler.TransportParameters()).To(Receive(¶ms))
|
||||
Expect(params.IdleTimeout).To(Equal(0x1337 * time.Second))
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("errors if the ClientHello doesn't contain TransportParameters", func() {
|
||||
err := handler.ReceivedExtensions(uint8(typeClientHello), nil)
|
||||
Expect(err).To(MatchError("ClientHello didn't contain a QUIC extension"))
|
||||
})
|
||||
|
||||
It("errors if it can't unmarshal the TransportParameters", func() {
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: []byte("invalid extension data"),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeClientHello), []qtls.Extension{ext})
|
||||
Expect(err).To(HaveOccurred()) // this will be some kind of decoding error
|
||||
})
|
||||
|
||||
Context("Version Negotiation", func() {
|
||||
It("accepts a ClientHello, when no version negotiation was performed", func() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
<-handler.TransportParameters()
|
||||
close(done)
|
||||
}()
|
||||
handler.version = 42
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&clientHelloTransportParameters{
|
||||
InitialVersion: 42,
|
||||
Parameters: parameters,
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeClientHello), []qtls.Extension{ext})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("accepts a valid version negotiation", func() {
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
<-handler.TransportParameters()
|
||||
close(done)
|
||||
}()
|
||||
handler.version = 42
|
||||
handler.supportedVersions = []protocol.VersionNumber{13, 37, 42}
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&clientHelloTransportParameters{
|
||||
InitialVersion: 22, // this must be an unsupported version
|
||||
Parameters: parameters,
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeClientHello), []qtls.Extension{ext})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("erros when a version negotiation was performed, although we already support the initial version", func() {
|
||||
handler.supportedVersions = []protocol.VersionNumber{11, 12, 13}
|
||||
handler.version = 13
|
||||
ext := qtls.Extension{
|
||||
Type: quicTLSExtensionType,
|
||||
Data: (&clientHelloTransportParameters{
|
||||
InitialVersion: 11, // this is an supported version
|
||||
}).Marshal(),
|
||||
}
|
||||
err := handler.ReceivedExtensions(uint8(typeClientHello), []qtls.Extension{ext})
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: Client should have used the initial version"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
165
internal/handshake/tls_extension_handler_test.go
Normal file
165
internal/handshake/tls_extension_handler_test.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package handshake
|
||||
|
||||
import (
|
||||
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||||
"github.com/marten-seemann/qtls"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("TLS Extension Handler, for the server", func() {
|
||||
var (
|
||||
handlerServer tlsExtensionHandler
|
||||
handlerClient tlsExtensionHandler
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
handlerServer = newExtensionHandler(
|
||||
[]byte("foobar"),
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
handlerClient = newExtensionHandler(
|
||||
[]byte("raboof"),
|
||||
protocol.PerspectiveClient,
|
||||
)
|
||||
})
|
||||
|
||||
Context("for the server", func() {
|
||||
Context("sending", func() {
|
||||
It("only adds TransportParameters for the Encrypted Extensions", func() {
|
||||
// test 2 other handshake types
|
||||
Expect(handlerServer.GetExtensions(uint8(typeCertificate))).To(BeEmpty())
|
||||
Expect(handlerServer.GetExtensions(uint8(typeFinished))).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("adds TransportParameters to the EncryptedExtensions message", func() {
|
||||
exts := handlerServer.GetExtensions(uint8(typeEncryptedExtensions))
|
||||
Expect(exts).To(HaveLen(1))
|
||||
Expect(exts[0].Type).To(BeEquivalentTo(quicTLSExtensionType))
|
||||
Expect(exts[0].Data).To(Equal([]byte("foobar")))
|
||||
})
|
||||
})
|
||||
|
||||
Context("receiving", func() {
|
||||
var chExts []qtls.Extension
|
||||
|
||||
BeforeEach(func() {
|
||||
chExts = handlerClient.GetExtensions(uint8(typeClientHello))
|
||||
Expect(chExts).To(HaveLen(1))
|
||||
})
|
||||
|
||||
It("sends the extension on the channel", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(handlerServer.ReceivedExtensions(uint8(typeClientHello), chExts)).To(Succeed())
|
||||
}()
|
||||
|
||||
var data []byte
|
||||
Eventually(handlerServer.TransportParameters()).Should(Receive(&data))
|
||||
Expect(data).To(Equal([]byte("raboof")))
|
||||
})
|
||||
|
||||
It("sends nil on the channel if the extension is missing", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(handlerServer.ReceivedExtensions(uint8(typeClientHello), nil)).To(Succeed())
|
||||
}()
|
||||
|
||||
var data []byte
|
||||
Eventually(handlerServer.TransportParameters()).Should(Receive(&data))
|
||||
Expect(data).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("ignores extensions with different code points", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
exts := []qtls.Extension{{Type: 0x1337, Data: []byte("invalid")}}
|
||||
Expect(handlerServer.ReceivedExtensions(uint8(typeClientHello), exts)).To(Succeed())
|
||||
}()
|
||||
|
||||
var data []byte
|
||||
Eventually(handlerServer.TransportParameters()).Should(Receive())
|
||||
Expect(data).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("ignores extensions that are not sent with the ClientHello", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(handlerServer.ReceivedExtensions(uint8(typeFinished), chExts)).To(Succeed())
|
||||
}()
|
||||
|
||||
Consistently(handlerServer.TransportParameters()).ShouldNot(Receive())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("for the client", func() {
|
||||
Context("sending", func() {
|
||||
It("only adds TransportParameters for the Encrypted Extensions", func() {
|
||||
// test 2 other handshake types
|
||||
Expect(handlerClient.GetExtensions(uint8(typeCertificate))).To(BeEmpty())
|
||||
Expect(handlerClient.GetExtensions(uint8(typeFinished))).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("adds TransportParameters to the ClientHello message", func() {
|
||||
exts := handlerClient.GetExtensions(uint8(typeClientHello))
|
||||
Expect(exts).To(HaveLen(1))
|
||||
Expect(exts[0].Type).To(BeEquivalentTo(quicTLSExtensionType))
|
||||
Expect(exts[0].Data).To(Equal([]byte("raboof")))
|
||||
})
|
||||
})
|
||||
|
||||
Context("receiving", func() {
|
||||
var chExts []qtls.Extension
|
||||
|
||||
BeforeEach(func() {
|
||||
chExts = handlerServer.GetExtensions(uint8(typeEncryptedExtensions))
|
||||
Expect(chExts).To(HaveLen(1))
|
||||
})
|
||||
|
||||
It("sends the extension on the channel", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(handlerClient.ReceivedExtensions(uint8(typeEncryptedExtensions), chExts)).To(Succeed())
|
||||
}()
|
||||
|
||||
var data []byte
|
||||
Eventually(handlerClient.TransportParameters()).Should(Receive(&data))
|
||||
Expect(data).To(Equal([]byte("foobar")))
|
||||
})
|
||||
|
||||
It("sends nil on the channel if the extension is missing", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(handlerClient.ReceivedExtensions(uint8(typeEncryptedExtensions), nil)).To(Succeed())
|
||||
}()
|
||||
|
||||
var data []byte
|
||||
Eventually(handlerClient.TransportParameters()).Should(Receive(&data))
|
||||
Expect(data).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("ignores extensions with different code points", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
exts := []qtls.Extension{{Type: 0x1337, Data: []byte("invalid")}}
|
||||
Expect(handlerClient.ReceivedExtensions(uint8(typeEncryptedExtensions), exts)).To(Succeed())
|
||||
}()
|
||||
|
||||
var data []byte
|
||||
Eventually(handlerClient.TransportParameters()).Should(Receive())
|
||||
Expect(data).To(BeEmpty())
|
||||
})
|
||||
|
||||
It("ignores extensions that are not sent with the EncryptedExtensions", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
Expect(handlerClient.ReceivedExtensions(uint8(typeFinished), chExts)).To(Succeed())
|
||||
}()
|
||||
|
||||
Consistently(handlerClient.TransportParameters()).ShouldNot(Receive())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -13,14 +13,14 @@ import (
|
||||
var _ = Describe("QUIC TLS Extension", func() {
|
||||
Context("Client Hello Transport Parameters", func() {
|
||||
It("marshals and unmarshals", func() {
|
||||
chtp := &clientHelloTransportParameters{
|
||||
chtp := &ClientHelloTransportParameters{
|
||||
InitialVersion: 0x123456,
|
||||
Parameters: TransportParameters{
|
||||
InitialMaxStreamDataUni: 0x42,
|
||||
IdleTimeout: 0x1337 * time.Second,
|
||||
},
|
||||
}
|
||||
chtp2 := &clientHelloTransportParameters{}
|
||||
chtp2 := &ClientHelloTransportParameters{}
|
||||
Expect(chtp2.Unmarshal(chtp.Marshal())).To(Succeed())
|
||||
Expect(chtp2.InitialVersion).To(Equal(chtp.InitialVersion))
|
||||
Expect(chtp2.Parameters.InitialMaxStreamDataUni).To(Equal(chtp.Parameters.InitialMaxStreamDataUni))
|
||||
@@ -32,7 +32,7 @@ var _ = Describe("QUIC TLS Extension", func() {
|
||||
b := make([]byte, 100)
|
||||
for i := 0; i < 1000; i++ {
|
||||
rand.Read(b)
|
||||
chtp := &clientHelloTransportParameters{}
|
||||
chtp := &ClientHelloTransportParameters{}
|
||||
chtp.Unmarshal(b[:int(rand.Int31n(100))])
|
||||
}
|
||||
})
|
||||
@@ -40,7 +40,7 @@ var _ = Describe("QUIC TLS Extension", func() {
|
||||
|
||||
Context("Encrypted Extensions Transport Parameters", func() {
|
||||
It("marshals and unmarshals", func() {
|
||||
eetp := &encryptedExtensionsTransportParameters{
|
||||
eetp := &EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 0x123456,
|
||||
SupportedVersions: []protocol.VersionNumber{0x42, 0x4242},
|
||||
Parameters: TransportParameters{
|
||||
@@ -48,7 +48,7 @@ var _ = Describe("QUIC TLS Extension", func() {
|
||||
IdleTimeout: 0x1337 * time.Second,
|
||||
},
|
||||
}
|
||||
eetp2 := &encryptedExtensionsTransportParameters{}
|
||||
eetp2 := &EncryptedExtensionsTransportParameters{}
|
||||
Expect(eetp2.Unmarshal(eetp.Marshal())).To(Succeed())
|
||||
Expect(eetp2.NegotiatedVersion).To(Equal(eetp.NegotiatedVersion))
|
||||
Expect(eetp2.SupportedVersions).To(Equal(eetp.SupportedVersions))
|
||||
@@ -61,7 +61,7 @@ var _ = Describe("QUIC TLS Extension", func() {
|
||||
b := make([]byte, 100)
|
||||
for i := 0; i < 1000; i++ {
|
||||
rand.Read(b)
|
||||
chtp := &encryptedExtensionsTransportParameters{}
|
||||
chtp := &EncryptedExtensionsTransportParameters{}
|
||||
chtp.Unmarshal(b[:int(rand.Int31n(100))])
|
||||
}
|
||||
})
|
||||
|
||||
100
session.go
100
session.go
@@ -73,12 +73,14 @@ var errCloseForRecreating = errors.New("closing session in order to recreate it"
|
||||
type session struct {
|
||||
sessionRunner sessionRunner
|
||||
|
||||
destConnID protocol.ConnectionID
|
||||
srcConnID protocol.ConnectionID
|
||||
destConnID protocol.ConnectionID
|
||||
origDestConnID protocol.ConnectionID // if the server sends a Retry, this is the connection ID we used initially
|
||||
srcConnID protocol.ConnectionID
|
||||
|
||||
perspective protocol.Perspective
|
||||
version protocol.VersionNumber
|
||||
config *Config
|
||||
perspective protocol.Perspective
|
||||
initialVersion protocol.VersionNumber // if version negotiation is performed, this is the version we initially tried
|
||||
version protocol.VersionNumber
|
||||
config *Config
|
||||
|
||||
conn connection
|
||||
|
||||
@@ -175,17 +177,19 @@ var newSession = func(
|
||||
s.version,
|
||||
)
|
||||
s.framer = newFramer(s.streamsMap, s.version)
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: s.version,
|
||||
SupportedVersions: protocol.GetGreasedVersions(conf.Versions),
|
||||
Parameters: *params,
|
||||
}
|
||||
cs, err := handshake.NewCryptoSetupServer(
|
||||
initialStream,
|
||||
handshakeStream,
|
||||
clientDestConnID,
|
||||
params,
|
||||
eetp,
|
||||
s.processTransportParameters,
|
||||
tlsConf,
|
||||
conf.Versions,
|
||||
v,
|
||||
logger,
|
||||
protocol.PerspectiveServer,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -236,28 +240,29 @@ var newClientSession = func(
|
||||
config: conf,
|
||||
srcConnID: srcConnID,
|
||||
destConnID: destConnID,
|
||||
origDestConnID: origDestConnID,
|
||||
perspective: protocol.PerspectiveClient,
|
||||
handshakeCompleteChan: make(chan struct{}),
|
||||
logger: logger,
|
||||
initialVersion: initialVersion,
|
||||
version: v,
|
||||
}
|
||||
s.preSetup()
|
||||
s.sentPacketHandler = ackhandler.NewSentPacketHandler(initialPacketNumber, s.rttStats, s.logger)
|
||||
initialStream := newCryptoStream()
|
||||
handshakeStream := newCryptoStream()
|
||||
chtp := &handshake.ClientHelloTransportParameters{
|
||||
InitialVersion: initialVersion,
|
||||
Parameters: *params,
|
||||
}
|
||||
cs, clientHelloWritten, err := handshake.NewCryptoSetupClient(
|
||||
initialStream,
|
||||
handshakeStream,
|
||||
origDestConnID,
|
||||
s.destConnID,
|
||||
params,
|
||||
chtp,
|
||||
s.processTransportParameters,
|
||||
tlsConf,
|
||||
initialVersion,
|
||||
conf.Versions,
|
||||
v,
|
||||
logger,
|
||||
protocol.PerspectiveClient,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -810,14 +815,73 @@ func (s *session) handleCloseError(closeErr closeError) error {
|
||||
return s.sendConnectionClose(quicErr)
|
||||
}
|
||||
|
||||
func (s *session) processTransportParameters(params *handshake.TransportParameters) {
|
||||
func (s *session) processTransportParameters(data []byte) {
|
||||
var params *handshake.TransportParameters
|
||||
var err error
|
||||
switch s.perspective {
|
||||
case protocol.PerspectiveClient:
|
||||
params, err = s.processTransportParametersForClient(data)
|
||||
case protocol.PerspectiveServer:
|
||||
params, err = s.processTransportParametersForServer(data)
|
||||
}
|
||||
if err != nil {
|
||||
s.closeLocal(err)
|
||||
return
|
||||
}
|
||||
s.logger.Debugf("Received Transport Parameters: %s", params)
|
||||
s.peerParams = params
|
||||
s.streamsMap.UpdateLimits(params)
|
||||
s.packer.HandleTransportParameters(params)
|
||||
s.frameParser.SetAckDelayExponent(params.AckDelayExponent)
|
||||
s.connFlowController.UpdateSendWindow(params.InitialMaxData)
|
||||
// the crypto stream is the only open stream at this moment
|
||||
// so we don't need to update stream flow control windows
|
||||
}
|
||||
|
||||
func (s *session) processTransportParametersForClient(data []byte) (*handshake.TransportParameters, error) {
|
||||
eetp := handshake.EncryptedExtensionsTransportParameters{}
|
||||
if err := eetp.Unmarshal(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check that the negotiated_version is the current version
|
||||
if eetp.NegotiatedVersion != s.version {
|
||||
return nil, qerr.Error(qerr.VersionNegotiationMismatch, "current version doesn't match negotiated_version")
|
||||
}
|
||||
// check that the current version is included in the supported versions
|
||||
if !protocol.IsSupportedVersion(eetp.SupportedVersions, s.version) {
|
||||
return nil, qerr.Error(qerr.VersionNegotiationMismatch, "current version not included in the supported versions")
|
||||
}
|
||||
// if version negotiation was performed, check that we would have selected the current version based on the supported versions sent by the server
|
||||
if s.version != s.initialVersion {
|
||||
negotiatedVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, eetp.SupportedVersions)
|
||||
if !ok || s.version != negotiatedVersion {
|
||||
return nil, qerr.Error(qerr.VersionNegotiationMismatch, "would have picked a different version")
|
||||
}
|
||||
}
|
||||
|
||||
params := &eetp.Parameters
|
||||
// check that the server sent a stateless reset token
|
||||
if len(params.StatelessResetToken) == 0 {
|
||||
return nil, errors.New("server didn't send stateless_reset_token")
|
||||
}
|
||||
// check the Retry token
|
||||
if !params.OriginalConnectionID.Equal(s.origDestConnID) {
|
||||
return nil, fmt.Errorf("expected original_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalConnectionID)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (s *session) processTransportParametersForServer(data []byte) (*handshake.TransportParameters, error) {
|
||||
chtp := handshake.ClientHelloTransportParameters{}
|
||||
if err := chtp.Unmarshal(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// perform the stateless version negotiation validation:
|
||||
// make sure that we would have sent a Version Negotiation Packet if the client offered the initial version
|
||||
// this is the case if and only if the initial version is not contained in the supported versions
|
||||
if chtp.InitialVersion != s.version && protocol.IsSupportedVersion(s.config.Versions, chtp.InitialVersion) {
|
||||
return nil, qerr.Error(qerr.VersionNegotiationMismatch, "Client should have used the initial version")
|
||||
}
|
||||
return &chtp.Parameters, nil
|
||||
}
|
||||
|
||||
func (s *session) sendPackets() error {
|
||||
|
||||
231
session_test.go
231
session_test.go
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
@@ -91,7 +92,7 @@ var _ = Describe("Session", func() {
|
||||
protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
populateServerConfig(&Config{}),
|
||||
nil, // tls.Config
|
||||
nil, // handshake.TransportParameters,
|
||||
&handshake.TransportParameters{},
|
||||
utils.DefaultLogger,
|
||||
protocol.VersionTLS,
|
||||
)
|
||||
@@ -1085,28 +1086,71 @@ var _ = Describe("Session", func() {
|
||||
Eventually(done).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("process transport parameters received from the peer", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
cryptoSetup.EXPECT().RunHandshake().Do(func() { <-sess.Context().Done() })
|
||||
sess.run()
|
||||
}()
|
||||
params := &handshake.TransportParameters{
|
||||
IdleTimeout: 90 * time.Second,
|
||||
InitialMaxStreamDataBidiLocal: 0x5000,
|
||||
InitialMaxData: 0x5000,
|
||||
MaxPacketSize: 0x42,
|
||||
}
|
||||
streamManager.EXPECT().UpdateLimits(params)
|
||||
packer.EXPECT().HandleTransportParameters(params)
|
||||
sess.processTransportParameters(params)
|
||||
// make the go routine return
|
||||
streamManager.EXPECT().CloseWithError(gomock.Any())
|
||||
sessionRunner.EXPECT().retireConnectionID(gomock.Any())
|
||||
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{}, nil)
|
||||
cryptoSetup.EXPECT().Close()
|
||||
sess.Close()
|
||||
Eventually(sess.Context().Done()).Should(BeClosed())
|
||||
Context("transport parameters", func() {
|
||||
It("errors if it can't unmarshal the TransportParameters", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
cryptoSetup.EXPECT().RunHandshake().Do(func() { <-sess.Context().Done() })
|
||||
err := sess.run()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("transport parameter"))
|
||||
}()
|
||||
streamManager.EXPECT().CloseWithError(gomock.Any())
|
||||
sessionRunner.EXPECT().retireConnectionID(gomock.Any())
|
||||
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{}, nil)
|
||||
cryptoSetup.EXPECT().Close()
|
||||
sess.processTransportParameters([]byte("invalid"))
|
||||
Eventually(sess.Context().Done()).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("process transport parameters received from the client", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
cryptoSetup.EXPECT().RunHandshake().Do(func() { <-sess.Context().Done() })
|
||||
sess.run()
|
||||
}()
|
||||
params := &handshake.TransportParameters{
|
||||
IdleTimeout: 90 * time.Second,
|
||||
InitialMaxStreamDataBidiLocal: 0x5000,
|
||||
InitialMaxData: 0x5000,
|
||||
// marshaling always sets it to this value
|
||||
MaxPacketSize: protocol.MaxReceivePacketSize,
|
||||
}
|
||||
chtp := &handshake.ClientHelloTransportParameters{
|
||||
InitialVersion: sess.version,
|
||||
Parameters: *params,
|
||||
}
|
||||
streamManager.EXPECT().UpdateLimits(params)
|
||||
packer.EXPECT().HandleTransportParameters(params)
|
||||
sess.processTransportParameters(chtp.Marshal())
|
||||
// make the go routine return
|
||||
streamManager.EXPECT().CloseWithError(gomock.Any())
|
||||
sessionRunner.EXPECT().retireConnectionID(gomock.Any())
|
||||
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{}, nil)
|
||||
cryptoSetup.EXPECT().Close()
|
||||
sess.Close()
|
||||
Eventually(sess.Context().Done()).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("accepts a valid version negotiation", func() {
|
||||
sess.version = 42
|
||||
sess.config.Versions = []protocol.VersionNumber{13, 37, 42}
|
||||
chtp := &handshake.ClientHelloTransportParameters{
|
||||
InitialVersion: 22, // this must be an unsupported version
|
||||
}
|
||||
_, err := sess.processTransportParametersForServer(chtp.Marshal())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("erros when a version negotiation was performed, although we already support the initial version", func() {
|
||||
sess.version = 42
|
||||
sess.config.Versions = []protocol.VersionNumber{13, 37, 42}
|
||||
chtp := &handshake.ClientHelloTransportParameters{
|
||||
InitialVersion: 13, // this must be a supported version
|
||||
}
|
||||
_, err := sess.processTransportParametersForServer(chtp.Marshal())
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: Client should have used the initial version"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("keep-alives", func() {
|
||||
@@ -1394,7 +1438,7 @@ var _ = Describe("Client Session", func() {
|
||||
populateClientConfig(&Config{}, true),
|
||||
nil, // tls.Config
|
||||
42, // initial packet number
|
||||
nil, // transport parameters
|
||||
&handshake.TransportParameters{},
|
||||
protocol.VersionWhatever,
|
||||
utils.DefaultLogger,
|
||||
protocol.VersionWhatever,
|
||||
@@ -1441,4 +1485,143 @@ var _ = Describe("Client Session", func() {
|
||||
Expect(sess.Close()).To(Succeed())
|
||||
Eventually(sess.Context().Done()).Should(BeClosed())
|
||||
})
|
||||
|
||||
Context("transport parameters", func() {
|
||||
It("errors if it can't unmarshal the TransportParameters", func() {
|
||||
go func() {
|
||||
defer GinkgoRecover()
|
||||
cryptoSetup.EXPECT().RunHandshake().Do(func() { <-sess.Context().Done() })
|
||||
err := sess.run()
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("transport parameter"))
|
||||
}()
|
||||
// streamManager.EXPECT().CloseWithError(gomock.Any())
|
||||
sessionRunner.EXPECT().retireConnectionID(gomock.Any())
|
||||
packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&packedPacket{}, nil)
|
||||
cryptoSetup.EXPECT().Close()
|
||||
sess.processTransportParameters([]byte("invalid"))
|
||||
Eventually(sess.Context().Done()).Should(BeClosed())
|
||||
})
|
||||
|
||||
It("errors if the TransportParameters don't contain the stateless reset token", func() {
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: sess.version,
|
||||
SupportedVersions: []protocol.VersionNumber{sess.version},
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).To(MatchError("server didn't send stateless_reset_token"))
|
||||
})
|
||||
|
||||
It("errors if the TransportParameters contain an original_connection_id, although no Retry was performed", func() {
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: sess.version,
|
||||
SupportedVersions: []protocol.VersionNumber{sess.version},
|
||||
Parameters: handshake.TransportParameters{
|
||||
OriginalConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad},
|
||||
StatelessResetToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
},
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).To(MatchError(fmt.Sprintf("expected original_connection_id to equal %s, is 0xdecafbad", sess.destConnID)))
|
||||
})
|
||||
|
||||
It("errors if the TransportParameters contain an original_connection_id, although no Retry was performed", func() {
|
||||
sess.origDestConnID = protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: sess.version,
|
||||
SupportedVersions: []protocol.VersionNumber{sess.version},
|
||||
Parameters: handshake.TransportParameters{
|
||||
OriginalConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad},
|
||||
StatelessResetToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
},
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).To(MatchError("expected original_connection_id to equal 0xdeadbeef, is 0xdecafbad"))
|
||||
})
|
||||
|
||||
Context("Version Negotiation", func() {
|
||||
var params handshake.TransportParameters
|
||||
|
||||
BeforeEach(func() {
|
||||
params = handshake.TransportParameters{
|
||||
StatelessResetToken: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
OriginalConnectionID: sess.origDestConnID,
|
||||
}
|
||||
})
|
||||
|
||||
It("accepts a valid version negotiation", func() {
|
||||
sess.initialVersion = 13
|
||||
sess.version = 37
|
||||
sess.config.Versions = []protocol.VersionNumber{13, 37, 42}
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 37,
|
||||
SupportedVersions: []protocol.VersionNumber{36, 37, 38},
|
||||
Parameters: params,
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("errors if the current version doesn't match negotiated_version", func() {
|
||||
sess.initialVersion = 13
|
||||
sess.version = 37
|
||||
sess.config.Versions = []protocol.VersionNumber{13, 37, 42}
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 38,
|
||||
SupportedVersions: []protocol.VersionNumber{36, 37, 38},
|
||||
Parameters: params,
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: current version doesn't match negotiated_version"))
|
||||
})
|
||||
|
||||
It("errors if the current version is not contained in the server's supported versions", func() {
|
||||
sess.version = 42
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 42,
|
||||
SupportedVersions: []protocol.VersionNumber{43, 44},
|
||||
Parameters: params,
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: current version not included in the supported versions"))
|
||||
})
|
||||
|
||||
It("errors if version negotiation was performed, but would have picked a different version based on the supported version list", func() {
|
||||
sess.version = 42
|
||||
sess.initialVersion = 41
|
||||
sess.config.Versions = []protocol.VersionNumber{43, 42, 41}
|
||||
serverSupportedVersions := []protocol.VersionNumber{42, 43}
|
||||
// check that version negotiation would have led us to pick version 43
|
||||
ver, ok := protocol.ChooseSupportedVersion(sess.config.Versions, serverSupportedVersions)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(ver).To(Equal(protocol.VersionNumber(43)))
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 42,
|
||||
SupportedVersions: serverSupportedVersions,
|
||||
Parameters: params,
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).To(MatchError("VersionNegotiationMismatch: would have picked a different version"))
|
||||
})
|
||||
|
||||
It("doesn't error if it would have picked a different version based on the supported version list, if no version negotiation was performed", func() {
|
||||
sess.version = 42
|
||||
sess.initialVersion = 42 // version == initialVersion means no version negotiation was performed
|
||||
sess.config.Versions = []protocol.VersionNumber{43, 42, 41}
|
||||
serverSupportedVersions := []protocol.VersionNumber{42, 43}
|
||||
// check that version negotiation would have led us to pick version 43
|
||||
ver, ok := protocol.ChooseSupportedVersion(sess.config.Versions, serverSupportedVersions)
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(ver).To(Equal(protocol.VersionNumber(43)))
|
||||
eetp := &handshake.EncryptedExtensionsTransportParameters{
|
||||
NegotiatedVersion: 42,
|
||||
SupportedVersions: serverSupportedVersions,
|
||||
Parameters: params,
|
||||
}
|
||||
_, err := sess.processTransportParametersForClient(eetp.Marshal())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user