mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 12:47:36 +03:00
uTLS is not yet bumped to the new version, so this commit breaks the dependencies relationship by getting rid of the local replace.
648 lines
18 KiB
Go
648 lines
18 KiB
Go
package handshake
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
tls "github.com/refraction-networking/utls"
|
|
|
|
"github.com/refraction-networking/uquic/internal/protocol"
|
|
"github.com/refraction-networking/uquic/internal/qerr"
|
|
"github.com/refraction-networking/uquic/internal/qtls"
|
|
"github.com/refraction-networking/uquic/internal/utils"
|
|
"github.com/refraction-networking/uquic/internal/wire"
|
|
"github.com/refraction-networking/uquic/logging"
|
|
"github.com/refraction-networking/uquic/quicvarint"
|
|
)
|
|
|
|
type quicVersionContextKey struct{}
|
|
|
|
var QUICVersionContextKey = &quicVersionContextKey{}
|
|
|
|
const clientSessionStateRevision = 3
|
|
|
|
type cryptoSetup struct {
|
|
tlsConf *tls.Config
|
|
conn *qtls.QUICConn
|
|
|
|
events []Event
|
|
|
|
version protocol.VersionNumber
|
|
|
|
ourParams *wire.TransportParameters
|
|
peerParams *wire.TransportParameters
|
|
|
|
zeroRTTParameters *wire.TransportParameters
|
|
allow0RTT bool
|
|
|
|
rttStats *utils.RTTStats
|
|
|
|
tracer logging.ConnectionTracer
|
|
logger utils.Logger
|
|
|
|
perspective protocol.Perspective
|
|
|
|
mutex sync.Mutex // protects all members below
|
|
|
|
handshakeCompleteTime time.Time
|
|
|
|
zeroRTTOpener LongHeaderOpener // only set for the server
|
|
zeroRTTSealer LongHeaderSealer // only set for the client
|
|
|
|
initialOpener LongHeaderOpener
|
|
initialSealer LongHeaderSealer
|
|
|
|
handshakeOpener LongHeaderOpener
|
|
handshakeSealer LongHeaderSealer
|
|
|
|
used0RTT atomic.Bool
|
|
|
|
aead *updatableAEAD
|
|
has1RTTSealer bool
|
|
has1RTTOpener bool
|
|
}
|
|
|
|
var _ CryptoSetup = &cryptoSetup{}
|
|
|
|
// NewCryptoSetupClient creates a new crypto setup for the client
|
|
func NewCryptoSetupClient(
|
|
connID protocol.ConnectionID,
|
|
tp *wire.TransportParameters,
|
|
tlsConf *tls.Config,
|
|
enable0RTT bool,
|
|
rttStats *utils.RTTStats,
|
|
tracer logging.ConnectionTracer,
|
|
logger utils.Logger,
|
|
version protocol.VersionNumber,
|
|
) CryptoSetup {
|
|
cs := newCryptoSetup(
|
|
connID,
|
|
tp,
|
|
rttStats,
|
|
tracer,
|
|
logger,
|
|
protocol.PerspectiveClient,
|
|
version,
|
|
)
|
|
|
|
tlsConf = tlsConf.Clone()
|
|
tlsConf.MinVersion = tls.VersionTLS13
|
|
quicConf := &qtls.QUICConfig{TLSConfig: tlsConf}
|
|
qtls.SetupConfigForClient(quicConf, cs.marshalDataForSessionState, cs.handleDataFromSessionState)
|
|
cs.tlsConf = tlsConf
|
|
|
|
cs.conn = qtls.QUICClient(quicConf)
|
|
cs.conn.SetTransportParameters(cs.ourParams.Marshal(protocol.PerspectiveClient))
|
|
|
|
return cs
|
|
}
|
|
|
|
// NewCryptoSetupServer creates a new crypto setup for the server
|
|
func NewCryptoSetupServer(
|
|
connID protocol.ConnectionID,
|
|
localAddr, remoteAddr net.Addr,
|
|
tp *wire.TransportParameters,
|
|
tlsConf *tls.Config,
|
|
allow0RTT bool,
|
|
rttStats *utils.RTTStats,
|
|
tracer logging.ConnectionTracer,
|
|
logger utils.Logger,
|
|
version protocol.VersionNumber,
|
|
) CryptoSetup {
|
|
cs := newCryptoSetup(
|
|
connID,
|
|
tp,
|
|
rttStats,
|
|
tracer,
|
|
logger,
|
|
protocol.PerspectiveServer,
|
|
version,
|
|
)
|
|
cs.allow0RTT = allow0RTT
|
|
|
|
quicConf := &qtls.QUICConfig{TLSConfig: tlsConf}
|
|
qtls.SetupConfigForServer(quicConf, cs.allow0RTT, cs.getDataForSessionTicket, cs.accept0RTT)
|
|
if quicConf.TLSConfig.GetConfigForClient != nil {
|
|
gcfc := quicConf.TLSConfig.GetConfigForClient
|
|
quicConf.TLSConfig.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
|
info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr}
|
|
return gcfc(info)
|
|
}
|
|
}
|
|
|
|
cs.tlsConf = quicConf.TLSConfig
|
|
cs.conn = qtls.QUICServer(quicConf)
|
|
|
|
return cs
|
|
}
|
|
|
|
func newCryptoSetup(
|
|
connID protocol.ConnectionID,
|
|
tp *wire.TransportParameters,
|
|
rttStats *utils.RTTStats,
|
|
tracer logging.ConnectionTracer,
|
|
logger utils.Logger,
|
|
perspective protocol.Perspective,
|
|
version protocol.VersionNumber,
|
|
) *cryptoSetup {
|
|
initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version)
|
|
if tracer != nil {
|
|
tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient)
|
|
tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer)
|
|
}
|
|
return &cryptoSetup{
|
|
initialSealer: initialSealer,
|
|
initialOpener: initialOpener,
|
|
aead: newUpdatableAEAD(rttStats, tracer, logger, version),
|
|
events: make([]Event, 0, 16),
|
|
ourParams: tp,
|
|
rttStats: rttStats,
|
|
tracer: tracer,
|
|
logger: logger,
|
|
perspective: perspective,
|
|
version: version,
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) {
|
|
initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version)
|
|
h.initialSealer = initialSealer
|
|
h.initialOpener = initialOpener
|
|
if h.tracer != nil {
|
|
h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient)
|
|
h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer)
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) SetLargest1RTTAcked(pn protocol.PacketNumber) error {
|
|
return h.aead.SetLargestAcked(pn)
|
|
}
|
|
|
|
func (h *cryptoSetup) StartHandshake() error {
|
|
err := h.conn.Start(context.WithValue(context.Background(), QUICVersionContextKey, h.version))
|
|
if err != nil {
|
|
return wrapError(err)
|
|
}
|
|
for {
|
|
ev := h.conn.NextEvent()
|
|
done, err := h.handleEvent(ev)
|
|
if err != nil {
|
|
return wrapError(err)
|
|
}
|
|
if done {
|
|
break
|
|
}
|
|
}
|
|
if h.perspective == protocol.PerspectiveClient {
|
|
if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil {
|
|
h.logger.Debugf("Doing 0-RTT.")
|
|
h.events = append(h.events, Event{Kind: EventRestoredTransportParameters, TransportParameters: h.zeroRTTParameters})
|
|
} else {
|
|
h.logger.Debugf("Not doing 0-RTT. Has sealer: %t, has params: %t", h.zeroRTTSealer != nil, h.zeroRTTParameters != nil)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Close closes the crypto setup.
|
|
// It aborts the handshake, if it is still running.
|
|
func (h *cryptoSetup) Close() error {
|
|
return h.conn.Close()
|
|
}
|
|
|
|
// HandleMessage handles a TLS handshake message.
|
|
// It is called by the crypto streams when a new message is available.
|
|
func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) error {
|
|
if err := h.handleMessage(data, encLevel); err != nil {
|
|
return wrapError(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *cryptoSetup) handleMessage(data []byte, encLevel protocol.EncryptionLevel) error {
|
|
if err := h.conn.HandleData(qtls.ToTLSEncryptionLevel(encLevel), data); err != nil {
|
|
return err
|
|
}
|
|
for {
|
|
ev := h.conn.NextEvent()
|
|
done, err := h.handleEvent(ev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if done {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) handleEvent(ev qtls.QUICEvent) (done bool, err error) {
|
|
switch ev.Kind {
|
|
case qtls.QUICNoEvent:
|
|
return true, nil
|
|
case qtls.QUICSetReadSecret:
|
|
h.SetReadKey(ev.Level, ev.Suite, ev.Data)
|
|
return false, nil
|
|
case qtls.QUICSetWriteSecret:
|
|
h.SetWriteKey(ev.Level, ev.Suite, ev.Data)
|
|
return false, nil
|
|
case qtls.QUICTransportParameters:
|
|
return false, h.handleTransportParameters(ev.Data)
|
|
case qtls.QUICTransportParametersRequired:
|
|
h.conn.SetTransportParameters(h.ourParams.Marshal(h.perspective))
|
|
// [UQUIC] doesn't expect this and may fail
|
|
return false, nil
|
|
case qtls.QUICRejectedEarlyData:
|
|
h.rejected0RTT()
|
|
return false, nil
|
|
case qtls.QUICWriteData:
|
|
h.WriteRecord(ev.Level, ev.Data)
|
|
return false, nil
|
|
case qtls.QUICHandshakeDone:
|
|
h.handshakeComplete()
|
|
return false, nil
|
|
default:
|
|
return false, fmt.Errorf("unexpected event: %d", ev.Kind)
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) NextEvent() Event {
|
|
if len(h.events) == 0 {
|
|
return Event{Kind: EventNoEvent}
|
|
}
|
|
ev := h.events[0]
|
|
h.events = h.events[1:]
|
|
return ev
|
|
}
|
|
|
|
func (h *cryptoSetup) handleTransportParameters(data []byte) error {
|
|
var tp wire.TransportParameters
|
|
if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil {
|
|
return err
|
|
}
|
|
h.peerParams = &tp
|
|
h.events = append(h.events, Event{Kind: EventReceivedTransportParameters, TransportParameters: h.peerParams})
|
|
return nil
|
|
}
|
|
|
|
// must be called after receiving the transport parameters
|
|
func (h *cryptoSetup) marshalDataForSessionState() []byte {
|
|
b := make([]byte, 0, 256)
|
|
b = quicvarint.Append(b, clientSessionStateRevision)
|
|
b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds()))
|
|
return h.peerParams.MarshalForSessionTicket(b)
|
|
}
|
|
|
|
func (h *cryptoSetup) handleDataFromSessionState(data []byte) {
|
|
tp, err := h.handleDataFromSessionStateImpl(data)
|
|
if err != nil {
|
|
h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error())
|
|
return
|
|
}
|
|
h.zeroRTTParameters = tp
|
|
}
|
|
|
|
func (h *cryptoSetup) handleDataFromSessionStateImpl(data []byte) (*wire.TransportParameters, error) {
|
|
r := bytes.NewReader(data)
|
|
ver, err := quicvarint.Read(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ver != clientSessionStateRevision {
|
|
return nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision)
|
|
}
|
|
rtt, err := quicvarint.Read(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h.rttStats.SetInitialRTT(time.Duration(rtt) * time.Microsecond)
|
|
var tp wire.TransportParameters
|
|
if err := tp.UnmarshalFromSessionTicket(r); err != nil {
|
|
return nil, err
|
|
}
|
|
return &tp, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) getDataForSessionTicket() []byte {
|
|
return (&sessionTicket{
|
|
Parameters: h.ourParams,
|
|
RTT: h.rttStats.SmoothedRTT(),
|
|
}).Marshal()
|
|
}
|
|
|
|
// GetSessionTicket generates a new session ticket.
|
|
// Due to limitations in crypto/tls, it's only possible to generate a single session ticket per connection.
|
|
// It is only valid for the server.
|
|
func (h *cryptoSetup) GetSessionTicket() ([]byte, error) {
|
|
if h.tlsConf.SessionTicketsDisabled {
|
|
return nil, nil
|
|
}
|
|
if err := h.conn.SendSessionTicket(h.allow0RTT); err != nil {
|
|
return nil, err
|
|
}
|
|
ev := h.conn.NextEvent()
|
|
if ev.Kind != qtls.QUICWriteData || ev.Level != qtls.QUICEncryptionLevelApplication {
|
|
panic("crypto/tls bug: where's my session ticket?")
|
|
}
|
|
ticket := ev.Data
|
|
if ev := h.conn.NextEvent(); ev.Kind != qtls.QUICNoEvent {
|
|
panic("crypto/tls bug: why more than one ticket?")
|
|
}
|
|
return ticket, nil
|
|
}
|
|
|
|
// accept0RTT is called for the server when receiving the client's session ticket.
|
|
// It decides whether to accept 0-RTT.
|
|
func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool {
|
|
var t sessionTicket
|
|
if err := t.Unmarshal(sessionTicketData); err != nil {
|
|
h.logger.Debugf("Unmarshalling transport parameters from session ticket failed: %s", err.Error())
|
|
return false
|
|
}
|
|
valid := h.ourParams.ValidFor0RTT(t.Parameters)
|
|
if !valid {
|
|
h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.")
|
|
return false
|
|
}
|
|
if !h.allow0RTT {
|
|
h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.")
|
|
return false
|
|
}
|
|
h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT)
|
|
h.rttStats.SetInitialRTT(t.RTT)
|
|
return true
|
|
}
|
|
|
|
// rejected0RTT is called for the client when the server rejects 0-RTT.
|
|
func (h *cryptoSetup) rejected0RTT() {
|
|
h.logger.Debugf("0-RTT was rejected. Dropping 0-RTT keys.")
|
|
|
|
h.mutex.Lock()
|
|
had0RTTKeys := h.zeroRTTSealer != nil
|
|
h.zeroRTTSealer = nil
|
|
h.mutex.Unlock()
|
|
|
|
if had0RTTKeys {
|
|
h.events = append(h.events, Event{Kind: EventDiscard0RTTKeys})
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) SetReadKey(el qtls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) {
|
|
suite := getCipherSuite(suiteID)
|
|
h.mutex.Lock()
|
|
//nolint:exhaustive // The TLS stack doesn't export Initial keys.
|
|
switch el {
|
|
case qtls.QUICEncryptionLevelEarly:
|
|
if h.perspective == protocol.PerspectiveClient {
|
|
panic("Received 0-RTT read key for the client")
|
|
}
|
|
h.zeroRTTOpener = newLongHeaderOpener(
|
|
createAEAD(suite, trafficSecret, h.version),
|
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
|
)
|
|
h.used0RTT.Store(true)
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
|
}
|
|
case qtls.QUICEncryptionLevelHandshake:
|
|
h.handshakeOpener = newLongHeaderOpener(
|
|
createAEAD(suite, trafficSecret, h.version),
|
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
|
)
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
|
}
|
|
case qtls.QUICEncryptionLevelApplication:
|
|
h.aead.SetReadKey(suite, trafficSecret)
|
|
h.has1RTTOpener = true
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID))
|
|
}
|
|
default:
|
|
panic("unexpected read encryption level")
|
|
}
|
|
h.mutex.Unlock()
|
|
h.events = append(h.events, Event{Kind: EventReceivedReadKeys})
|
|
if h.tracer != nil {
|
|
h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective.Opposite())
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) SetWriteKey(el qtls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) {
|
|
suite := getCipherSuite(suiteID)
|
|
h.mutex.Lock()
|
|
//nolint:exhaustive // The TLS stack doesn't export Initial keys.
|
|
switch el {
|
|
case qtls.QUICEncryptionLevelEarly:
|
|
if h.perspective == protocol.PerspectiveServer {
|
|
panic("Received 0-RTT write key for the server")
|
|
}
|
|
h.zeroRTTSealer = newLongHeaderSealer(
|
|
createAEAD(suite, trafficSecret, h.version),
|
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
|
)
|
|
h.mutex.Unlock()
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
|
}
|
|
if h.tracer != nil {
|
|
h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective)
|
|
}
|
|
// don't set used0RTT here. 0-RTT might still get rejected.
|
|
return
|
|
case qtls.QUICEncryptionLevelHandshake:
|
|
h.handshakeSealer = newLongHeaderSealer(
|
|
createAEAD(suite, trafficSecret, h.version),
|
|
newHeaderProtector(suite, trafficSecret, true, h.version),
|
|
)
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
|
}
|
|
case qtls.QUICEncryptionLevelApplication:
|
|
h.aead.SetWriteKey(suite, trafficSecret)
|
|
h.has1RTTSealer = true
|
|
if h.logger.Debug() {
|
|
h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID))
|
|
}
|
|
if h.zeroRTTSealer != nil {
|
|
// Once we receive handshake keys, we know that 0-RTT was not rejected.
|
|
h.used0RTT.Store(true)
|
|
h.zeroRTTSealer = nil
|
|
h.logger.Debugf("Dropping 0-RTT keys.")
|
|
if h.tracer != nil {
|
|
h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT)
|
|
}
|
|
}
|
|
default:
|
|
panic("unexpected write encryption level")
|
|
}
|
|
h.mutex.Unlock()
|
|
if h.tracer != nil {
|
|
h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective)
|
|
}
|
|
}
|
|
|
|
// WriteRecord is called when TLS writes data
|
|
func (h *cryptoSetup) WriteRecord(encLevel qtls.QUICEncryptionLevel, p []byte) {
|
|
//nolint:exhaustive // handshake records can only be written for Initial and Handshake.
|
|
switch encLevel {
|
|
case qtls.QUICEncryptionLevelInitial:
|
|
h.events = append(h.events, Event{Kind: EventWriteInitialData, Data: p})
|
|
case qtls.QUICEncryptionLevelHandshake:
|
|
h.events = append(h.events, Event{Kind: EventWriteHandshakeData, Data: p})
|
|
case qtls.QUICEncryptionLevelApplication:
|
|
panic("unexpected write")
|
|
default:
|
|
panic(fmt.Sprintf("unexpected write encryption level: %s", encLevel))
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) DiscardInitialKeys() {
|
|
h.mutex.Lock()
|
|
dropped := h.initialOpener != nil
|
|
h.initialOpener = nil
|
|
h.initialSealer = nil
|
|
h.mutex.Unlock()
|
|
if dropped {
|
|
h.logger.Debugf("Dropping Initial keys.")
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) handshakeComplete() {
|
|
h.handshakeCompleteTime = time.Now()
|
|
h.events = append(h.events, Event{Kind: EventHandshakeComplete})
|
|
}
|
|
|
|
func (h *cryptoSetup) SetHandshakeConfirmed() {
|
|
h.aead.SetHandshakeConfirmed()
|
|
// drop Handshake keys
|
|
var dropped bool
|
|
h.mutex.Lock()
|
|
if h.handshakeOpener != nil {
|
|
h.handshakeOpener = nil
|
|
h.handshakeSealer = nil
|
|
dropped = true
|
|
}
|
|
h.mutex.Unlock()
|
|
if dropped {
|
|
h.logger.Debugf("Dropping Handshake keys.")
|
|
}
|
|
}
|
|
|
|
func (h *cryptoSetup) GetInitialSealer() (LongHeaderSealer, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.initialSealer == nil {
|
|
return nil, ErrKeysDropped
|
|
}
|
|
return h.initialSealer, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.zeroRTTSealer == nil {
|
|
return nil, ErrKeysDropped
|
|
}
|
|
return h.zeroRTTSealer, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) GetHandshakeSealer() (LongHeaderSealer, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.handshakeSealer == nil {
|
|
if h.initialSealer == nil {
|
|
return nil, ErrKeysDropped
|
|
}
|
|
return nil, ErrKeysNotYetAvailable
|
|
}
|
|
return h.handshakeSealer, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) Get1RTTSealer() (ShortHeaderSealer, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if !h.has1RTTSealer {
|
|
return nil, ErrKeysNotYetAvailable
|
|
}
|
|
return h.aead, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) GetInitialOpener() (LongHeaderOpener, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.initialOpener == nil {
|
|
return nil, ErrKeysDropped
|
|
}
|
|
return h.initialOpener, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) Get0RTTOpener() (LongHeaderOpener, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.zeroRTTOpener == nil {
|
|
if h.initialOpener != nil {
|
|
return nil, ErrKeysNotYetAvailable
|
|
}
|
|
// if the initial opener is also not available, the keys were already dropped
|
|
return nil, ErrKeysDropped
|
|
}
|
|
return h.zeroRTTOpener, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) GetHandshakeOpener() (LongHeaderOpener, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.handshakeOpener == nil {
|
|
if h.initialOpener != nil {
|
|
return nil, ErrKeysNotYetAvailable
|
|
}
|
|
// if the initial opener is also not available, the keys were already dropped
|
|
return nil, ErrKeysDropped
|
|
}
|
|
return h.handshakeOpener, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) {
|
|
h.mutex.Lock()
|
|
defer h.mutex.Unlock()
|
|
|
|
if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) {
|
|
h.zeroRTTOpener = nil
|
|
h.logger.Debugf("Dropping 0-RTT keys.")
|
|
if h.tracer != nil {
|
|
h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT)
|
|
}
|
|
}
|
|
|
|
if !h.has1RTTOpener {
|
|
return nil, ErrKeysNotYetAvailable
|
|
}
|
|
return h.aead, nil
|
|
}
|
|
|
|
func (h *cryptoSetup) ConnectionState() ConnectionState {
|
|
return ConnectionState{
|
|
ConnectionState: h.conn.ConnectionState(),
|
|
Used0RTT: h.used0RTT.Load(),
|
|
}
|
|
}
|
|
|
|
func wrapError(err error) error {
|
|
if alertErr := qtls.AlertError(0); errors.As(err, &alertErr) && alertErr != 80 {
|
|
return qerr.NewLocalCryptoError(uint8(alertErr), err.Error())
|
|
}
|
|
return &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: err.Error()}
|
|
}
|