 9bdcbe0447
			
		
	
	9bdcbe0447
	
	
	
		
			
			Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			419 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			419 lines
		
	
	
		
			12 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 qtls
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| )
 | |
| 
 | |
| // QUICEncryptionLevel represents a QUIC encryption level used to transmit
 | |
| // handshake messages.
 | |
| type QUICEncryptionLevel int
 | |
| 
 | |
| const (
 | |
| 	QUICEncryptionLevelInitial = QUICEncryptionLevel(iota)
 | |
| 	QUICEncryptionLevelEarly
 | |
| 	QUICEncryptionLevelHandshake
 | |
| 	QUICEncryptionLevelApplication
 | |
| )
 | |
| 
 | |
| func (l QUICEncryptionLevel) String() string {
 | |
| 	switch l {
 | |
| 	case QUICEncryptionLevelInitial:
 | |
| 		return "Initial"
 | |
| 	case QUICEncryptionLevelEarly:
 | |
| 		return "Early"
 | |
| 	case QUICEncryptionLevelHandshake:
 | |
| 		return "Handshake"
 | |
| 	case QUICEncryptionLevelApplication:
 | |
| 		return "Application"
 | |
| 	default:
 | |
| 		return fmt.Sprintf("QUICEncryptionLevel(%v)", int(l))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // A QUICConn represents a connection which uses a QUIC implementation as the underlying
 | |
| // transport as described in RFC 9001.
 | |
| //
 | |
| // Methods of QUICConn are not safe for concurrent use.
 | |
| type QUICConn struct {
 | |
| 	conn *Conn
 | |
| 
 | |
| 	sessionTicketSent bool
 | |
| }
 | |
| 
 | |
| // A QUICConfig configures a QUICConn.
 | |
| type QUICConfig struct {
 | |
| 	TLSConfig   *Config
 | |
| 	ExtraConfig *ExtraConfig
 | |
| }
 | |
| 
 | |
| // A QUICEventKind is a type of operation on a QUIC connection.
 | |
| type QUICEventKind int
 | |
| 
 | |
| const (
 | |
| 	// QUICNoEvent indicates that there are no events available.
 | |
| 	QUICNoEvent QUICEventKind = iota
 | |
| 
 | |
| 	// QUICSetReadSecret and QUICSetWriteSecret provide the read and write
 | |
| 	// secrets for a given encryption level.
 | |
| 	// QUICEvent.Level, QUICEvent.Data, and QUICEvent.Suite are set.
 | |
| 	//
 | |
| 	// Secrets for the Initial encryption level are derived from the initial
 | |
| 	// destination connection ID, and are not provided by the QUICConn.
 | |
| 	QUICSetReadSecret
 | |
| 	QUICSetWriteSecret
 | |
| 
 | |
| 	// QUICWriteData provides data to send to the peer in CRYPTO frames.
 | |
| 	// QUICEvent.Data is set.
 | |
| 	QUICWriteData
 | |
| 
 | |
| 	// QUICTransportParameters provides the peer's QUIC transport parameters.
 | |
| 	// QUICEvent.Data is set.
 | |
| 	QUICTransportParameters
 | |
| 
 | |
| 	// QUICTransportParametersRequired indicates that the caller must provide
 | |
| 	// QUIC transport parameters to send to the peer. The caller should set
 | |
| 	// the transport parameters with QUICConn.SetTransportParameters and call
 | |
| 	// QUICConn.NextEvent again.
 | |
| 	//
 | |
| 	// If transport parameters are set before calling QUICConn.Start, the
 | |
| 	// connection will never generate a QUICTransportParametersRequired event.
 | |
| 	QUICTransportParametersRequired
 | |
| 
 | |
| 	// QUICRejectedEarlyData indicates that the server rejected 0-RTT data even
 | |
| 	// if we offered it. It's returned before QUICEncryptionLevelApplication
 | |
| 	// keys are returned.
 | |
| 	QUICRejectedEarlyData
 | |
| 
 | |
| 	// QUICHandshakeDone indicates that the TLS handshake has completed.
 | |
| 	QUICHandshakeDone
 | |
| )
 | |
| 
 | |
| // A QUICEvent is an event occurring on a QUIC connection.
 | |
| //
 | |
| // The type of event is specified by the Kind field.
 | |
| // The contents of the other fields are kind-specific.
 | |
| type QUICEvent struct {
 | |
| 	Kind QUICEventKind
 | |
| 
 | |
| 	// Set for QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData.
 | |
| 	Level QUICEncryptionLevel
 | |
| 
 | |
| 	// Set for QUICTransportParameters, QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData.
 | |
| 	// The contents are owned by crypto/tls, and are valid until the next NextEvent call.
 | |
| 	Data []byte
 | |
| 
 | |
| 	// Set for QUICSetReadSecret and QUICSetWriteSecret.
 | |
| 	Suite uint16
 | |
| }
 | |
| 
 | |
| type quicState struct {
 | |
| 	events    []QUICEvent
 | |
| 	nextEvent int
 | |
| 
 | |
| 	// eventArr is a statically allocated event array, large enough to handle
 | |
| 	// the usual maximum number of events resulting from a single call: transport
 | |
| 	// parameters, Initial data, Early read secret, Handshake write and read
 | |
| 	// secrets, Handshake data, Application write secret, Application data.
 | |
| 	eventArr [8]QUICEvent
 | |
| 
 | |
| 	started  bool
 | |
| 	signalc  chan struct{}   // handshake data is available to be read
 | |
| 	blockedc chan struct{}   // handshake is waiting for data, closed when done
 | |
| 	cancelc  <-chan struct{} // handshake has been canceled
 | |
| 	cancel   context.CancelFunc
 | |
| 
 | |
| 	// readbuf is shared between HandleData and the handshake goroutine.
 | |
| 	// HandshakeCryptoData passes ownership to the handshake goroutine by
 | |
| 	// reading from signalc, and reclaims ownership by reading from blockedc.
 | |
| 	readbuf []byte
 | |
| 
 | |
| 	transportParams []byte // to send to the peer
 | |
| }
 | |
| 
 | |
| // QUICClient returns a new TLS client side connection using QUICTransport as the
 | |
| // underlying transport. The config cannot be nil.
 | |
| //
 | |
| // The config's MinVersion must be at least TLS 1.3.
 | |
| func QUICClient(config *QUICConfig) *QUICConn {
 | |
| 	return newQUICConn(Client(nil, config.TLSConfig), config.ExtraConfig)
 | |
| }
 | |
| 
 | |
| // QUICServer returns a new TLS server side connection using QUICTransport as the
 | |
| // underlying transport. The config cannot be nil.
 | |
| //
 | |
| // The config's MinVersion must be at least TLS 1.3.
 | |
| func QUICServer(config *QUICConfig) *QUICConn {
 | |
| 	return newQUICConn(Server(nil, config.TLSConfig), config.ExtraConfig)
 | |
| }
 | |
| 
 | |
| func newQUICConn(conn *Conn, extraConfig *ExtraConfig) *QUICConn {
 | |
| 	conn.quic = &quicState{
 | |
| 		signalc:  make(chan struct{}),
 | |
| 		blockedc: make(chan struct{}),
 | |
| 	}
 | |
| 	conn.quic.events = conn.quic.eventArr[:0]
 | |
| 	conn.extraConfig = extraConfig
 | |
| 	return &QUICConn{
 | |
| 		conn: conn,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Start starts the client or server handshake protocol.
 | |
| // It may produce connection events, which may be read with NextEvent.
 | |
| //
 | |
| // Start must be called at most once.
 | |
| func (q *QUICConn) Start(ctx context.Context) error {
 | |
| 	if q.conn.quic.started {
 | |
| 		return quicError(errors.New("tls: Start called more than once"))
 | |
| 	}
 | |
| 	q.conn.quic.started = true
 | |
| 	if q.conn.config.MinVersion < VersionTLS13 {
 | |
| 		return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13"))
 | |
| 	}
 | |
| 	go q.conn.HandshakeContext(ctx)
 | |
| 	if _, ok := <-q.conn.quic.blockedc; !ok {
 | |
| 		return q.conn.handshakeErr
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // NextEvent returns the next event occurring on the connection.
 | |
| // It returns an event with a Kind of QUICNoEvent when no events are available.
 | |
| func (q *QUICConn) NextEvent() QUICEvent {
 | |
| 	qs := q.conn.quic
 | |
| 	if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
 | |
| 		// Write over some of the previous event's data,
 | |
| 		// to catch callers erroniously retaining it.
 | |
| 		qs.events[last].Data[0] = 0
 | |
| 	}
 | |
| 	if qs.nextEvent >= len(qs.events) {
 | |
| 		qs.events = qs.events[:0]
 | |
| 		qs.nextEvent = 0
 | |
| 		return QUICEvent{Kind: QUICNoEvent}
 | |
| 	}
 | |
| 	e := qs.events[qs.nextEvent]
 | |
| 	qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data
 | |
| 	qs.nextEvent++
 | |
| 	return e
 | |
| }
 | |
| 
 | |
| // Close closes the connection and stops any in-progress handshake.
 | |
| func (q *QUICConn) Close() error {
 | |
| 	if q.conn.quic.cancel == nil {
 | |
| 		return nil // never started
 | |
| 	}
 | |
| 	q.conn.quic.cancel()
 | |
| 	for range q.conn.quic.blockedc {
 | |
| 		// Wait for the handshake goroutine to return.
 | |
| 	}
 | |
| 	return q.conn.handshakeErr
 | |
| }
 | |
| 
 | |
| // HandleData handles handshake bytes received from the peer.
 | |
| // It may produce connection events, which may be read with NextEvent.
 | |
| func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
 | |
| 	c := q.conn
 | |
| 	if c.in.level != level {
 | |
| 		return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level")))
 | |
| 	}
 | |
| 	c.quic.readbuf = data
 | |
| 	<-c.quic.signalc
 | |
| 	_, ok := <-c.quic.blockedc
 | |
| 	if ok {
 | |
| 		// The handshake goroutine is waiting for more data.
 | |
| 		return nil
 | |
| 	}
 | |
| 	// The handshake goroutine has exited.
 | |
| 	c.handshakeMutex.Lock()
 | |
| 	defer c.handshakeMutex.Unlock()
 | |
| 	c.hand.Write(c.quic.readbuf)
 | |
| 	c.quic.readbuf = nil
 | |
| 	for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
 | |
| 		b := q.conn.hand.Bytes()
 | |
| 		n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
 | |
| 		if n > maxHandshake {
 | |
| 			q.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)
 | |
| 			break
 | |
| 		}
 | |
| 		if len(b) < 4+n {
 | |
| 			return nil
 | |
| 		}
 | |
| 		if err := q.conn.handlePostHandshakeMessage(); err != nil {
 | |
| 			q.conn.handshakeErr = err
 | |
| 		}
 | |
| 	}
 | |
| 	if q.conn.handshakeErr != nil {
 | |
| 		return quicError(q.conn.handshakeErr)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // SendSessionTicket sends a session ticket to the client.
 | |
| // It produces connection events, which may be read with NextEvent.
 | |
| // Currently, it can only be called once.
 | |
| func (q *QUICConn) SendSessionTicket(earlyData bool) error {
 | |
| 	c := q.conn
 | |
| 	if !c.isHandshakeComplete.Load() {
 | |
| 		return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
 | |
| 	}
 | |
| 	if c.isClient {
 | |
| 		return quicError(errors.New("tls: SendSessionTicket called on the client"))
 | |
| 	}
 | |
| 	if q.sessionTicketSent {
 | |
| 		return quicError(errors.New("tls: SendSessionTicket called multiple times"))
 | |
| 	}
 | |
| 	q.sessionTicketSent = true
 | |
| 	return quicError(c.sendSessionTicket(earlyData))
 | |
| }
 | |
| 
 | |
| // ConnectionState returns basic TLS details about the connection.
 | |
| func (q *QUICConn) ConnectionState() ConnectionState {
 | |
| 	return q.conn.ConnectionState()
 | |
| }
 | |
| 
 | |
| // SetTransportParameters sets the transport parameters to send to the peer.
 | |
| //
 | |
| // Server connections may delay setting the transport parameters until after
 | |
| // receiving the client's transport parameters. See QUICTransportParametersRequired.
 | |
| func (q *QUICConn) SetTransportParameters(params []byte) {
 | |
| 	if params == nil {
 | |
| 		params = []byte{}
 | |
| 	}
 | |
| 	q.conn.quic.transportParams = params
 | |
| 	if q.conn.quic.started {
 | |
| 		<-q.conn.quic.signalc
 | |
| 		<-q.conn.quic.blockedc
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // quicError ensures err is an AlertError.
 | |
| // If err is not already, quicError wraps it with alertInternalError.
 | |
| func quicError(err error) error {
 | |
| 	if err == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	var ae AlertError
 | |
| 	if errors.As(err, &ae) {
 | |
| 		return err
 | |
| 	}
 | |
| 	var a alert
 | |
| 	if !errors.As(err, &a) {
 | |
| 		a = alertInternalError
 | |
| 	}
 | |
| 	// Return an error wrapping the original error and an AlertError.
 | |
| 	// Truncate the text of the alert to 0 characters.
 | |
| 	return fmt.Errorf("%w%.0w", err, AlertError(a))
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicReadHandshakeBytes(n int) error {
 | |
| 	for c.hand.Len() < n {
 | |
| 		if err := c.quicWaitForSignal(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
 | |
| 	c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 		Kind:  QUICSetReadSecret,
 | |
| 		Level: level,
 | |
| 		Suite: suite,
 | |
| 		Data:  secret,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
 | |
| 	c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 		Kind:  QUICSetWriteSecret,
 | |
| 		Level: level,
 | |
| 		Suite: suite,
 | |
| 		Data:  secret,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicWriteCryptoData(level QUICEncryptionLevel, data []byte) {
 | |
| 	var last *QUICEvent
 | |
| 	if len(c.quic.events) > 0 {
 | |
| 		last = &c.quic.events[len(c.quic.events)-1]
 | |
| 	}
 | |
| 	if last == nil || last.Kind != QUICWriteData || last.Level != level {
 | |
| 		c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 			Kind:  QUICWriteData,
 | |
| 			Level: level,
 | |
| 		})
 | |
| 		last = &c.quic.events[len(c.quic.events)-1]
 | |
| 	}
 | |
| 	last.Data = append(last.Data, data...)
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicSetTransportParameters(params []byte) {
 | |
| 	c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 		Kind: QUICTransportParameters,
 | |
| 		Data: params,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicGetTransportParameters() ([]byte, error) {
 | |
| 	if c.quic.transportParams == nil {
 | |
| 		c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 			Kind: QUICTransportParametersRequired,
 | |
| 		})
 | |
| 	}
 | |
| 	for c.quic.transportParams == nil {
 | |
| 		if err := c.quicWaitForSignal(); err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 	return c.quic.transportParams, nil
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicHandshakeComplete() {
 | |
| 	c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 		Kind: QUICHandshakeDone,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func (c *Conn) quicRejectedEarlyData() {
 | |
| 	c.quic.events = append(c.quic.events, QUICEvent{
 | |
| 		Kind: QUICRejectedEarlyData,
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // quicWaitForSignal notifies the QUICConn that handshake progress is blocked,
 | |
| // and waits for a signal that the handshake should proceed.
 | |
| //
 | |
| // The handshake may become blocked waiting for handshake bytes
 | |
| // or for the user to provide transport parameters.
 | |
| func (c *Conn) quicWaitForSignal() error {
 | |
| 	// Drop the handshake mutex while blocked to allow the user
 | |
| 	// to call ConnectionState before the handshake completes.
 | |
| 	c.handshakeMutex.Unlock()
 | |
| 	defer c.handshakeMutex.Lock()
 | |
| 	// Send on blockedc to notify the QUICConn that the handshake is blocked.
 | |
| 	// Exported methods of QUICConn wait for the handshake to become blocked
 | |
| 	// before returning to the user.
 | |
| 	select {
 | |
| 	case c.quic.blockedc <- struct{}{}:
 | |
| 	case <-c.quic.cancelc:
 | |
| 		return c.sendAlertLocked(alertCloseNotify)
 | |
| 	}
 | |
| 	// The QUICConn reads from signalc to notify us that the handshake may
 | |
| 	// be able to proceed. (The QUICConn reads, because we close signalc to
 | |
| 	// indicate that the handshake has completed.)
 | |
| 	select {
 | |
| 	case c.quic.signalc <- struct{}{}:
 | |
| 		c.hand.Write(c.quic.readbuf)
 | |
| 		c.quic.readbuf = nil
 | |
| 	case <-c.quic.cancelc:
 | |
| 		return c.sendAlertLocked(alertCloseNotify)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |