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 5package tls 6 7import ( 8 "context" 9 "errors" 10 "fmt" 11) 12 13// QUICEncryptionLevel represents a QUIC encryption level used to transmit 14// handshake messages. 15type QUICEncryptionLevel int 16 17const ( 18 QUICEncryptionLevelInitial = QUICEncryptionLevel(iota) 19 QUICEncryptionLevelEarly 20 QUICEncryptionLevelHandshake 21 QUICEncryptionLevelApplication 22) 23 24func (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. 43type QUICConn struct { 44 conn *Conn 45 46 sessionTicketSent bool 47} 48 49// A QUICConfig configures a [QUICConn]. 50type 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. 62type QUICEventKind int 63 64const ( 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 122// A QUICEvent is an event occurring on a QUIC connection. 123// 124// The type of event is specified by the Kind field. 125// The contents of the other fields are kind-specific. 126type QUICEvent struct { 127 Kind QUICEventKind 128 129 // Set for QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData. 130 Level QUICEncryptionLevel 131 132 // Set for QUICTransportParameters, QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData. 133 // The contents are owned by crypto/tls, and are valid until the next NextEvent call. 134 Data []byte 135 136 // Set for QUICSetReadSecret and QUICSetWriteSecret. 137 Suite uint16 138 139 // Set for QUICResumeSession and QUICStoreSession. 140 SessionState *SessionState 141} 142 143type quicState struct { 144 events []QUICEvent 145 nextEvent int 146 147 // eventArr is a statically allocated event array, large enough to handle 148 // the usual maximum number of events resulting from a single call: transport 149 // parameters, Initial data, Early read secret, Handshake write and read 150 // secrets, Handshake data, Application write secret, Application data. 151 eventArr [8]QUICEvent 152 153 started bool 154 signalc chan struct{} // handshake data is available to be read 155 blockedc chan struct{} // handshake is waiting for data, closed when done 156 cancelc <-chan struct{} // handshake has been canceled 157 cancel context.CancelFunc 158 159 waitingForDrain bool 160 161 // readbuf is shared between HandleData and the handshake goroutine. 162 // HandshakeCryptoData passes ownership to the handshake goroutine by 163 // reading from signalc, and reclaims ownership by reading from blockedc. 164 readbuf []byte 165 166 transportParams []byte // to send to the peer 167 168 enableSessionEvents bool 169} 170 171// QUICClient returns a new TLS client side connection using QUICTransport as the 172// underlying transport. The config cannot be nil. 173// 174// The config's MinVersion must be at least TLS 1.3. 175func QUICClient(config *QUICConfig) *QUICConn { 176 return newQUICConn(Client(nil, config.TLSConfig), config) 177} 178 179// QUICServer returns a new TLS server side connection using QUICTransport as the 180// underlying transport. The config cannot be nil. 181// 182// The config's MinVersion must be at least TLS 1.3. 183func QUICServer(config *QUICConfig) *QUICConn { 184 return newQUICConn(Server(nil, config.TLSConfig), config) 185} 186 187func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn { 188 conn.quic = &quicState{ 189 signalc: make(chan struct{}), 190 blockedc: make(chan struct{}), 191 enableSessionEvents: config.EnableSessionEvents, 192 } 193 conn.quic.events = conn.quic.eventArr[:0] 194 return &QUICConn{ 195 conn: conn, 196 } 197} 198 199// Start starts the client or server handshake protocol. 200// It may produce connection events, which may be read with [QUICConn.NextEvent]. 201// 202// Start must be called at most once. 203func (q *QUICConn) Start(ctx context.Context) error { 204 if q.conn.quic.started { 205 return quicError(errors.New("tls: Start called more than once")) 206 } 207 q.conn.quic.started = true 208 if q.conn.config.MinVersion < VersionTLS13 { 209 return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13")) 210 } 211 go q.conn.HandshakeContext(ctx) 212 if _, ok := <-q.conn.quic.blockedc; !ok { 213 return q.conn.handshakeErr 214 } 215 return nil 216} 217 218// NextEvent returns the next event occurring on the connection. 219// It returns an event with a Kind of [QUICNoEvent] when no events are available. 220func (q *QUICConn) NextEvent() QUICEvent { 221 qs := q.conn.quic 222 if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 { 223 // Write over some of the previous event's data, 224 // to catch callers erroniously retaining it. 225 qs.events[last].Data[0] = 0 226 } 227 if qs.nextEvent >= len(qs.events) && qs.waitingForDrain { 228 qs.waitingForDrain = false 229 <-qs.signalc 230 <-qs.blockedc 231 } 232 if qs.nextEvent >= len(qs.events) { 233 qs.events = qs.events[:0] 234 qs.nextEvent = 0 235 return QUICEvent{Kind: QUICNoEvent} 236 } 237 e := qs.events[qs.nextEvent] 238 qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data 239 qs.nextEvent++ 240 return e 241} 242 243// Close closes the connection and stops any in-progress handshake. 244func (q *QUICConn) Close() error { 245 if q.conn.quic.cancel == nil { 246 return nil // never started 247 } 248 q.conn.quic.cancel() 249 for range q.conn.quic.blockedc { 250 // Wait for the handshake goroutine to return. 251 } 252 return q.conn.handshakeErr 253} 254 255// HandleData handles handshake bytes received from the peer. 256// It may produce connection events, which may be read with [QUICConn.NextEvent]. 257func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error { 258 c := q.conn 259 if c.in.level != level { 260 return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level"))) 261 } 262 c.quic.readbuf = data 263 <-c.quic.signalc 264 _, ok := <-c.quic.blockedc 265 if ok { 266 // The handshake goroutine is waiting for more data. 267 return nil 268 } 269 // The handshake goroutine has exited. 270 c.handshakeMutex.Lock() 271 defer c.handshakeMutex.Unlock() 272 c.hand.Write(c.quic.readbuf) 273 c.quic.readbuf = nil 274 for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil { 275 b := q.conn.hand.Bytes() 276 n := int(b[1])<<16 | int(b[2])<<8 | int(b[3]) 277 if n > maxHandshake { 278 q.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake) 279 break 280 } 281 if len(b) < 4+n { 282 return nil 283 } 284 if err := q.conn.handlePostHandshakeMessage(); err != nil { 285 q.conn.handshakeErr = err 286 } 287 } 288 if q.conn.handshakeErr != nil { 289 return quicError(q.conn.handshakeErr) 290 } 291 return nil 292} 293 294type QUICSessionTicketOptions struct { 295 // EarlyData specifies whether the ticket may be used for 0-RTT. 296 EarlyData bool 297 Extra [][]byte 298} 299 300// SendSessionTicket sends a session ticket to the client. 301// It produces connection events, which may be read with [QUICConn.NextEvent]. 302// Currently, it can only be called once. 303func (q *QUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error { 304 c := q.conn 305 if !c.isHandshakeComplete.Load() { 306 return quicError(errors.New("tls: SendSessionTicket called before handshake completed")) 307 } 308 if c.isClient { 309 return quicError(errors.New("tls: SendSessionTicket called on the client")) 310 } 311 if q.sessionTicketSent { 312 return quicError(errors.New("tls: SendSessionTicket called multiple times")) 313 } 314 q.sessionTicketSent = true 315 return quicError(c.sendSessionTicket(opts.EarlyData, opts.Extra)) 316} 317 318// StoreSession stores a session previously received in a QUICStoreSession event 319// in the ClientSessionCache. 320// The application may process additional events or modify the SessionState 321// before storing the session. 322func (q *QUICConn) StoreSession(session *SessionState) error { 323 c := q.conn 324 if !c.isClient { 325 return quicError(errors.New("tls: StoreSessionTicket called on the server")) 326 } 327 cacheKey := c.clientSessionCacheKey() 328 if cacheKey == "" { 329 return nil 330 } 331 cs := &ClientSessionState{session: session} 332 c.config.ClientSessionCache.Put(cacheKey, cs) 333 return nil 334} 335 336// ConnectionState returns basic TLS details about the connection. 337func (q *QUICConn) ConnectionState() ConnectionState { 338 return q.conn.ConnectionState() 339} 340 341// SetTransportParameters sets the transport parameters to send to the peer. 342// 343// Server connections may delay setting the transport parameters until after 344// receiving the client's transport parameters. See [QUICTransportParametersRequired]. 345func (q *QUICConn) SetTransportParameters(params []byte) { 346 if params == nil { 347 params = []byte{} 348 } 349 q.conn.quic.transportParams = params 350 if q.conn.quic.started { 351 <-q.conn.quic.signalc 352 <-q.conn.quic.blockedc 353 } 354} 355 356// quicError ensures err is an AlertError. 357// If err is not already, quicError wraps it with alertInternalError. 358func quicError(err error) error { 359 if err == nil { 360 return nil 361 } 362 var ae AlertError 363 if errors.As(err, &ae) { 364 return err 365 } 366 var a alert 367 if !errors.As(err, &a) { 368 a = alertInternalError 369 } 370 // Return an error wrapping the original error and an AlertError. 371 // Truncate the text of the alert to 0 characters. 372 return fmt.Errorf("%w%.0w", err, AlertError(a)) 373} 374 375func (c *Conn) quicReadHandshakeBytes(n int) error { 376 for c.hand.Len() < n { 377 if err := c.quicWaitForSignal(); err != nil { 378 return err 379 } 380 } 381 return nil 382} 383 384func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) { 385 c.quic.events = append(c.quic.events, QUICEvent{ 386 Kind: QUICSetReadSecret, 387 Level: level, 388 Suite: suite, 389 Data: secret, 390 }) 391} 392 393func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) { 394 c.quic.events = append(c.quic.events, QUICEvent{ 395 Kind: QUICSetWriteSecret, 396 Level: level, 397 Suite: suite, 398 Data: secret, 399 }) 400} 401 402func (c *Conn) quicWriteCryptoData(level QUICEncryptionLevel, data []byte) { 403 var last *QUICEvent 404 if len(c.quic.events) > 0 { 405 last = &c.quic.events[len(c.quic.events)-1] 406 } 407 if last == nil || last.Kind != QUICWriteData || last.Level != level { 408 c.quic.events = append(c.quic.events, QUICEvent{ 409 Kind: QUICWriteData, 410 Level: level, 411 }) 412 last = &c.quic.events[len(c.quic.events)-1] 413 } 414 last.Data = append(last.Data, data...) 415} 416 417func (c *Conn) quicResumeSession(session *SessionState) error { 418 c.quic.events = append(c.quic.events, QUICEvent{ 419 Kind: QUICResumeSession, 420 SessionState: session, 421 }) 422 c.quic.waitingForDrain = true 423 for c.quic.waitingForDrain { 424 if err := c.quicWaitForSignal(); err != nil { 425 return err 426 } 427 } 428 return nil 429} 430 431func (c *Conn) quicStoreSession(session *SessionState) { 432 c.quic.events = append(c.quic.events, QUICEvent{ 433 Kind: QUICStoreSession, 434 SessionState: session, 435 }) 436} 437 438func (c *Conn) quicSetTransportParameters(params []byte) { 439 c.quic.events = append(c.quic.events, QUICEvent{ 440 Kind: QUICTransportParameters, 441 Data: params, 442 }) 443} 444 445func (c *Conn) quicGetTransportParameters() ([]byte, error) { 446 if c.quic.transportParams == nil { 447 c.quic.events = append(c.quic.events, QUICEvent{ 448 Kind: QUICTransportParametersRequired, 449 }) 450 } 451 for c.quic.transportParams == nil { 452 if err := c.quicWaitForSignal(); err != nil { 453 return nil, err 454 } 455 } 456 return c.quic.transportParams, nil 457} 458 459func (c *Conn) quicHandshakeComplete() { 460 c.quic.events = append(c.quic.events, QUICEvent{ 461 Kind: QUICHandshakeDone, 462 }) 463} 464 465func (c *Conn) quicRejectedEarlyData() { 466 c.quic.events = append(c.quic.events, QUICEvent{ 467 Kind: QUICRejectedEarlyData, 468 }) 469} 470 471// quicWaitForSignal notifies the QUICConn that handshake progress is blocked, 472// and waits for a signal that the handshake should proceed. 473// 474// The handshake may become blocked waiting for handshake bytes 475// or for the user to provide transport parameters. 476func (c *Conn) quicWaitForSignal() error { 477 // Drop the handshake mutex while blocked to allow the user 478 // to call ConnectionState before the handshake completes. 479 c.handshakeMutex.Unlock() 480 defer c.handshakeMutex.Lock() 481 // Send on blockedc to notify the QUICConn that the handshake is blocked. 482 // Exported methods of QUICConn wait for the handshake to become blocked 483 // before returning to the user. 484 select { 485 case c.quic.blockedc <- struct{}{}: 486 case <-c.quic.cancelc: 487 return c.sendAlertLocked(alertCloseNotify) 488 } 489 // The QUICConn reads from signalc to notify us that the handshake may 490 // be able to proceed. (The QUICConn reads, because we close signalc to 491 // indicate that the handshake has completed.) 492 select { 493 case c.quic.signalc <- struct{}{}: 494 c.hand.Write(c.quic.readbuf) 495 c.quic.readbuf = nil 496 case <-c.quic.cancelc: 497 return c.sendAlertLocked(alertCloseNotify) 498 } 499 return nil 500} 501