quic.mx raw

   1  // Copyright 2023 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package tls
   6  
   7  import (
   8  	"context"
   9  	"errors"
  10  	"fmt"
  11  )
  12  
  13  // QUICEncryptionLevel represents a QUIC encryption level used to transmit
  14  // handshake messages.
  15  type QUICEncryptionLevel int
  16  
  17  const (
  18  	QUICEncryptionLevelInitial = QUICEncryptionLevel(iota)
  19  	QUICEncryptionLevelEarly
  20  	QUICEncryptionLevelHandshake
  21  	QUICEncryptionLevelApplication
  22  )
  23  
  24  func (l QUICEncryptionLevel) String() string {
  25  	switch l {
  26  	case QUICEncryptionLevelInitial:
  27  		return "Initial"
  28  	case QUICEncryptionLevelEarly:
  29  		return "Early"
  30  	case QUICEncryptionLevelHandshake:
  31  		return "Handshake"
  32  	case QUICEncryptionLevelApplication:
  33  		return "Application"
  34  	default:
  35  		return fmt.Sprintf("QUICEncryptionLevel(%v)", int(l))
  36  	}
  37  }
  38  
  39  // A QUICConn represents a connection which uses a QUIC implementation as the underlying
  40  // transport as described in RFC 9001.
  41  //
  42  // Methods of QUICConn are not safe for concurrent use.
  43  type QUICConn struct {
  44  	conn *Conn
  45  
  46  	sessionTicketSent bool
  47  }
  48  
  49  // A QUICConfig configures a [QUICConn].
  50  type QUICConfig struct {
  51  	TLSConfig *Config
  52  
  53  	// EnableSessionEvents may be set to true to enable the
  54  	// [QUICStoreSession] and [QUICResumeSession] events for client connections.
  55  	// When this event is enabled, sessions are not automatically
  56  	// stored in the client session cache.
  57  	// The application should use [QUICConn.StoreSession] to store sessions.
  58  	EnableSessionEvents bool
  59  }
  60  
  61  // A QUICEventKind is a type of operation on a QUIC connection.
  62  type QUICEventKind int
  63  
  64  const (
  65  	// QUICNoEvent indicates that there are no events available.
  66  	QUICNoEvent QUICEventKind = iota
  67  
  68  	// QUICSetReadSecret and QUICSetWriteSecret provide the read and write
  69  	// secrets for a given encryption level.
  70  	// QUICEvent.Level, QUICEvent.Data, and QUICEvent.Suite are set.
  71  	//
  72  	// Secrets for the Initial encryption level are derived from the initial
  73  	// destination connection ID, and are not provided by the QUICConn.
  74  	QUICSetReadSecret
  75  	QUICSetWriteSecret
  76  
  77  	// QUICWriteData provides data to send to the peer in CRYPTO frames.
  78  	// QUICEvent.Data is set.
  79  	QUICWriteData
  80  
  81  	// QUICTransportParameters provides the peer's QUIC transport parameters.
  82  	// QUICEvent.Data is set.
  83  	QUICTransportParameters
  84  
  85  	// QUICTransportParametersRequired indicates that the caller must provide
  86  	// QUIC transport parameters to send to the peer. The caller should set
  87  	// the transport parameters with QUICConn.SetTransportParameters and call
  88  	// QUICConn.NextEvent again.
  89  	//
  90  	// If transport parameters are set before calling QUICConn.Start, the
  91  	// connection will never generate a QUICTransportParametersRequired event.
  92  	QUICTransportParametersRequired
  93  
  94  	// QUICRejectedEarlyData indicates that the server rejected 0-RTT data even
  95  	// if we offered it. It's returned before QUICEncryptionLevelApplication
  96  	// keys are returned.
  97  	// This event only occurs on client connections.
  98  	QUICRejectedEarlyData
  99  
 100  	// QUICHandshakeDone indicates that the TLS handshake has completed.
 101  	QUICHandshakeDone
 102  
 103  	// QUICResumeSession indicates that a client is attempting to resume a previous session.
 104  	// [QUICEvent.SessionState] is set.
 105  	//
 106  	// For client connections, this event occurs when the session ticket is selected.
 107  	// For server connections, this event occurs when receiving the client's session ticket.
 108  	//
 109  	// The application may set [QUICEvent.SessionState.EarlyData] to false before the
 110  	// next call to [QUICConn.NextEvent] to decline 0-RTT even if the session supports it.
 111  	QUICResumeSession
 112  
 113  	// QUICStoreSession indicates that the server has provided state permitting
 114  	// the client to resume the session.
 115  	// [QUICEvent.SessionState] is set.
 116  	// The application should use [QUICConn.StoreSession] session to store the [SessionState].
 117  	// The application may modify the [SessionState] before storing it.
 118  	// This event only occurs on client connections.
 119  	QUICStoreSession
 120  
 121  	// QUICErrorEvent indicates that a fatal error has occurred.
 122  	// The handshake cannot proceed and the connection must be closed.
 123  	// QUICEvent.Err is set.
 124  	QUICErrorEvent
 125  )
 126  
 127  // A QUICEvent is an event occurring on a QUIC connection.
 128  //
 129  // The type of event is specified by the Kind field.
 130  // The contents of the other fields are kind-specific.
 131  type QUICEvent struct {
 132  	Kind QUICEventKind
 133  
 134  	// Set for QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData.
 135  	Level QUICEncryptionLevel
 136  
 137  	// Set for QUICTransportParameters, QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData.
 138  	// The contents are owned by crypto/tls, and are valid until the next NextEvent call.
 139  	Data []byte
 140  
 141  	// Set for QUICSetReadSecret and QUICSetWriteSecret.
 142  	Suite uint16
 143  
 144  	// Set for QUICResumeSession and QUICStoreSession.
 145  	SessionState *SessionState
 146  
 147  	// Set for QUICErrorEvent.
 148  	// The error will wrap AlertError.
 149  	Err error
 150  }
 151  
 152  type quicState struct {
 153  	events    []QUICEvent
 154  	nextEvent int
 155  
 156  	// eventArr is a statically allocated event array, large enough to handle
 157  	// the usual maximum number of events resulting from a single call: transport
 158  	// parameters, Initial data, Early read secret, Handshake write and read
 159  	// secrets, Handshake data, Application write secret, Application data.
 160  	eventArr [8]QUICEvent
 161  
 162  	started  bool
 163  	signalc  chan struct{}   // handshake data is available to be read
 164  	blockedc chan struct{}   // handshake is waiting for data, closed when done
 165  	ctx      context.Context // handshake context
 166  	cancel   context.CancelFunc
 167  
 168  	waitingForDrain bool
 169  	errorReturned   bool
 170  
 171  	// readbuf is shared between HandleData and the handshake goroutine.
 172  	// HandshakeCryptoData passes ownership to the handshake goroutine by
 173  	// reading from signalc, and reclaims ownership by reading from blockedc.
 174  	readbuf []byte
 175  
 176  	transportParams []byte // to send to the peer
 177  
 178  	enableSessionEvents bool
 179  }
 180  
 181  // QUICClient returns a new TLS client side connection using QUICTransport as the
 182  // underlying transport. The config cannot be nil.
 183  //
 184  // The config's MinVersion must be at least TLS 1.3.
 185  func QUICClient(config *QUICConfig) *QUICConn {
 186  	return newQUICConn(Client(nil, config.TLSConfig), config)
 187  }
 188  
 189  // QUICServer returns a new TLS server side connection using QUICTransport as the
 190  // underlying transport. The config cannot be nil.
 191  //
 192  // The config's MinVersion must be at least TLS 1.3.
 193  func QUICServer(config *QUICConfig) *QUICConn {
 194  	return newQUICConn(Server(nil, config.TLSConfig), config)
 195  }
 196  
 197  func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn {
 198  	conn.quic = &quicState{
 199  		signalc:             chan struct{}{},
 200  		blockedc:            chan struct{}{},
 201  		enableSessionEvents: config.EnableSessionEvents,
 202  	}
 203  	conn.quic.events = conn.quic.eventArr[:0]
 204  	return &QUICConn{
 205  		conn: conn,
 206  	}
 207  }
 208  
 209  // Start starts the client or server handshake protocol.
 210  // It may produce connection events, which may be read with [QUICConn.NextEvent].
 211  //
 212  // Start must be called at most once.
 213  func (q *QUICConn) Start(ctx context.Context) error {
 214  	if q.conn.quic.started {
 215  		return quicError(errors.New("tls: Start called more than once"))
 216  	}
 217  	q.conn.quic.started = true
 218  	if q.conn.config.MinVersion < VersionTLS13 {
 219  		return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3"))
 220  	}
 221  	// moxie: no goroutines, run synchronously.
 222  	// QUIC is not used in moxie; this is a stub.
 223  	q.conn.HandshakeContext(ctx)
 224  	if _, ok := <-q.conn.quic.blockedc; !ok {
 225  		return q.conn.handshakeErr
 226  	}
 227  	return nil
 228  }
 229  
 230  // NextEvent returns the next event occurring on the connection.
 231  // It returns an event with a Kind of [QUICNoEvent] when no events are available.
 232  func (q *QUICConn) NextEvent() QUICEvent {
 233  	qs := q.conn.quic
 234  	if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
 235  		// Write over some of the previous event's data,
 236  		// to catch callers erroneously retaining it.
 237  		qs.events[last].Data[0] = 0
 238  	}
 239  	if qs.nextEvent >= len(qs.events) && qs.waitingForDrain {
 240  		qs.waitingForDrain = false
 241  		<-qs.signalc
 242  		<-qs.blockedc
 243  	}
 244  	if err := q.conn.handshakeErr; err != nil {
 245  		if qs.errorReturned {
 246  			return QUICEvent{Kind: QUICNoEvent}
 247  		}
 248  		qs.errorReturned = true
 249  		qs.events = nil
 250  		qs.nextEvent = 0
 251  		return QUICEvent{Kind: QUICErrorEvent, Err: q.conn.handshakeErr}
 252  	}
 253  	if qs.nextEvent >= len(qs.events) {
 254  		qs.events = qs.events[:0]
 255  		qs.nextEvent = 0
 256  		return QUICEvent{Kind: QUICNoEvent}
 257  	}
 258  	e := qs.events[qs.nextEvent]
 259  	qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data
 260  	qs.nextEvent++
 261  	return e
 262  }
 263  
 264  // Close closes the connection and stops any in-progress handshake.
 265  func (q *QUICConn) Close() error {
 266  	if q.conn.quic.ctx == nil {
 267  		return nil // never started
 268  	}
 269  	q.conn.quic.cancel()
 270  	<-q.conn.quic.signalc
 271  	for range q.conn.quic.blockedc {
 272  		// Wait for the handshake goroutine to return.
 273  	}
 274  	return q.conn.handshakeErr
 275  }
 276  
 277  // HandleData handles handshake bytes received from the peer.
 278  // It may produce connection events, which may be read with [QUICConn.NextEvent].
 279  func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
 280  	c := q.conn
 281  	if c.in.level != level {
 282  		return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level")))
 283  	}
 284  	c.quic.readbuf = data
 285  	<-c.quic.signalc
 286  	_, ok := <-c.quic.blockedc
 287  	if ok {
 288  		// The handshake goroutine is waiting for more data.
 289  		return nil
 290  	}
 291  	// The handshake goroutine has exited.
 292  	c.handshakeMutex.Lock()
 293  	defer c.handshakeMutex.Unlock()
 294  	c.hand.Write(c.quic.readbuf)
 295  	c.quic.readbuf = nil
 296  	for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
 297  		b := q.conn.hand.Bytes()
 298  		n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
 299  		if n > maxHandshake {
 300  			q.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)
 301  			break
 302  		}
 303  		if len(b) < 4+n {
 304  			return nil
 305  		}
 306  		if err := q.conn.handlePostHandshakeMessage(); err != nil {
 307  			q.conn.handshakeErr = err
 308  		}
 309  	}
 310  	if q.conn.handshakeErr != nil {
 311  		return quicError(q.conn.handshakeErr)
 312  	}
 313  	return nil
 314  }
 315  
 316  type QUICSessionTicketOptions struct {
 317  	// EarlyData specifies whether the ticket may be used for 0-RTT.
 318  	EarlyData bool
 319  	Extra     [][]byte
 320  }
 321  
 322  // SendSessionTicket sends a session ticket to the client.
 323  // It produces connection events, which may be read with [QUICConn.NextEvent].
 324  // Currently, it can only be called once.
 325  func (q *QUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error {
 326  	c := q.conn
 327  	if c.config.SessionTicketsDisabled {
 328  		return nil
 329  	}
 330  	if !c.isHandshakeComplete.Load() {
 331  		return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
 332  	}
 333  	if c.isClient {
 334  		return quicError(errors.New("tls: SendSessionTicket called on the client"))
 335  	}
 336  	if q.sessionTicketSent {
 337  		return quicError(errors.New("tls: SendSessionTicket called multiple times"))
 338  	}
 339  	q.sessionTicketSent = true
 340  	return quicError(c.sendSessionTicket(opts.EarlyData, opts.Extra))
 341  }
 342  
 343  // StoreSession stores a session previously received in a QUICStoreSession event
 344  // in the ClientSessionCache.
 345  // The application may process additional events or modify the SessionState
 346  // before storing the session.
 347  func (q *QUICConn) StoreSession(session *SessionState) error {
 348  	c := q.conn
 349  	if !c.isClient {
 350  		return quicError(errors.New("tls: StoreSessionTicket called on the server"))
 351  	}
 352  	cacheKey := c.clientSessionCacheKey()
 353  	if cacheKey == "" {
 354  		return nil
 355  	}
 356  	cs := &ClientSessionState{session: session}
 357  	c.config.ClientSessionCache.Put(cacheKey, cs)
 358  	return nil
 359  }
 360  
 361  // ConnectionState returns basic TLS details about the connection.
 362  func (q *QUICConn) ConnectionState() ConnectionState {
 363  	return q.conn.ConnectionState()
 364  }
 365  
 366  // SetTransportParameters sets the transport parameters to send to the peer.
 367  //
 368  // Server connections may delay setting the transport parameters until after
 369  // receiving the client's transport parameters. See [QUICTransportParametersRequired].
 370  func (q *QUICConn) SetTransportParameters(params []byte) {
 371  	if params == nil {
 372  		params = []byte{}
 373  	}
 374  	q.conn.quic.transportParams = params
 375  	if q.conn.quic.started {
 376  		<-q.conn.quic.signalc
 377  		<-q.conn.quic.blockedc
 378  	}
 379  }
 380  
 381  // quicError ensures err is an AlertError.
 382  // If err is not already, quicError wraps it with alertInternalError.
 383  func quicError(err error) error {
 384  	if err == nil {
 385  		return nil
 386  	}
 387  	if _, ok := err.(AlertError); ok {
 388  		return err
 389  	}
 390  	a, ok := err.(alert)
 391  	if !ok {
 392  		a = alertInternalError
 393  	}
 394  	// Return an error wrapping the original error and an AlertError.
 395  	// Truncate the text of the alert to 0 characters.
 396  	return fmt.Errorf("%w%.0w", err, AlertError(a))
 397  }
 398  
 399  func (c *Conn) quicReadHandshakeBytes(n int) error {
 400  	for c.hand.Len() < n {
 401  		if err := c.quicWaitForSignal(); err != nil {
 402  			return err
 403  		}
 404  	}
 405  	return nil
 406  }
 407  
 408  func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) error {
 409  	// Ensure that there are no buffered handshake messages before changing the
 410  	// read keys, since that can cause messages to be parsed that were encrypted
 411  	// using old keys which are no longer appropriate.
 412  	// TODO(roland): we should merge this check with the similar one in setReadTrafficSecret.
 413  	if c.hand.Len() != 0 {
 414  		c.sendAlert(alertUnexpectedMessage)
 415  		return errors.New("tls: handshake buffer not empty before setting read traffic secret")
 416  	}
 417  	c.quic.events = append(c.quic.events, QUICEvent{
 418  		Kind:  QUICSetReadSecret,
 419  		Level: level,
 420  		Suite: suite,
 421  		Data:  secret,
 422  	})
 423  	return nil
 424  }
 425  
 426  func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
 427  	c.quic.events = append(c.quic.events, QUICEvent{
 428  		Kind:  QUICSetWriteSecret,
 429  		Level: level,
 430  		Suite: suite,
 431  		Data:  secret,
 432  	})
 433  }
 434  
 435  func (c *Conn) quicWriteCryptoData(level QUICEncryptionLevel, data []byte) {
 436  	var last *QUICEvent
 437  	if len(c.quic.events) > 0 {
 438  		last = &c.quic.events[len(c.quic.events)-1]
 439  	}
 440  	if last == nil || last.Kind != QUICWriteData || last.Level != level {
 441  		c.quic.events = append(c.quic.events, QUICEvent{
 442  			Kind:  QUICWriteData,
 443  			Level: level,
 444  		})
 445  		last = &c.quic.events[len(c.quic.events)-1]
 446  	}
 447  	last.Data = append(last.Data, data...)
 448  }
 449  
 450  func (c *Conn) quicResumeSession(session *SessionState) error {
 451  	c.quic.events = append(c.quic.events, QUICEvent{
 452  		Kind:         QUICResumeSession,
 453  		SessionState: session,
 454  	})
 455  	c.quic.waitingForDrain = true
 456  	for c.quic.waitingForDrain {
 457  		if err := c.quicWaitForSignal(); err != nil {
 458  			return err
 459  		}
 460  	}
 461  	return nil
 462  }
 463  
 464  func (c *Conn) quicStoreSession(session *SessionState) {
 465  	c.quic.events = append(c.quic.events, QUICEvent{
 466  		Kind:         QUICStoreSession,
 467  		SessionState: session,
 468  	})
 469  }
 470  
 471  func (c *Conn) quicSetTransportParameters(params []byte) {
 472  	c.quic.events = append(c.quic.events, QUICEvent{
 473  		Kind: QUICTransportParameters,
 474  		Data: params,
 475  	})
 476  }
 477  
 478  func (c *Conn) quicGetTransportParameters() ([]byte, error) {
 479  	if c.quic.transportParams == nil {
 480  		c.quic.events = append(c.quic.events, QUICEvent{
 481  			Kind: QUICTransportParametersRequired,
 482  		})
 483  	}
 484  	for c.quic.transportParams == nil {
 485  		if err := c.quicWaitForSignal(); err != nil {
 486  			return nil, err
 487  		}
 488  	}
 489  	return c.quic.transportParams, nil
 490  }
 491  
 492  func (c *Conn) quicHandshakeComplete() {
 493  	c.quic.events = append(c.quic.events, QUICEvent{
 494  		Kind: QUICHandshakeDone,
 495  	})
 496  }
 497  
 498  func (c *Conn) quicRejectedEarlyData() {
 499  	c.quic.events = append(c.quic.events, QUICEvent{
 500  		Kind: QUICRejectedEarlyData,
 501  	})
 502  }
 503  
 504  // quicWaitForSignal notifies the QUICConn that handshake progress is blocked,
 505  // and waits for a signal that the handshake should proceed.
 506  //
 507  // The handshake may become blocked waiting for handshake bytes
 508  // or for the user to provide transport parameters.
 509  func (c *Conn) quicWaitForSignal() error {
 510  	// Drop the handshake mutex while blocked to allow the user
 511  	// to call ConnectionState before the handshake completes.
 512  	c.handshakeMutex.Unlock()
 513  	defer c.handshakeMutex.Lock()
 514  	// Send on blockedc to notify the QUICConn that the handshake is blocked.
 515  	// Exported methods of QUICConn wait for the handshake to become blocked
 516  	// before returning to the user.
 517  	c.quic.blockedc <- struct{}{}
 518  	// The QUICConn reads from signalc to notify us that the handshake may
 519  	// be able to proceed. (The QUICConn reads, because we close signalc to
 520  	// indicate that the handshake has completed.)
 521  	c.quic.signalc <- struct{}{}
 522  	if c.quic.ctx.Err() != nil {
 523  		// The connection has been canceled.
 524  		return c.sendAlertLocked(alertCloseNotify)
 525  	}
 526  	c.hand.Write(c.quic.readbuf)
 527  	c.quic.readbuf = nil
 528  	return nil
 529  }
 530