github.com/mikelsr/quic-go@v0.36.1-0.20230701132136-1d9415b66898/internal/handshake/crypto_setup.go (about) 1 package handshake 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/tls" 7 "errors" 8 "fmt" 9 "io" 10 "sync" 11 "sync/atomic" 12 "time" 13 14 "github.com/mikelsr/quic-go/internal/protocol" 15 "github.com/mikelsr/quic-go/internal/qerr" 16 "github.com/mikelsr/quic-go/internal/qtls" 17 "github.com/mikelsr/quic-go/internal/utils" 18 "github.com/mikelsr/quic-go/internal/wire" 19 "github.com/mikelsr/quic-go/logging" 20 "github.com/mikelsr/quic-go/quicvarint" 21 ) 22 23 type quicVersionContextKey struct{} 24 25 var QUICVersionContextKey = &quicVersionContextKey{} 26 27 const clientSessionStateRevision = 3 28 29 type cryptoSetup struct { 30 tlsConf *tls.Config 31 conn *qtls.QUICConn 32 33 version protocol.VersionNumber 34 35 ourParams *wire.TransportParameters 36 peerParams *wire.TransportParameters 37 38 runner handshakeRunner 39 40 zeroRTTParameters *wire.TransportParameters 41 zeroRTTParametersChan chan<- *wire.TransportParameters 42 allow0RTT bool 43 44 rttStats *utils.RTTStats 45 46 tracer logging.ConnectionTracer 47 logger utils.Logger 48 49 perspective protocol.Perspective 50 51 mutex sync.Mutex // protects all members below 52 53 handshakeCompleteTime time.Time 54 55 zeroRTTOpener LongHeaderOpener // only set for the server 56 zeroRTTSealer LongHeaderSealer // only set for the client 57 58 initialStream io.Writer 59 initialOpener LongHeaderOpener 60 initialSealer LongHeaderSealer 61 62 handshakeStream io.Writer 63 handshakeOpener LongHeaderOpener 64 handshakeSealer LongHeaderSealer 65 66 used0RTT atomic.Bool 67 68 oneRTTStream io.Writer 69 aead *updatableAEAD 70 has1RTTSealer bool 71 has1RTTOpener bool 72 } 73 74 var _ CryptoSetup = &cryptoSetup{} 75 76 // NewCryptoSetupClient creates a new crypto setup for the client 77 func NewCryptoSetupClient( 78 initialStream, handshakeStream, oneRTTStream io.Writer, 79 connID protocol.ConnectionID, 80 tp *wire.TransportParameters, 81 runner handshakeRunner, 82 tlsConf *tls.Config, 83 enable0RTT bool, 84 rttStats *utils.RTTStats, 85 tracer logging.ConnectionTracer, 86 logger utils.Logger, 87 version protocol.VersionNumber, 88 ) (CryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) { 89 cs, clientHelloWritten := newCryptoSetup( 90 initialStream, 91 handshakeStream, 92 oneRTTStream, 93 connID, 94 tp, 95 runner, 96 rttStats, 97 tracer, 98 logger, 99 protocol.PerspectiveClient, 100 version, 101 ) 102 103 tlsConf = tlsConf.Clone() 104 tlsConf.MinVersion = tls.VersionTLS13 105 quicConf := &qtls.QUICConfig{TLSConfig: tlsConf} 106 qtls.SetupConfigForClient(quicConf, cs.marshalDataForSessionState, cs.handleDataFromSessionState) 107 cs.tlsConf = tlsConf 108 109 cs.conn = qtls.QUICClient(quicConf) 110 cs.conn.SetTransportParameters(cs.ourParams.Marshal(protocol.PerspectiveClient)) 111 112 return cs, clientHelloWritten 113 } 114 115 // NewCryptoSetupServer creates a new crypto setup for the server 116 func NewCryptoSetupServer( 117 initialStream, handshakeStream, oneRTTStream io.Writer, 118 connID protocol.ConnectionID, 119 tp *wire.TransportParameters, 120 runner handshakeRunner, 121 tlsConf *tls.Config, 122 allow0RTT bool, 123 rttStats *utils.RTTStats, 124 tracer logging.ConnectionTracer, 125 logger utils.Logger, 126 version protocol.VersionNumber, 127 ) CryptoSetup { 128 cs, _ := newCryptoSetup( 129 initialStream, 130 handshakeStream, 131 oneRTTStream, 132 connID, 133 tp, 134 runner, 135 rttStats, 136 tracer, 137 logger, 138 protocol.PerspectiveServer, 139 version, 140 ) 141 cs.allow0RTT = allow0RTT 142 143 quicConf := &qtls.QUICConfig{TLSConfig: tlsConf} 144 qtls.SetupConfigForServer(quicConf, cs.allow0RTT, cs.getDataForSessionTicket, cs.accept0RTT) 145 146 cs.tlsConf = quicConf.TLSConfig 147 cs.conn = qtls.QUICServer(quicConf) 148 149 return cs 150 } 151 152 func newCryptoSetup( 153 initialStream, handshakeStream, oneRTTStream io.Writer, 154 connID protocol.ConnectionID, 155 tp *wire.TransportParameters, 156 runner handshakeRunner, 157 rttStats *utils.RTTStats, 158 tracer logging.ConnectionTracer, 159 logger utils.Logger, 160 perspective protocol.Perspective, 161 version protocol.VersionNumber, 162 ) (*cryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) { 163 initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version) 164 if tracer != nil { 165 tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) 166 tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) 167 } 168 zeroRTTParametersChan := make(chan *wire.TransportParameters, 1) 169 return &cryptoSetup{ 170 initialStream: initialStream, 171 initialSealer: initialSealer, 172 initialOpener: initialOpener, 173 handshakeStream: handshakeStream, 174 oneRTTStream: oneRTTStream, 175 aead: newUpdatableAEAD(rttStats, tracer, logger, version), 176 runner: runner, 177 ourParams: tp, 178 rttStats: rttStats, 179 tracer: tracer, 180 logger: logger, 181 perspective: perspective, 182 zeroRTTParametersChan: zeroRTTParametersChan, 183 version: version, 184 }, zeroRTTParametersChan 185 } 186 187 func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) { 188 initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version) 189 h.initialSealer = initialSealer 190 h.initialOpener = initialOpener 191 if h.tracer != nil { 192 h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) 193 h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) 194 } 195 } 196 197 func (h *cryptoSetup) SetLargest1RTTAcked(pn protocol.PacketNumber) error { 198 return h.aead.SetLargestAcked(pn) 199 } 200 201 func (h *cryptoSetup) StartHandshake() error { 202 err := h.conn.Start(context.WithValue(context.Background(), QUICVersionContextKey, h.version)) 203 if err != nil { 204 return wrapError(err) 205 } 206 for { 207 ev := h.conn.NextEvent() 208 done, err := h.handleEvent(ev) 209 if err != nil { 210 return wrapError(err) 211 } 212 if done { 213 break 214 } 215 } 216 if h.perspective == protocol.PerspectiveClient { 217 if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { 218 h.logger.Debugf("Doing 0-RTT.") 219 h.zeroRTTParametersChan <- h.zeroRTTParameters 220 } else { 221 h.logger.Debugf("Not doing 0-RTT. Has sealer: %t, has params: %t", h.zeroRTTSealer != nil, h.zeroRTTParameters != nil) 222 h.zeroRTTParametersChan <- nil 223 } 224 } 225 return nil 226 } 227 228 // Close closes the crypto setup. 229 // It aborts the handshake, if it is still running. 230 func (h *cryptoSetup) Close() error { 231 return h.conn.Close() 232 } 233 234 // HandleMessage handles a TLS handshake message. 235 // It is called by the crypto streams when a new message is available. 236 func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) error { 237 if err := h.handleMessage(data, encLevel); err != nil { 238 return wrapError(err) 239 } 240 return nil 241 } 242 243 func (h *cryptoSetup) handleMessage(data []byte, encLevel protocol.EncryptionLevel) error { 244 if err := h.conn.HandleData(qtls.ToTLSEncryptionLevel(encLevel), data); err != nil { 245 return err 246 } 247 for { 248 ev := h.conn.NextEvent() 249 done, err := h.handleEvent(ev) 250 if err != nil { 251 return err 252 } 253 if done { 254 return nil 255 } 256 } 257 } 258 259 func (h *cryptoSetup) handleEvent(ev qtls.QUICEvent) (done bool, err error) { 260 switch ev.Kind { 261 case qtls.QUICNoEvent: 262 return true, nil 263 case qtls.QUICSetReadSecret: 264 h.SetReadKey(ev.Level, ev.Suite, ev.Data) 265 return false, nil 266 case qtls.QUICSetWriteSecret: 267 h.SetWriteKey(ev.Level, ev.Suite, ev.Data) 268 return false, nil 269 case qtls.QUICTransportParameters: 270 return false, h.handleTransportParameters(ev.Data) 271 case qtls.QUICTransportParametersRequired: 272 h.conn.SetTransportParameters(h.ourParams.Marshal(h.perspective)) 273 return false, nil 274 case qtls.QUICRejectedEarlyData: 275 h.rejected0RTT() 276 return false, nil 277 case qtls.QUICWriteData: 278 return false, h.WriteRecord(ev.Level, ev.Data) 279 case qtls.QUICHandshakeDone: 280 h.handshakeComplete() 281 return false, nil 282 default: 283 return false, fmt.Errorf("unexpected event: %d", ev.Kind) 284 } 285 } 286 287 func (h *cryptoSetup) handleTransportParameters(data []byte) error { 288 var tp wire.TransportParameters 289 if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil { 290 return err 291 } 292 h.peerParams = &tp 293 h.runner.OnReceivedParams(h.peerParams) 294 return nil 295 } 296 297 // must be called after receiving the transport parameters 298 func (h *cryptoSetup) marshalDataForSessionState() []byte { 299 b := make([]byte, 0, 256) 300 b = quicvarint.Append(b, clientSessionStateRevision) 301 b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds())) 302 return h.peerParams.MarshalForSessionTicket(b) 303 } 304 305 func (h *cryptoSetup) handleDataFromSessionState(data []byte) { 306 tp, err := h.handleDataFromSessionStateImpl(data) 307 if err != nil { 308 h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error()) 309 return 310 } 311 h.zeroRTTParameters = tp 312 } 313 314 func (h *cryptoSetup) handleDataFromSessionStateImpl(data []byte) (*wire.TransportParameters, error) { 315 r := bytes.NewReader(data) 316 ver, err := quicvarint.Read(r) 317 if err != nil { 318 return nil, err 319 } 320 if ver != clientSessionStateRevision { 321 return nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision) 322 } 323 rtt, err := quicvarint.Read(r) 324 if err != nil { 325 return nil, err 326 } 327 h.rttStats.SetInitialRTT(time.Duration(rtt) * time.Microsecond) 328 var tp wire.TransportParameters 329 if err := tp.UnmarshalFromSessionTicket(r); err != nil { 330 return nil, err 331 } 332 return &tp, nil 333 } 334 335 func (h *cryptoSetup) getDataForSessionTicket() []byte { 336 return (&sessionTicket{ 337 Parameters: h.ourParams, 338 RTT: h.rttStats.SmoothedRTT(), 339 }).Marshal() 340 } 341 342 // GetSessionTicket generates a new session ticket. 343 // Due to limitations in crypto/tls, it's only possible to generate a single session ticket per connection. 344 // It is only valid for the server. 345 func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { 346 if h.tlsConf.SessionTicketsDisabled { 347 return nil, nil 348 } 349 if err := h.conn.SendSessionTicket(h.allow0RTT); err != nil { 350 return nil, err 351 } 352 ev := h.conn.NextEvent() 353 if ev.Kind != qtls.QUICWriteData || ev.Level != qtls.QUICEncryptionLevelApplication { 354 panic("crypto/tls bug: where's my session ticket?") 355 } 356 ticket := ev.Data 357 if ev := h.conn.NextEvent(); ev.Kind != qtls.QUICNoEvent { 358 panic("crypto/tls bug: why more than one ticket?") 359 } 360 return ticket, nil 361 } 362 363 // accept0RTT is called for the server when receiving the client's session ticket. 364 // It decides whether to accept 0-RTT. 365 func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool { 366 var t sessionTicket 367 if err := t.Unmarshal(sessionTicketData); err != nil { 368 h.logger.Debugf("Unmarshalling transport parameters from session ticket failed: %s", err.Error()) 369 return false 370 } 371 valid := h.ourParams.ValidFor0RTT(t.Parameters) 372 if !valid { 373 h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.") 374 return false 375 } 376 if !h.allow0RTT { 377 h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.") 378 return false 379 } 380 h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT) 381 h.rttStats.SetInitialRTT(t.RTT) 382 return true 383 } 384 385 // rejected0RTT is called for the client when the server rejects 0-RTT. 386 func (h *cryptoSetup) rejected0RTT() { 387 h.logger.Debugf("0-RTT was rejected. Dropping 0-RTT keys.") 388 389 h.mutex.Lock() 390 had0RTTKeys := h.zeroRTTSealer != nil 391 h.zeroRTTSealer = nil 392 h.mutex.Unlock() 393 394 if had0RTTKeys { 395 h.runner.DropKeys(protocol.Encryption0RTT) 396 } 397 } 398 399 func (h *cryptoSetup) SetReadKey(el qtls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { 400 suite := getCipherSuite(suiteID) 401 h.mutex.Lock() 402 //nolint:exhaustive // The TLS stack doesn't export Initial keys. 403 switch el { 404 case qtls.QUICEncryptionLevelEarly: 405 if h.perspective == protocol.PerspectiveClient { 406 panic("Received 0-RTT read key for the client") 407 } 408 h.zeroRTTOpener = newLongHeaderOpener( 409 createAEAD(suite, trafficSecret, h.version), 410 newHeaderProtector(suite, trafficSecret, true, h.version), 411 ) 412 h.used0RTT.Store(true) 413 if h.logger.Debug() { 414 h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) 415 } 416 case qtls.QUICEncryptionLevelHandshake: 417 h.handshakeOpener = newHandshakeOpener( 418 createAEAD(suite, trafficSecret, h.version), 419 newHeaderProtector(suite, trafficSecret, true, h.version), 420 h.dropInitialKeys, 421 h.perspective, 422 ) 423 if h.logger.Debug() { 424 h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID)) 425 } 426 case qtls.QUICEncryptionLevelApplication: 427 h.aead.SetReadKey(suite, trafficSecret) 428 h.has1RTTOpener = true 429 if h.logger.Debug() { 430 h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) 431 } 432 default: 433 panic("unexpected read encryption level") 434 } 435 h.mutex.Unlock() 436 h.runner.OnReceivedReadKeys() 437 if h.tracer != nil { 438 h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective.Opposite()) 439 } 440 } 441 442 func (h *cryptoSetup) SetWriteKey(el qtls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { 443 suite := getCipherSuite(suiteID) 444 h.mutex.Lock() 445 //nolint:exhaustive // The TLS stack doesn't export Initial keys. 446 switch el { 447 case qtls.QUICEncryptionLevelEarly: 448 if h.perspective == protocol.PerspectiveServer { 449 panic("Received 0-RTT write key for the server") 450 } 451 h.zeroRTTSealer = newLongHeaderSealer( 452 createAEAD(suite, trafficSecret, h.version), 453 newHeaderProtector(suite, trafficSecret, true, h.version), 454 ) 455 h.mutex.Unlock() 456 if h.logger.Debug() { 457 h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) 458 } 459 if h.tracer != nil { 460 h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective) 461 } 462 // don't set used0RTT here. 0-RTT might still get rejected. 463 return 464 case qtls.QUICEncryptionLevelHandshake: 465 h.handshakeSealer = newHandshakeSealer( 466 createAEAD(suite, trafficSecret, h.version), 467 newHeaderProtector(suite, trafficSecret, true, h.version), 468 h.dropInitialKeys, 469 h.perspective, 470 ) 471 if h.logger.Debug() { 472 h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID)) 473 } 474 case qtls.QUICEncryptionLevelApplication: 475 h.aead.SetWriteKey(suite, trafficSecret) 476 h.has1RTTSealer = true 477 if h.logger.Debug() { 478 h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) 479 } 480 if h.zeroRTTSealer != nil { 481 // Once we receive handshake keys, we know that 0-RTT was not rejected. 482 h.used0RTT.Store(true) 483 h.zeroRTTSealer = nil 484 h.logger.Debugf("Dropping 0-RTT keys.") 485 if h.tracer != nil { 486 h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) 487 } 488 } 489 default: 490 panic("unexpected write encryption level") 491 } 492 h.mutex.Unlock() 493 if h.tracer != nil { 494 h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective) 495 } 496 } 497 498 // WriteRecord is called when TLS writes data 499 func (h *cryptoSetup) WriteRecord(encLevel qtls.QUICEncryptionLevel, p []byte) error { 500 h.mutex.Lock() 501 defer h.mutex.Unlock() 502 503 var str io.Writer 504 //nolint:exhaustive // handshake records can only be written for Initial and Handshake. 505 switch encLevel { 506 case qtls.QUICEncryptionLevelInitial: 507 // assume that the first WriteRecord call contains the ClientHello 508 str = h.initialStream 509 case qtls.QUICEncryptionLevelHandshake: 510 str = h.handshakeStream 511 case qtls.QUICEncryptionLevelApplication: 512 str = h.oneRTTStream 513 default: 514 panic(fmt.Sprintf("unexpected write encryption level: %s", encLevel)) 515 } 516 _, err := str.Write(p) 517 return err 518 } 519 520 // used a callback in the handshakeSealer and handshakeOpener 521 func (h *cryptoSetup) dropInitialKeys() { 522 h.mutex.Lock() 523 h.initialOpener = nil 524 h.initialSealer = nil 525 h.mutex.Unlock() 526 h.runner.DropKeys(protocol.EncryptionInitial) 527 h.logger.Debugf("Dropping Initial keys.") 528 } 529 530 func (h *cryptoSetup) handshakeComplete() { 531 h.handshakeCompleteTime = time.Now() 532 h.runner.OnHandshakeComplete() 533 } 534 535 func (h *cryptoSetup) SetHandshakeConfirmed() { 536 h.aead.SetHandshakeConfirmed() 537 // drop Handshake keys 538 var dropped bool 539 h.mutex.Lock() 540 if h.handshakeOpener != nil { 541 h.handshakeOpener = nil 542 h.handshakeSealer = nil 543 dropped = true 544 } 545 h.mutex.Unlock() 546 if dropped { 547 h.runner.DropKeys(protocol.EncryptionHandshake) 548 h.logger.Debugf("Dropping Handshake keys.") 549 } 550 } 551 552 func (h *cryptoSetup) GetInitialSealer() (LongHeaderSealer, error) { 553 h.mutex.Lock() 554 defer h.mutex.Unlock() 555 556 if h.initialSealer == nil { 557 return nil, ErrKeysDropped 558 } 559 return h.initialSealer, nil 560 } 561 562 func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) { 563 h.mutex.Lock() 564 defer h.mutex.Unlock() 565 566 if h.zeroRTTSealer == nil { 567 return nil, ErrKeysDropped 568 } 569 return h.zeroRTTSealer, nil 570 } 571 572 func (h *cryptoSetup) GetHandshakeSealer() (LongHeaderSealer, error) { 573 h.mutex.Lock() 574 defer h.mutex.Unlock() 575 576 if h.handshakeSealer == nil { 577 if h.initialSealer == nil { 578 return nil, ErrKeysDropped 579 } 580 return nil, ErrKeysNotYetAvailable 581 } 582 return h.handshakeSealer, nil 583 } 584 585 func (h *cryptoSetup) Get1RTTSealer() (ShortHeaderSealer, error) { 586 h.mutex.Lock() 587 defer h.mutex.Unlock() 588 589 if !h.has1RTTSealer { 590 return nil, ErrKeysNotYetAvailable 591 } 592 return h.aead, nil 593 } 594 595 func (h *cryptoSetup) GetInitialOpener() (LongHeaderOpener, error) { 596 h.mutex.Lock() 597 defer h.mutex.Unlock() 598 599 if h.initialOpener == nil { 600 return nil, ErrKeysDropped 601 } 602 return h.initialOpener, nil 603 } 604 605 func (h *cryptoSetup) Get0RTTOpener() (LongHeaderOpener, error) { 606 h.mutex.Lock() 607 defer h.mutex.Unlock() 608 609 if h.zeroRTTOpener == nil { 610 if h.initialOpener != nil { 611 return nil, ErrKeysNotYetAvailable 612 } 613 // if the initial opener is also not available, the keys were already dropped 614 return nil, ErrKeysDropped 615 } 616 return h.zeroRTTOpener, nil 617 } 618 619 func (h *cryptoSetup) GetHandshakeOpener() (LongHeaderOpener, error) { 620 h.mutex.Lock() 621 defer h.mutex.Unlock() 622 623 if h.handshakeOpener == nil { 624 if h.initialOpener != nil { 625 return nil, ErrKeysNotYetAvailable 626 } 627 // if the initial opener is also not available, the keys were already dropped 628 return nil, ErrKeysDropped 629 } 630 return h.handshakeOpener, nil 631 } 632 633 func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) { 634 h.mutex.Lock() 635 defer h.mutex.Unlock() 636 637 if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) { 638 h.zeroRTTOpener = nil 639 h.logger.Debugf("Dropping 0-RTT keys.") 640 if h.tracer != nil { 641 h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) 642 } 643 } 644 645 if !h.has1RTTOpener { 646 return nil, ErrKeysNotYetAvailable 647 } 648 return h.aead, nil 649 } 650 651 func (h *cryptoSetup) ConnectionState() ConnectionState { 652 return ConnectionState{ 653 ConnectionState: h.conn.ConnectionState(), 654 Used0RTT: h.used0RTT.Load(), 655 } 656 } 657 658 func wrapError(err error) error { 659 if alertErr := qtls.AlertError(0); errors.As(err, &alertErr) && alertErr != 80 { 660 return qerr.NewLocalCryptoError(uint8(alertErr), err.Error()) 661 } 662 return &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: err.Error()} 663 }