mirror of
https://github.com/refraction-networking/utls.git
synced 2025-03-31 10:37:36 +03:00
Go 1.23 adds two new events to QUICConns: QUICStoreSessionEvent and QUICResumeSessionEvent. We added a QUICConfig.EnableStoreSessionEvent flag to control whether the store-session event is provided or not, because receiving this event requires additional action from the caller: the session must be explicitly stored with QUICConn.StoreSession. We did not add a control for whether the resume-session event is provided, because this event requires no action and the caller is expected to ignore unknown events. However, we never documented the expectation that callers ignore unknown events, and quic-go produces an error when receiving an unexpected event. So change the EnableStoreSessionEvent flag to apply to both new events. Fixes #68124 For #63691 Change-Id: I84af487e52b3815f7b648e09884608f8915cd645 Reviewed-on: https://go-review.googlesource.com/c/go/+/594475 Reviewed-by: Marten Seemann <martenseemann@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Roland Shoemaker <roland@golang.org>
626 lines
21 KiB
Go
626 lines
21 KiB
Go
// Copyright 2023 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package tls
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
type testQUICConn struct {
|
|
t *testing.T
|
|
conn *QUICConn
|
|
readSecret map[QUICEncryptionLevel]suiteSecret
|
|
writeSecret map[QUICEncryptionLevel]suiteSecret
|
|
ticketOpts QUICSessionTicketOptions
|
|
onResumeSession func(*SessionState)
|
|
gotParams []byte
|
|
earlyDataRejected bool
|
|
complete bool
|
|
}
|
|
|
|
func newTestQUICClient(t *testing.T, config *QUICConfig) *testQUICConn {
|
|
q := &testQUICConn{
|
|
t: t,
|
|
conn: QUICClient(config),
|
|
}
|
|
t.Cleanup(func() {
|
|
q.conn.Close()
|
|
})
|
|
return q
|
|
}
|
|
|
|
func newTestQUICServer(t *testing.T, config *QUICConfig) *testQUICConn {
|
|
q := &testQUICConn{
|
|
t: t,
|
|
conn: QUICServer(config),
|
|
}
|
|
t.Cleanup(func() {
|
|
q.conn.Close()
|
|
})
|
|
return q
|
|
}
|
|
|
|
type suiteSecret struct {
|
|
suite uint16
|
|
secret []byte
|
|
}
|
|
|
|
func (q *testQUICConn) setReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
|
|
if _, ok := q.writeSecret[level]; !ok && level != QUICEncryptionLevelEarly {
|
|
q.t.Errorf("SetReadSecret for level %v called before SetWriteSecret", level)
|
|
}
|
|
if level == QUICEncryptionLevelApplication && !q.complete {
|
|
q.t.Errorf("SetReadSecret for level %v called before HandshakeComplete", level)
|
|
}
|
|
if _, ok := q.readSecret[level]; ok {
|
|
q.t.Errorf("SetReadSecret for level %v called twice", level)
|
|
}
|
|
if q.readSecret == nil {
|
|
q.readSecret = map[QUICEncryptionLevel]suiteSecret{}
|
|
}
|
|
switch level {
|
|
case QUICEncryptionLevelHandshake,
|
|
QUICEncryptionLevelEarly,
|
|
QUICEncryptionLevelApplication:
|
|
q.readSecret[level] = suiteSecret{suite, secret}
|
|
default:
|
|
q.t.Errorf("SetReadSecret for unexpected level %v", level)
|
|
}
|
|
}
|
|
|
|
func (q *testQUICConn) setWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
|
|
if _, ok := q.writeSecret[level]; ok {
|
|
q.t.Errorf("SetWriteSecret for level %v called twice", level)
|
|
}
|
|
if q.writeSecret == nil {
|
|
q.writeSecret = map[QUICEncryptionLevel]suiteSecret{}
|
|
}
|
|
switch level {
|
|
case QUICEncryptionLevelHandshake,
|
|
QUICEncryptionLevelEarly,
|
|
QUICEncryptionLevelApplication:
|
|
q.writeSecret[level] = suiteSecret{suite, secret}
|
|
default:
|
|
q.t.Errorf("SetWriteSecret for unexpected level %v", level)
|
|
}
|
|
}
|
|
|
|
var errTransportParametersRequired = errors.New("transport parameters required")
|
|
|
|
func runTestQUICConnection(ctx context.Context, cli, srv *testQUICConn, onEvent func(e QUICEvent, src, dst *testQUICConn) bool) error {
|
|
a, b := cli, srv
|
|
for _, c := range []*testQUICConn{a, b} {
|
|
if !c.conn.conn.quic.started {
|
|
if err := c.conn.Start(ctx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
idleCount := 0
|
|
for {
|
|
e := a.conn.NextEvent()
|
|
if onEvent != nil && onEvent(e, a, b) {
|
|
continue
|
|
}
|
|
switch e.Kind {
|
|
case QUICNoEvent:
|
|
idleCount++
|
|
if idleCount == 2 {
|
|
if !a.complete || !b.complete {
|
|
return errors.New("handshake incomplete")
|
|
}
|
|
return nil
|
|
}
|
|
a, b = b, a
|
|
case QUICSetReadSecret:
|
|
a.setReadSecret(e.Level, e.Suite, e.Data)
|
|
case QUICSetWriteSecret:
|
|
a.setWriteSecret(e.Level, e.Suite, e.Data)
|
|
case QUICWriteData:
|
|
if err := b.conn.HandleData(e.Level, e.Data); err != nil {
|
|
return err
|
|
}
|
|
case QUICTransportParameters:
|
|
a.gotParams = e.Data
|
|
if a.gotParams == nil {
|
|
a.gotParams = []byte{}
|
|
}
|
|
case QUICTransportParametersRequired:
|
|
return errTransportParametersRequired
|
|
case QUICHandshakeDone:
|
|
a.complete = true
|
|
if a == srv {
|
|
if err := srv.conn.SendSessionTicket(srv.ticketOpts); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case QUICStoreSession:
|
|
if a != cli {
|
|
return errors.New("unexpected QUICStoreSession event received by server")
|
|
}
|
|
a.conn.StoreSession(e.SessionState)
|
|
case QUICResumeSession:
|
|
if a.onResumeSession != nil {
|
|
a.onResumeSession(e.SessionState)
|
|
}
|
|
case QUICRejectedEarlyData:
|
|
a.earlyDataRejected = true
|
|
}
|
|
if e.Kind != QUICNoEvent {
|
|
idleCount = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQUICConnection(t *testing.T) {
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
|
|
if _, ok := cli.readSecret[QUICEncryptionLevelHandshake]; !ok {
|
|
t.Errorf("client has no Handshake secret")
|
|
}
|
|
if _, ok := cli.readSecret[QUICEncryptionLevelApplication]; !ok {
|
|
t.Errorf("client has no Application secret")
|
|
}
|
|
if _, ok := srv.readSecret[QUICEncryptionLevelHandshake]; !ok {
|
|
t.Errorf("server has no Handshake secret")
|
|
}
|
|
if _, ok := srv.readSecret[QUICEncryptionLevelApplication]; !ok {
|
|
t.Errorf("server has no Application secret")
|
|
}
|
|
for _, level := range []QUICEncryptionLevel{QUICEncryptionLevelHandshake, QUICEncryptionLevelApplication} {
|
|
if _, ok := cli.readSecret[level]; !ok {
|
|
t.Errorf("client has no %v read secret", level)
|
|
}
|
|
if _, ok := srv.readSecret[level]; !ok {
|
|
t.Errorf("server has no %v read secret", level)
|
|
}
|
|
if !reflect.DeepEqual(cli.readSecret[level], srv.writeSecret[level]) {
|
|
t.Errorf("client read secret does not match server write secret for level %v", level)
|
|
}
|
|
if !reflect.DeepEqual(cli.writeSecret[level], srv.readSecret[level]) {
|
|
t.Errorf("client write secret does not match server read secret for level %v", level)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQUICSessionResumption(t *testing.T) {
|
|
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
|
clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1)
|
|
clientConfig.TLSConfig.ServerName = "example.go.dev"
|
|
|
|
serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
serverConfig.TLSConfig.MinVersion = VersionTLS13
|
|
|
|
cli := newTestQUICClient(t, clientConfig)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, serverConfig)
|
|
srv.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during first connection handshake: %v", err)
|
|
}
|
|
if cli.conn.ConnectionState().DidResume {
|
|
t.Errorf("first connection unexpectedly used session resumption")
|
|
}
|
|
|
|
cli2 := newTestQUICClient(t, clientConfig)
|
|
cli2.conn.SetTransportParameters(nil)
|
|
srv2 := newTestQUICServer(t, serverConfig)
|
|
srv2.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(context.Background(), cli2, srv2, nil); err != nil {
|
|
t.Fatalf("error during second connection handshake: %v", err)
|
|
}
|
|
if !cli2.conn.ConnectionState().DidResume {
|
|
t.Errorf("second connection did not use session resumption")
|
|
}
|
|
}
|
|
|
|
func TestQUICFragmentaryData(t *testing.T) {
|
|
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
|
clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1)
|
|
clientConfig.TLSConfig.ServerName = "example.go.dev"
|
|
|
|
serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
serverConfig.TLSConfig.MinVersion = VersionTLS13
|
|
|
|
cli := newTestQUICClient(t, clientConfig)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, serverConfig)
|
|
srv.conn.SetTransportParameters(nil)
|
|
onEvent := func(e QUICEvent, src, dst *testQUICConn) bool {
|
|
if e.Kind == QUICWriteData {
|
|
// Provide the data one byte at a time.
|
|
for i := range e.Data {
|
|
if err := dst.conn.HandleData(e.Level, e.Data[i:i+1]); err != nil {
|
|
t.Errorf("HandleData: %v", err)
|
|
break
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, onEvent); err != nil {
|
|
t.Fatalf("error during first connection handshake: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestQUICPostHandshakeClientAuthentication(t *testing.T) {
|
|
// RFC 9001, Section 4.4.
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
|
|
certReq := new(certificateRequestMsgTLS13)
|
|
certReq.ocspStapling = true
|
|
certReq.scts = true
|
|
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
|
certReqBytes, err := certReq.marshal()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cli.conn.HandleData(QUICEncryptionLevelApplication, append([]byte{
|
|
byte(typeCertificateRequest),
|
|
byte(0), byte(0), byte(len(certReqBytes)),
|
|
}, certReqBytes...)); err == nil {
|
|
t.Fatalf("post-handshake authentication request: got no error, want one")
|
|
}
|
|
}
|
|
|
|
func TestQUICPostHandshakeKeyUpdate(t *testing.T) {
|
|
// RFC 9001, Section 6.
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
|
|
keyUpdate := new(keyUpdateMsg)
|
|
keyUpdateBytes, err := keyUpdate.marshal()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := cli.conn.HandleData(QUICEncryptionLevelApplication, append([]byte{
|
|
byte(typeKeyUpdate),
|
|
byte(0), byte(0), byte(len(keyUpdateBytes)),
|
|
}, keyUpdateBytes...)); !errors.Is(err, alertUnexpectedMessage) {
|
|
t.Fatalf("key update request: got error %v, want alertUnexpectedMessage", err)
|
|
}
|
|
}
|
|
|
|
func TestQUICPostHandshakeMessageTooLarge(t *testing.T) {
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
|
|
size := maxHandshake + 1
|
|
if err := cli.conn.HandleData(QUICEncryptionLevelApplication, []byte{
|
|
byte(typeNewSessionTicket),
|
|
byte(size >> 16),
|
|
byte(size >> 8),
|
|
byte(size),
|
|
}); err == nil {
|
|
t.Fatalf("%v-byte post-handshake message: got no error, want one", size)
|
|
}
|
|
}
|
|
|
|
func TestQUICHandshakeError(t *testing.T) {
|
|
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
|
clientConfig.TLSConfig.InsecureSkipVerify = false
|
|
clientConfig.TLSConfig.ServerName = "name"
|
|
|
|
serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
serverConfig.TLSConfig.MinVersion = VersionTLS13
|
|
|
|
cli := newTestQUICClient(t, clientConfig)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, serverConfig)
|
|
srv.conn.SetTransportParameters(nil)
|
|
err := runTestQUICConnection(context.Background(), cli, srv, nil)
|
|
if !errors.Is(err, AlertError(alertBadCertificate)) {
|
|
t.Errorf("connection handshake terminated with error %q, want alertBadCertificate", err)
|
|
}
|
|
var e *CertificateVerificationError
|
|
if !errors.As(err, &e) {
|
|
t.Errorf("connection handshake terminated with error %q, want CertificateVerificationError", err)
|
|
}
|
|
}
|
|
|
|
// Test that QUICConn.ConnectionState can be used during the handshake,
|
|
// and that it reports the application protocol as soon as it has been
|
|
// negotiated.
|
|
func TestQUICConnectionState(t *testing.T) {
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
config.TLSConfig.NextProtos = []string{"h3"}
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
onEvent := func(e QUICEvent, src, dst *testQUICConn) bool {
|
|
cliCS := cli.conn.ConnectionState()
|
|
if _, ok := cli.readSecret[QUICEncryptionLevelApplication]; ok {
|
|
if want, got := cliCS.NegotiatedProtocol, "h3"; want != got {
|
|
t.Errorf("cli.ConnectionState().NegotiatedProtocol = %q, want %q", want, got)
|
|
}
|
|
}
|
|
srvCS := srv.conn.ConnectionState()
|
|
if _, ok := srv.readSecret[QUICEncryptionLevelHandshake]; ok {
|
|
if want, got := srvCS.NegotiatedProtocol, "h3"; want != got {
|
|
t.Errorf("srv.ConnectionState().NegotiatedProtocol = %q, want %q", want, got)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, onEvent); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestQUICStartContextPropagation(t *testing.T) {
|
|
const key = "key"
|
|
const value = "value"
|
|
ctx := context.WithValue(context.Background(), key, value)
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
calls := 0
|
|
config.TLSConfig.GetConfigForClient = func(info *ClientHelloInfo) (*Config, error) {
|
|
calls++
|
|
got, _ := info.Context().Value(key).(string)
|
|
if got != value {
|
|
t.Errorf("GetConfigForClient context key %q has value %q, want %q", key, got, value)
|
|
}
|
|
return nil, nil
|
|
}
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(ctx, cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
if calls != 1 {
|
|
t.Errorf("GetConfigForClient called %v times, want 1", calls)
|
|
}
|
|
}
|
|
|
|
func TestQUICDelayedTransportParameters(t *testing.T) {
|
|
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
|
clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1)
|
|
clientConfig.TLSConfig.ServerName = "example.go.dev"
|
|
|
|
serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
serverConfig.TLSConfig.MinVersion = VersionTLS13
|
|
|
|
cliParams := "client params"
|
|
srvParams := "server params"
|
|
|
|
cli := newTestQUICClient(t, clientConfig)
|
|
srv := newTestQUICServer(t, serverConfig)
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != errTransportParametersRequired {
|
|
t.Fatalf("handshake with no client parameters: %v; want errTransportParametersRequired", err)
|
|
}
|
|
cli.conn.SetTransportParameters([]byte(cliParams))
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != errTransportParametersRequired {
|
|
t.Fatalf("handshake with no server parameters: %v; want errTransportParametersRequired", err)
|
|
}
|
|
srv.conn.SetTransportParameters([]byte(srvParams))
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
|
|
if got, want := string(cli.gotParams), srvParams; got != want {
|
|
t.Errorf("client got transport params: %q, want %q", got, want)
|
|
}
|
|
if got, want := string(srv.gotParams), cliParams; got != want {
|
|
t.Errorf("server got transport params: %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestQUICEmptyTransportParameters(t *testing.T) {
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, config)
|
|
srv.conn.SetTransportParameters(nil)
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during connection handshake: %v", err)
|
|
}
|
|
|
|
if cli.gotParams == nil {
|
|
t.Errorf("client did not get transport params")
|
|
}
|
|
if srv.gotParams == nil {
|
|
t.Errorf("server did not get transport params")
|
|
}
|
|
if len(cli.gotParams) != 0 {
|
|
t.Errorf("client got transport params: %v, want empty", cli.gotParams)
|
|
}
|
|
if len(srv.gotParams) != 0 {
|
|
t.Errorf("server got transport params: %v, want empty", srv.gotParams)
|
|
}
|
|
}
|
|
|
|
func TestQUICCanceledWaitingForData(t *testing.T) {
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.SetTransportParameters(nil)
|
|
cli.conn.Start(context.Background())
|
|
for cli.conn.NextEvent().Kind != QUICNoEvent {
|
|
}
|
|
err := cli.conn.Close()
|
|
if !errors.Is(err, alertCloseNotify) {
|
|
t.Errorf("conn.Close() = %v, want alertCloseNotify", err)
|
|
}
|
|
}
|
|
|
|
func TestQUICCanceledWaitingForTransportParams(t *testing.T) {
|
|
config := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
config.TLSConfig.MinVersion = VersionTLS13
|
|
cli := newTestQUICClient(t, config)
|
|
cli.conn.Start(context.Background())
|
|
for cli.conn.NextEvent().Kind != QUICTransportParametersRequired {
|
|
}
|
|
err := cli.conn.Close()
|
|
if !errors.Is(err, alertCloseNotify) {
|
|
t.Errorf("conn.Close() = %v, want alertCloseNotify", err)
|
|
}
|
|
}
|
|
|
|
func TestQUICEarlyData(t *testing.T) {
|
|
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
|
clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1)
|
|
clientConfig.TLSConfig.ServerName = "example.go.dev"
|
|
clientConfig.TLSConfig.NextProtos = []string{"h3"}
|
|
|
|
serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
serverConfig.TLSConfig.MinVersion = VersionTLS13
|
|
serverConfig.TLSConfig.NextProtos = []string{"h3"}
|
|
|
|
cli := newTestQUICClient(t, clientConfig)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, serverConfig)
|
|
srv.conn.SetTransportParameters(nil)
|
|
srv.ticketOpts.EarlyData = true
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during first connection handshake: %v", err)
|
|
}
|
|
if cli.conn.ConnectionState().DidResume {
|
|
t.Errorf("first connection unexpectedly used session resumption")
|
|
}
|
|
|
|
cli2 := newTestQUICClient(t, clientConfig)
|
|
cli2.conn.SetTransportParameters(nil)
|
|
srv2 := newTestQUICServer(t, serverConfig)
|
|
srv2.conn.SetTransportParameters(nil)
|
|
onEvent := func(e QUICEvent, src, dst *testQUICConn) bool {
|
|
switch e.Kind {
|
|
case QUICStoreSession, QUICResumeSession:
|
|
t.Errorf("with EnableSessionEvents=false, got unexpected event %v", e.Kind)
|
|
}
|
|
return false
|
|
}
|
|
if err := runTestQUICConnection(context.Background(), cli2, srv2, onEvent); err != nil {
|
|
t.Fatalf("error during second connection handshake: %v", err)
|
|
}
|
|
if !cli2.conn.ConnectionState().DidResume {
|
|
t.Errorf("second connection did not use session resumption")
|
|
}
|
|
cliSecret := cli2.writeSecret[QUICEncryptionLevelEarly]
|
|
if cliSecret.secret == nil {
|
|
t.Errorf("client did not receive early data write secret")
|
|
}
|
|
srvSecret := srv2.readSecret[QUICEncryptionLevelEarly]
|
|
if srvSecret.secret == nil {
|
|
t.Errorf("server did not receive early data read secret")
|
|
}
|
|
if cliSecret.suite != srvSecret.suite || !bytes.Equal(cliSecret.secret, srvSecret.secret) {
|
|
t.Errorf("client early data secret does not match server")
|
|
}
|
|
}
|
|
|
|
func TestQUICEarlyDataDeclined(t *testing.T) {
|
|
t.Run("server", func(t *testing.T) {
|
|
testQUICEarlyDataDeclined(t, true)
|
|
})
|
|
t.Run("client", func(t *testing.T) {
|
|
testQUICEarlyDataDeclined(t, false)
|
|
})
|
|
}
|
|
|
|
func testQUICEarlyDataDeclined(t *testing.T, server bool) {
|
|
clientConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
clientConfig.EnableSessionEvents = true
|
|
clientConfig.TLSConfig.MinVersion = VersionTLS13
|
|
clientConfig.TLSConfig.ClientSessionCache = NewLRUClientSessionCache(1)
|
|
clientConfig.TLSConfig.ServerName = "example.go.dev"
|
|
clientConfig.TLSConfig.NextProtos = []string{"h3"}
|
|
|
|
serverConfig := &QUICConfig{TLSConfig: testConfig.Clone()}
|
|
serverConfig.EnableSessionEvents = true
|
|
serverConfig.TLSConfig.MinVersion = VersionTLS13
|
|
serverConfig.TLSConfig.NextProtos = []string{"h3"}
|
|
|
|
cli := newTestQUICClient(t, clientConfig)
|
|
cli.conn.SetTransportParameters(nil)
|
|
srv := newTestQUICServer(t, serverConfig)
|
|
srv.conn.SetTransportParameters(nil)
|
|
srv.ticketOpts.EarlyData = true
|
|
if err := runTestQUICConnection(context.Background(), cli, srv, nil); err != nil {
|
|
t.Fatalf("error during first connection handshake: %v", err)
|
|
}
|
|
if cli.conn.ConnectionState().DidResume {
|
|
t.Errorf("first connection unexpectedly used session resumption")
|
|
}
|
|
|
|
cli2 := newTestQUICClient(t, clientConfig)
|
|
cli2.conn.SetTransportParameters(nil)
|
|
srv2 := newTestQUICServer(t, serverConfig)
|
|
srv2.conn.SetTransportParameters(nil)
|
|
declineEarlyData := func(state *SessionState) {
|
|
state.EarlyData = false
|
|
}
|
|
if server {
|
|
srv2.onResumeSession = declineEarlyData
|
|
} else {
|
|
cli2.onResumeSession = declineEarlyData
|
|
}
|
|
if err := runTestQUICConnection(context.Background(), cli2, srv2, nil); err != nil {
|
|
t.Fatalf("error during second connection handshake: %v", err)
|
|
}
|
|
if !cli2.conn.ConnectionState().DidResume {
|
|
t.Errorf("second connection did not use session resumption")
|
|
}
|
|
_, cliEarlyData := cli2.writeSecret[QUICEncryptionLevelEarly]
|
|
if server {
|
|
if !cliEarlyData {
|
|
t.Errorf("client did not receive early data write secret")
|
|
}
|
|
if !cli2.earlyDataRejected {
|
|
t.Errorf("client did not receive QUICEarlyDataRejected")
|
|
}
|
|
}
|
|
if _, srvEarlyData := srv2.readSecret[QUICEncryptionLevelEarly]; srvEarlyData {
|
|
t.Errorf("server received early data read secret")
|
|
}
|
|
}
|