blob: 584316f0e457ef1469bd34c86291ee78f315311a [file] [log] [blame]
// 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.
//go:build go1.21
package quic
import (
"context"
"crypto/tls"
"errors"
"fmt"
"time"
)
// startTLS starts the TLS handshake.
func (c *Conn) startTLS(now time.Time, initialConnID []byte, params transportParameters) error {
clientKeys, serverKeys := initialKeys(initialConnID)
if c.side == clientSide {
c.wkeys[initialSpace], c.rkeys[initialSpace] = clientKeys, serverKeys
} else {
c.wkeys[initialSpace], c.rkeys[initialSpace] = serverKeys, clientKeys
}
qconfig := &tls.QUICConfig{TLSConfig: c.config.TLSConfig}
if c.side == clientSide {
c.tls = tls.QUICClient(qconfig)
} else {
c.tls = tls.QUICServer(qconfig)
}
c.tls.SetTransportParameters(marshalTransportParameters(params))
// TODO: We don't need or want a context for cancelation here,
// but users can use a context to plumb values through to hooks defined
// in the tls.Config. Pass through a context.
if err := c.tls.Start(context.TODO()); err != nil {
return err
}
return c.handleTLSEvents(now)
}
func (c *Conn) handleTLSEvents(now time.Time) error {
for {
e := c.tls.NextEvent()
if c.testHooks != nil {
c.testHooks.handleTLSEvent(e)
}
switch e.Kind {
case tls.QUICNoEvent:
return nil
case tls.QUICSetReadSecret:
space, k, err := tlsKey(e)
if err != nil {
return err
}
c.rkeys[space] = k
case tls.QUICSetWriteSecret:
space, k, err := tlsKey(e)
if err != nil {
return err
}
c.wkeys[space] = k
case tls.QUICWriteData:
space, err := spaceForLevel(e.Level)
if err != nil {
return err
}
c.crypto[space].write(e.Data)
case tls.QUICHandshakeDone:
if c.side == serverSide {
// "[...] the TLS handshake is considered confirmed
// at the server when the handshake completes."
// https://www.rfc-editor.org/rfc/rfc9001#section-4.1.2-1
c.confirmHandshake(now)
}
case tls.QUICTransportParameters:
params, err := unmarshalTransportParams(e.Data)
if err != nil {
return err
}
if err := c.receiveTransportParameters(params); err != nil {
return err
}
}
}
}
// tlsKey returns the keys in a QUICSetReadSecret or QUICSetWriteSecret event.
func tlsKey(e tls.QUICEvent) (numberSpace, keys, error) {
space, err := spaceForLevel(e.Level)
if err != nil {
return 0, keys{}, err
}
k, err := newKeys(e.Suite, e.Data)
if err != nil {
return 0, keys{}, err
}
return space, k, nil
}
func spaceForLevel(level tls.QUICEncryptionLevel) (numberSpace, error) {
switch level {
case tls.QUICEncryptionLevelInitial:
return initialSpace, nil
case tls.QUICEncryptionLevelHandshake:
return handshakeSpace, nil
case tls.QUICEncryptionLevelApplication:
return appDataSpace, nil
default:
return 0, fmt.Errorf("quic: internal error: write handshake data at level %v", level)
}
}
// handleCrypto processes data received in a CRYPTO frame.
func (c *Conn) handleCrypto(now time.Time, space numberSpace, off int64, data []byte) error {
var level tls.QUICEncryptionLevel
switch space {
case initialSpace:
level = tls.QUICEncryptionLevelInitial
case handshakeSpace:
level = tls.QUICEncryptionLevelHandshake
case appDataSpace:
level = tls.QUICEncryptionLevelApplication
default:
return errors.New("quic: internal error: received CRYPTO frame in unexpected number space")
}
err := c.crypto[space].handleCrypto(off, data, func(b []byte) error {
return c.tls.HandleData(level, b)
})
if err != nil {
return err
}
return c.handleTLSEvents(now)
}