package tls
import (
)
type QUICEncryptionLevel int
const (
QUICEncryptionLevelInitial = QUICEncryptionLevel(iota)
QUICEncryptionLevelEarly
QUICEncryptionLevelHandshake
QUICEncryptionLevelApplication
)
func ( QUICEncryptionLevel) () string {
switch {
case QUICEncryptionLevelInitial:
return "Initial"
case QUICEncryptionLevelEarly:
return "Early"
case QUICEncryptionLevelHandshake:
return "Handshake"
case QUICEncryptionLevelApplication:
return "Application"
default:
return fmt.Sprintf("QUICEncryptionLevel(%v)", int())
}
}
type QUICConn struct {
conn *Conn
sessionTicketSent bool
}
type QUICConfig struct {
TLSConfig *Config
EnableSessionEvents bool
}
type QUICEventKind int
const (
QUICNoEvent QUICEventKind = iota
QUICSetReadSecret
QUICSetWriteSecret
QUICWriteData
QUICTransportParameters
QUICTransportParametersRequired
QUICRejectedEarlyData
QUICHandshakeDone
QUICResumeSession
QUICStoreSession
)
type QUICEvent struct {
Kind QUICEventKind
Level QUICEncryptionLevel
Data []byte
Suite uint16
SessionState *SessionState
}
type quicState struct {
events []QUICEvent
nextEvent int
eventArr [8]QUICEvent
started bool
signalc chan struct{}
blockedc chan struct{}
cancelc <-chan struct{}
cancel context.CancelFunc
waitingForDrain bool
readbuf []byte
transportParams []byte
enableSessionEvents bool
}
func ( *QUICConfig) *QUICConn {
return newQUICConn(Client(nil, .TLSConfig), )
}
func ( *QUICConfig) *QUICConn {
return newQUICConn(Server(nil, .TLSConfig), )
}
func ( *Conn, *QUICConfig) *QUICConn {
.quic = &quicState{
signalc: make(chan struct{}),
blockedc: make(chan struct{}),
enableSessionEvents: .EnableSessionEvents,
}
.quic.events = .quic.eventArr[:0]
return &QUICConn{
conn: ,
}
}
func ( *QUICConn) ( context.Context) error {
if .conn.quic.started {
return quicError(errors.New("tls: Start called more than once"))
}
.conn.quic.started = true
if .conn.config.MinVersion < VersionTLS13 {
return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3"))
}
go .conn.HandshakeContext()
if , := <-.conn.quic.blockedc; ! {
return .conn.handshakeErr
}
return nil
}
func ( *QUICConn) () QUICEvent {
:= .conn.quic
if := .nextEvent - 1; >= 0 && len(.events[].Data) > 0 {
.events[].Data[0] = 0
}
if .nextEvent >= len(.events) && .waitingForDrain {
.waitingForDrain = false
<-.signalc
<-.blockedc
}
if .nextEvent >= len(.events) {
.events = .events[:0]
.nextEvent = 0
return QUICEvent{Kind: QUICNoEvent}
}
:= .events[.nextEvent]
.events[.nextEvent] = QUICEvent{}
.nextEvent++
return
}
func ( *QUICConn) () error {
if .conn.quic.cancel == nil {
return nil
}
.conn.quic.cancel()
for range .conn.quic.blockedc {
}
return .conn.handshakeErr
}
func ( *QUICConn) ( QUICEncryptionLevel, []byte) error {
:= .conn
if .in.level != {
return quicError(.in.setErrorLocked(errors.New("tls: handshake data received at wrong level")))
}
.quic.readbuf =
<-.quic.signalc
, := <-.quic.blockedc
if {
return nil
}
.handshakeMutex.Lock()
defer .handshakeMutex.Unlock()
.hand.Write(.quic.readbuf)
.quic.readbuf = nil
for .conn.hand.Len() >= 4 && .conn.handshakeErr == nil {
:= .conn.hand.Bytes()
:= int([1])<<16 | int([2])<<8 | int([3])
if > maxHandshake {
.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", , maxHandshake)
break
}
if len() < 4+ {
return nil
}
if := .conn.handlePostHandshakeMessage(); != nil {
.conn.handshakeErr =
}
}
if .conn.handshakeErr != nil {
return quicError(.conn.handshakeErr)
}
return nil
}
type QUICSessionTicketOptions struct {
EarlyData bool
Extra [][]byte
}
func ( *QUICConn) ( QUICSessionTicketOptions) error {
:= .conn
if !.isHandshakeComplete.Load() {
return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
}
if .isClient {
return quicError(errors.New("tls: SendSessionTicket called on the client"))
}
if .sessionTicketSent {
return quicError(errors.New("tls: SendSessionTicket called multiple times"))
}
.sessionTicketSent = true
return quicError(.sendSessionTicket(.EarlyData, .Extra))
}
func ( *QUICConn) ( *SessionState) error {
:= .conn
if !.isClient {
return quicError(errors.New("tls: StoreSessionTicket called on the server"))
}
:= .clientSessionCacheKey()
if == "" {
return nil
}
:= &ClientSessionState{session: }
.config.ClientSessionCache.Put(, )
return nil
}
func ( *QUICConn) () ConnectionState {
return .conn.ConnectionState()
}
func ( *QUICConn) ( []byte) {
if == nil {
= []byte{}
}
.conn.quic.transportParams =
if .conn.quic.started {
<-.conn.quic.signalc
<-.conn.quic.blockedc
}
}
func ( error) error {
if == nil {
return nil
}
var AlertError
if errors.As(, &) {
return
}
var alert
if !errors.As(, &) {
= alertInternalError
}
return fmt.Errorf("%w%.0w", , AlertError())
}
func ( *Conn) ( int) error {
for .hand.Len() < {
if := .quicWaitForSignal(); != nil {
return
}
}
return nil
}
func ( *Conn) ( QUICEncryptionLevel, uint16, []byte) {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICSetReadSecret,
Level: ,
Suite: ,
Data: ,
})
}
func ( *Conn) ( QUICEncryptionLevel, uint16, []byte) {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICSetWriteSecret,
Level: ,
Suite: ,
Data: ,
})
}
func ( *Conn) ( QUICEncryptionLevel, []byte) {
var *QUICEvent
if len(.quic.events) > 0 {
= &.quic.events[len(.quic.events)-1]
}
if == nil || .Kind != QUICWriteData || .Level != {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICWriteData,
Level: ,
})
= &.quic.events[len(.quic.events)-1]
}
.Data = append(.Data, ...)
}
func ( *Conn) ( *SessionState) error {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICResumeSession,
SessionState: ,
})
.quic.waitingForDrain = true
for .quic.waitingForDrain {
if := .quicWaitForSignal(); != nil {
return
}
}
return nil
}
func ( *Conn) ( *SessionState) {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICStoreSession,
SessionState: ,
})
}
func ( *Conn) ( []byte) {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICTransportParameters,
Data: ,
})
}
func ( *Conn) () ([]byte, error) {
if .quic.transportParams == nil {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICTransportParametersRequired,
})
}
for .quic.transportParams == nil {
if := .quicWaitForSignal(); != nil {
return nil,
}
}
return .quic.transportParams, nil
}
func ( *Conn) () {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICHandshakeDone,
})
}
func ( *Conn) () {
.quic.events = append(.quic.events, QUICEvent{
Kind: QUICRejectedEarlyData,
})
}
func ( *Conn) () error {
.handshakeMutex.Unlock()
defer .handshakeMutex.Lock()
select {
case .quic.blockedc <- struct{}{}:
case <-.quic.cancelc:
return .sendAlertLocked(alertCloseNotify)
}
select {
case .quic.signalc <- struct{}{}:
.hand.Write(.quic.readbuf)
.quic.readbuf = nil
case <-.quic.cancelc:
return .sendAlertLocked(alertCloseNotify)
}
return nil
}