github.com/3andne/restls-client-go@v0.1.6/quic.go (about) 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 54 // A QUICEventKind is a type of operation on a QUIC connection. 55 type QUICEventKind int 56 57 const ( 58 // QUICNoEvent indicates that there are no events available. 59 QUICNoEvent QUICEventKind = iota 60 61 // QUICSetReadSecret and QUICSetWriteSecret provide the read and write 62 // secrets for a given encryption level. 63 // QUICEvent.Level, QUICEvent.Data, and QUICEvent.Suite are set. 64 // 65 // Secrets for the Initial encryption level are derived from the initial 66 // destination connection ID, and are not provided by the QUICConn. 67 QUICSetReadSecret 68 QUICSetWriteSecret 69 70 // QUICWriteData provides data to send to the peer in CRYPTO frames. 71 // QUICEvent.Data is set. 72 QUICWriteData 73 74 // QUICTransportParameters provides the peer's QUIC transport parameters. 75 // QUICEvent.Data is set. 76 QUICTransportParameters 77 78 // QUICTransportParametersRequired indicates that the caller must provide 79 // QUIC transport parameters to send to the peer. The caller should set 80 // the transport parameters with QUICConn.SetTransportParameters and call 81 // QUICConn.NextEvent again. 82 // 83 // If transport parameters are set before calling QUICConn.Start, the 84 // connection will never generate a QUICTransportParametersRequired event. 85 QUICTransportParametersRequired 86 87 // QUICRejectedEarlyData indicates that the server rejected 0-RTT data even 88 // if we offered it. It's returned before QUICEncryptionLevelApplication 89 // keys are returned. 90 QUICRejectedEarlyData 91 92 // QUICHandshakeDone indicates that the TLS handshake has completed. 93 QUICHandshakeDone 94 ) 95 96 // A QUICEvent is an event occurring on a QUIC connection. 97 // 98 // The type of event is specified by the Kind field. 99 // The contents of the other fields are kind-specific. 100 type QUICEvent struct { 101 Kind QUICEventKind 102 103 // Set for QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData. 104 Level QUICEncryptionLevel 105 106 // Set for QUICTransportParameters, QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData. 107 // The contents are owned by crypto/tls, and are valid until the next NextEvent call. 108 Data []byte 109 110 // Set for QUICSetReadSecret and QUICSetWriteSecret. 111 Suite uint16 112 } 113 114 type quicState struct { 115 events []QUICEvent 116 nextEvent int 117 118 // eventArr is a statically allocated event array, large enough to handle 119 // the usual maximum number of events resulting from a single call: transport 120 // parameters, Initial data, Early read secret, Handshake write and read 121 // secrets, Handshake data, Application write secret, Application data. 122 eventArr [8]QUICEvent 123 124 started bool 125 signalc chan struct{} // handshake data is available to be read 126 blockedc chan struct{} // handshake is waiting for data, closed when done 127 cancelc <-chan struct{} // handshake has been canceled 128 cancel context.CancelFunc 129 130 // readbuf is shared between HandleData and the handshake goroutine. 131 // HandshakeCryptoData passes ownership to the handshake goroutine by 132 // reading from signalc, and reclaims ownership by reading from blockedc. 133 readbuf []byte 134 135 transportParams []byte // to send to the peer 136 } 137 138 // QUICClient returns a new TLS client side connection using QUICTransport as the 139 // underlying transport. The config cannot be nil. 140 // 141 // The config's MinVersion must be at least TLS 1.3. 142 func QUICClient(config *QUICConfig) *QUICConn { 143 return newQUICConn(Client(nil, config.TLSConfig)) 144 } 145 146 // QUICServer returns a new TLS server side connection using QUICTransport as the 147 // underlying transport. The config cannot be nil. 148 // 149 // The config's MinVersion must be at least TLS 1.3. 150 func QUICServer(config *QUICConfig) *QUICConn { 151 return newQUICConn(Server(nil, config.TLSConfig)) 152 } 153 154 func newQUICConn(conn *Conn) *QUICConn { 155 conn.quic = &quicState{ 156 signalc: make(chan struct{}), 157 blockedc: make(chan struct{}), 158 } 159 conn.quic.events = conn.quic.eventArr[:0] 160 return &QUICConn{ 161 conn: conn, 162 } 163 } 164 165 // Start starts the client or server handshake protocol. 166 // It may produce connection events, which may be read with NextEvent. 167 // 168 // Start must be called at most once. 169 func (q *QUICConn) Start(ctx context.Context) error { 170 if q.conn.quic.started { 171 return quicError(errors.New("tls: Start called more than once")) 172 } 173 q.conn.quic.started = true 174 if q.conn.config.MinVersion < VersionTLS13 { 175 return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13")) 176 } 177 go q.conn.HandshakeContext(ctx) 178 if _, ok := <-q.conn.quic.blockedc; !ok { 179 return q.conn.handshakeErr 180 } 181 return nil 182 } 183 184 // NextEvent returns the next event occurring on the connection. 185 // It returns an event with a Kind of QUICNoEvent when no events are available. 186 func (q *QUICConn) NextEvent() QUICEvent { 187 qs := q.conn.quic 188 if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 { 189 // Write over some of the previous event's data, 190 // to catch callers erroniously retaining it. 191 qs.events[last].Data[0] = 0 192 } 193 if qs.nextEvent >= len(qs.events) { 194 qs.events = qs.events[:0] 195 qs.nextEvent = 0 196 return QUICEvent{Kind: QUICNoEvent} 197 } 198 e := qs.events[qs.nextEvent] 199 qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data 200 qs.nextEvent++ 201 return e 202 } 203 204 // Close closes the connection and stops any in-progress handshake. 205 func (q *QUICConn) Close() error { 206 if q.conn.quic.cancel == nil { 207 return nil // never started 208 } 209 q.conn.quic.cancel() 210 for range q.conn.quic.blockedc { 211 // Wait for the handshake goroutine to return. 212 } 213 return q.conn.handshakeErr 214 } 215 216 // HandleData handles handshake bytes received from the peer. 217 // It may produce connection events, which may be read with NextEvent. 218 func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error { 219 c := q.conn 220 if c.in.level != level { 221 return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level"))) 222 } 223 c.quic.readbuf = data 224 <-c.quic.signalc 225 _, ok := <-c.quic.blockedc 226 if ok { 227 // The handshake goroutine is waiting for more data. 228 return nil 229 } 230 // The handshake goroutine has exited. 231 c.hand.Write(c.quic.readbuf) 232 c.quic.readbuf = nil 233 for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil { 234 b := q.conn.hand.Bytes() 235 n := int(b[1])<<16 | int(b[2])<<8 | int(b[3]) 236 if 4+n < len(b) { 237 return nil 238 } 239 if err := q.conn.handlePostHandshakeMessage(); err != nil { 240 return quicError(err) 241 } 242 } 243 if q.conn.handshakeErr != nil { 244 return quicError(q.conn.handshakeErr) 245 } 246 return nil 247 } 248 249 type QUICSessionTicketOptions struct { 250 // EarlyData specifies whether the ticket may be used for 0-RTT. 251 EarlyData bool 252 } 253 254 // SendSessionTicket sends a session ticket to the client. 255 // It produces connection events, which may be read with NextEvent. 256 // Currently, it can only be called once. 257 func (q *QUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error { 258 c := q.conn 259 if !c.isHandshakeComplete.Load() { 260 return quicError(errors.New("tls: SendSessionTicket called before handshake completed")) 261 } 262 if c.isClient { 263 return quicError(errors.New("tls: SendSessionTicket called on the client")) 264 } 265 if q.sessionTicketSent { 266 return quicError(errors.New("tls: SendSessionTicket called multiple times")) 267 } 268 q.sessionTicketSent = true 269 return quicError(c.sendSessionTicket(opts.EarlyData)) 270 } 271 272 // ConnectionState returns basic TLS details about the connection. 273 func (q *QUICConn) ConnectionState() ConnectionState { 274 return q.conn.ConnectionState() 275 } 276 277 // SetTransportParameters sets the transport parameters to send to the peer. 278 // 279 // Server connections may delay setting the transport parameters until after 280 // receiving the client's transport parameters. See QUICTransportParametersRequired. 281 func (q *QUICConn) SetTransportParameters(params []byte) { 282 if params == nil { 283 params = []byte{} 284 } 285 q.conn.quic.transportParams = params 286 if q.conn.quic.started { 287 <-q.conn.quic.signalc 288 <-q.conn.quic.blockedc 289 } 290 } 291 292 // quicError ensures err is an AlertError. 293 // If err is not already, quicError wraps it with alertInternalError. 294 func quicError(err error) error { 295 if err == nil { 296 return nil 297 } 298 var ae AlertError 299 if errors.As(err, &ae) { 300 return err 301 } 302 var a alert 303 if !errors.As(err, &a) { 304 a = alertInternalError 305 } 306 // Return an error wrapping the original error and an AlertError. 307 // Truncate the text of the alert to 0 characters. 308 return fmt.Errorf("%w%.0w", err, AlertError(a)) 309 } 310 311 func (c *Conn) quicReadHandshakeBytes(n int) error { 312 for c.hand.Len() < n { 313 if err := c.quicWaitForSignal(); err != nil { 314 return err 315 } 316 } 317 return nil 318 } 319 320 func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) { 321 c.quic.events = append(c.quic.events, QUICEvent{ 322 Kind: QUICSetReadSecret, 323 Level: level, 324 Suite: suite, 325 Data: secret, 326 }) 327 } 328 329 func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) { 330 c.quic.events = append(c.quic.events, QUICEvent{ 331 Kind: QUICSetWriteSecret, 332 Level: level, 333 Suite: suite, 334 Data: secret, 335 }) 336 } 337 338 func (c *Conn) quicWriteCryptoData(level QUICEncryptionLevel, data []byte) { 339 var last *QUICEvent 340 if len(c.quic.events) > 0 { 341 last = &c.quic.events[len(c.quic.events)-1] 342 } 343 if last == nil || last.Kind != QUICWriteData || last.Level != level { 344 c.quic.events = append(c.quic.events, QUICEvent{ 345 Kind: QUICWriteData, 346 Level: level, 347 }) 348 last = &c.quic.events[len(c.quic.events)-1] 349 } 350 last.Data = append(last.Data, data...) 351 } 352 353 func (c *Conn) quicSetTransportParameters(params []byte) { 354 c.quic.events = append(c.quic.events, QUICEvent{ 355 Kind: QUICTransportParameters, 356 Data: params, 357 }) 358 } 359 360 func (c *Conn) quicGetTransportParameters() ([]byte, error) { 361 if c.quic.transportParams == nil { 362 c.quic.events = append(c.quic.events, QUICEvent{ 363 Kind: QUICTransportParametersRequired, 364 }) 365 } 366 for c.quic.transportParams == nil { 367 if err := c.quicWaitForSignal(); err != nil { 368 return nil, err 369 } 370 } 371 return c.quic.transportParams, nil 372 } 373 374 func (c *Conn) quicHandshakeComplete() { 375 c.quic.events = append(c.quic.events, QUICEvent{ 376 Kind: QUICHandshakeDone, 377 }) 378 } 379 380 func (c *Conn) quicRejectedEarlyData() { 381 c.quic.events = append(c.quic.events, QUICEvent{ 382 Kind: QUICRejectedEarlyData, 383 }) 384 } 385 386 // quicWaitForSignal notifies the QUICConn that handshake progress is blocked, 387 // and waits for a signal that the handshake should proceed. 388 // 389 // The handshake may become blocked waiting for handshake bytes 390 // or for the user to provide transport parameters. 391 func (c *Conn) quicWaitForSignal() error { 392 // Drop the handshake mutex while blocked to allow the user 393 // to call ConnectionState before the handshake completes. 394 c.handshakeMutex.Unlock() 395 defer c.handshakeMutex.Lock() 396 // Send on blockedc to notify the QUICConn that the handshake is blocked. 397 // Exported methods of QUICConn wait for the handshake to become blocked 398 // before returning to the user. 399 select { 400 case c.quic.blockedc <- struct{}{}: 401 case <-c.quic.cancelc: 402 return c.sendAlertLocked(alertCloseNotify) 403 } 404 // The QUICConn reads from signalc to notify us that the handshake may 405 // be able to proceed. (The QUICConn reads, because we close signalc to 406 // indicate that the handshake has completed.) 407 select { 408 case c.quic.signalc <- struct{}{}: 409 c.hand.Write(c.quic.readbuf) 410 c.quic.readbuf = nil 411 case <-c.quic.cancelc: 412 return c.sendAlertLocked(alertCloseNotify) 413 } 414 return nil 415 }