 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>
		
			
				
	
	
		
			1644 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1644 lines
		
	
	
		
			50 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2010 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.
 | |
| 
 | |
| // TLS low level connection and record layer
 | |
| 
 | |
| package qtls
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"crypto/cipher"
 | |
| 	"crypto/subtle"
 | |
| 	"crypto/x509"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"hash"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"sync"
 | |
| 	"sync/atomic"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // A Conn represents a secured connection.
 | |
| // It implements the net.Conn interface.
 | |
| type Conn struct {
 | |
| 	// constant
 | |
| 	conn        net.Conn
 | |
| 	isClient    bool
 | |
| 	handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
 | |
| 	quic        *quicState                  // nil for non-QUIC connections
 | |
| 
 | |
| 	// isHandshakeComplete is true if the connection is currently transferring
 | |
| 	// application data (i.e. is not currently processing a handshake).
 | |
| 	// isHandshakeComplete is true implies handshakeErr == nil.
 | |
| 	isHandshakeComplete atomic.Bool
 | |
| 	// constant after handshake; protected by handshakeMutex
 | |
| 	handshakeMutex sync.Mutex
 | |
| 	handshakeErr   error   // error resulting from handshake
 | |
| 	vers           uint16  // TLS version
 | |
| 	haveVers       bool    // version has been negotiated
 | |
| 	config         *config // configuration passed to constructor
 | |
| 	extraConfig    *ExtraConfig
 | |
| 	// handshakes counts the number of handshakes performed on the
 | |
| 	// connection so far. If renegotiation is disabled then this is either
 | |
| 	// zero or one.
 | |
| 	handshakes       int
 | |
| 	didResume        bool // whether this connection was a session resumption
 | |
| 	cipherSuite      uint16
 | |
| 	ocspResponse     []byte   // stapled OCSP response
 | |
| 	scts             [][]byte // signed certificate timestamps from server
 | |
| 	peerCertificates []*x509.Certificate
 | |
| 	// activeCertHandles contains the cache handles to certificates in
 | |
| 	// peerCertificates that are used to track active references.
 | |
| 	activeCertHandles []*activeCert
 | |
| 	// verifiedChains contains the certificate chains that we built, as
 | |
| 	// opposed to the ones presented by the server.
 | |
| 	verifiedChains [][]*x509.Certificate
 | |
| 	// serverName contains the server name indicated by the client, if any.
 | |
| 	serverName string
 | |
| 	// secureRenegotiation is true if the server echoed the secure
 | |
| 	// renegotiation extension. (This is meaningless as a server because
 | |
| 	// renegotiation is not supported in that case.)
 | |
| 	secureRenegotiation bool
 | |
| 	// ekm is a closure for exporting keying material.
 | |
| 	ekm func(label string, context []byte, length int) ([]byte, error)
 | |
| 	// resumptionSecret is the resumption_master_secret for handling
 | |
| 	// or sending NewSessionTicket messages.
 | |
| 	resumptionSecret []byte
 | |
| 
 | |
| 	// ticketKeys is the set of active session ticket keys for this
 | |
| 	// connection. The first one is used to encrypt new tickets and
 | |
| 	// all are tried to decrypt tickets.
 | |
| 	ticketKeys []ticketKey
 | |
| 
 | |
| 	// clientFinishedIsFirst is true if the client sent the first Finished
 | |
| 	// message during the most recent handshake. This is recorded because
 | |
| 	// the first transmitted Finished message is the tls-unique
 | |
| 	// channel-binding value.
 | |
| 	clientFinishedIsFirst bool
 | |
| 
 | |
| 	// closeNotifyErr is any error from sending the alertCloseNotify record.
 | |
| 	closeNotifyErr error
 | |
| 	// closeNotifySent is true if the Conn attempted to send an
 | |
| 	// alertCloseNotify record.
 | |
| 	closeNotifySent bool
 | |
| 
 | |
| 	// clientFinished and serverFinished contain the Finished message sent
 | |
| 	// by the client or server in the most recent handshake. This is
 | |
| 	// retained to support the renegotiation extension and tls-unique
 | |
| 	// channel-binding.
 | |
| 	clientFinished [12]byte
 | |
| 	serverFinished [12]byte
 | |
| 
 | |
| 	// clientProtocol is the negotiated ALPN protocol.
 | |
| 	clientProtocol string
 | |
| 
 | |
| 	// input/output
 | |
| 	in, out   halfConn
 | |
| 	rawInput  bytes.Buffer // raw input, starting with a record header
 | |
| 	input     bytes.Reader // application data waiting to be read, from rawInput.Next
 | |
| 	hand      bytes.Buffer // handshake data waiting to be read
 | |
| 	buffering bool         // whether records are buffered in sendBuf
 | |
| 	sendBuf   []byte       // a buffer of records waiting to be sent
 | |
| 
 | |
| 	// bytesSent counts the bytes of application data sent.
 | |
| 	// packetsSent counts packets.
 | |
| 	bytesSent   int64
 | |
| 	packetsSent int64
 | |
| 
 | |
| 	// retryCount counts the number of consecutive non-advancing records
 | |
| 	// received by Conn.readRecord. That is, records that neither advance the
 | |
| 	// handshake, nor deliver application data. Protected by in.Mutex.
 | |
| 	retryCount int
 | |
| 
 | |
| 	// activeCall indicates whether Close has been call in the low bit.
 | |
| 	// the rest of the bits are the number of goroutines in Conn.Write.
 | |
| 	activeCall atomic.Int32
 | |
| 
 | |
| 	tmp [16]byte
 | |
| }
 | |
| 
 | |
| // Access to net.Conn methods.
 | |
| // Cannot just embed net.Conn because that would
 | |
| // export the struct field too.
 | |
| 
 | |
| // LocalAddr returns the local network address.
 | |
| func (c *Conn) LocalAddr() net.Addr {
 | |
| 	return c.conn.LocalAddr()
 | |
| }
 | |
| 
 | |
| // RemoteAddr returns the remote network address.
 | |
| func (c *Conn) RemoteAddr() net.Addr {
 | |
| 	return c.conn.RemoteAddr()
 | |
| }
 | |
| 
 | |
| // SetDeadline sets the read and write deadlines associated with the connection.
 | |
| // A zero value for t means Read and Write will not time out.
 | |
| // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
 | |
| func (c *Conn) SetDeadline(t time.Time) error {
 | |
| 	return c.conn.SetDeadline(t)
 | |
| }
 | |
| 
 | |
| // SetReadDeadline sets the read deadline on the underlying connection.
 | |
| // A zero value for t means Read will not time out.
 | |
| func (c *Conn) SetReadDeadline(t time.Time) error {
 | |
| 	return c.conn.SetReadDeadline(t)
 | |
| }
 | |
| 
 | |
| // SetWriteDeadline sets the write deadline on the underlying connection.
 | |
| // A zero value for t means Write will not time out.
 | |
| // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
 | |
| func (c *Conn) SetWriteDeadline(t time.Time) error {
 | |
| 	return c.conn.SetWriteDeadline(t)
 | |
| }
 | |
| 
 | |
| // NetConn returns the underlying connection that is wrapped by c.
 | |
| // Note that writing to or reading from this connection directly will corrupt the
 | |
| // TLS session.
 | |
| func (c *Conn) NetConn() net.Conn {
 | |
| 	return c.conn
 | |
| }
 | |
| 
 | |
| // A halfConn represents one direction of the record layer
 | |
| // connection, either sending or receiving.
 | |
| type halfConn struct {
 | |
| 	sync.Mutex
 | |
| 
 | |
| 	err     error  // first permanent error
 | |
| 	version uint16 // protocol version
 | |
| 	cipher  any    // cipher algorithm
 | |
| 	mac     hash.Hash
 | |
| 	seq     [8]byte // 64-bit sequence number
 | |
| 
 | |
| 	scratchBuf [13]byte // to avoid allocs; interface method args escape
 | |
| 
 | |
| 	nextCipher any       // next encryption state
 | |
| 	nextMac    hash.Hash // next MAC algorithm
 | |
| 
 | |
| 	level         QUICEncryptionLevel // current QUIC encryption level
 | |
| 	trafficSecret []byte              // current TLS 1.3 traffic secret
 | |
| }
 | |
| 
 | |
| type permanentError struct {
 | |
| 	err net.Error
 | |
| }
 | |
| 
 | |
| func (e *permanentError) Error() string   { return e.err.Error() }
 | |
| func (e *permanentError) Unwrap() error   { return e.err }
 | |
| func (e *permanentError) Timeout() bool   { return e.err.Timeout() }
 | |
| func (e *permanentError) Temporary() bool { return false }
 | |
| 
 | |
| func (hc *halfConn) setErrorLocked(err error) error {
 | |
| 	if e, ok := err.(net.Error); ok {
 | |
| 		hc.err = &permanentError{err: e}
 | |
| 	} else {
 | |
| 		hc.err = err
 | |
| 	}
 | |
| 	return hc.err
 | |
| }
 | |
| 
 | |
| // prepareCipherSpec sets the encryption and MAC states
 | |
| // that a subsequent changeCipherSpec will use.
 | |
| func (hc *halfConn) prepareCipherSpec(version uint16, cipher any, mac hash.Hash) {
 | |
| 	hc.version = version
 | |
| 	hc.nextCipher = cipher
 | |
| 	hc.nextMac = mac
 | |
| }
 | |
| 
 | |
| // changeCipherSpec changes the encryption and MAC states
 | |
| // to the ones previously passed to prepareCipherSpec.
 | |
| func (hc *halfConn) changeCipherSpec() error {
 | |
| 	if hc.nextCipher == nil || hc.version == VersionTLS13 {
 | |
| 		return alertInternalError
 | |
| 	}
 | |
| 	hc.cipher = hc.nextCipher
 | |
| 	hc.mac = hc.nextMac
 | |
| 	hc.nextCipher = nil
 | |
| 	hc.nextMac = nil
 | |
| 	for i := range hc.seq {
 | |
| 		hc.seq[i] = 0
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) {
 | |
| 	hc.trafficSecret = secret
 | |
| 	hc.level = level
 | |
| 	key, iv := suite.trafficKey(secret)
 | |
| 	hc.cipher = suite.aead(key, iv)
 | |
| 	for i := range hc.seq {
 | |
| 		hc.seq[i] = 0
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // incSeq increments the sequence number.
 | |
| func (hc *halfConn) incSeq() {
 | |
| 	for i := 7; i >= 0; i-- {
 | |
| 		hc.seq[i]++
 | |
| 		if hc.seq[i] != 0 {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Not allowed to let sequence number wrap.
 | |
| 	// Instead, must renegotiate before it does.
 | |
| 	// Not likely enough to bother.
 | |
| 	panic("TLS: sequence number wraparound")
 | |
| }
 | |
| 
 | |
| // explicitNonceLen returns the number of bytes of explicit nonce or IV included
 | |
| // in each record. Explicit nonces are present only in CBC modes after TLS 1.0
 | |
| // and in certain AEAD modes in TLS 1.2.
 | |
| func (hc *halfConn) explicitNonceLen() int {
 | |
| 	if hc.cipher == nil {
 | |
| 		return 0
 | |
| 	}
 | |
| 
 | |
| 	switch c := hc.cipher.(type) {
 | |
| 	case cipher.Stream:
 | |
| 		return 0
 | |
| 	case aead:
 | |
| 		return c.explicitNonceLen()
 | |
| 	case cbcMode:
 | |
| 		// TLS 1.1 introduced a per-record explicit IV to fix the BEAST attack.
 | |
| 		if hc.version >= VersionTLS11 {
 | |
| 			return c.BlockSize()
 | |
| 		}
 | |
| 		return 0
 | |
| 	default:
 | |
| 		panic("unknown cipher type")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // extractPadding returns, in constant time, the length of the padding to remove
 | |
| // from the end of payload. It also returns a byte which is equal to 255 if the
 | |
| // padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
 | |
| func extractPadding(payload []byte) (toRemove int, good byte) {
 | |
| 	if len(payload) < 1 {
 | |
| 		return 0, 0
 | |
| 	}
 | |
| 
 | |
| 	paddingLen := payload[len(payload)-1]
 | |
| 	t := uint(len(payload)-1) - uint(paddingLen)
 | |
| 	// if len(payload) >= (paddingLen - 1) then the MSB of t is zero
 | |
| 	good = byte(int32(^t) >> 31)
 | |
| 
 | |
| 	// The maximum possible padding length plus the actual length field
 | |
| 	toCheck := 256
 | |
| 	// The length of the padded data is public, so we can use an if here
 | |
| 	if toCheck > len(payload) {
 | |
| 		toCheck = len(payload)
 | |
| 	}
 | |
| 
 | |
| 	for i := 0; i < toCheck; i++ {
 | |
| 		t := uint(paddingLen) - uint(i)
 | |
| 		// if i <= paddingLen then the MSB of t is zero
 | |
| 		mask := byte(int32(^t) >> 31)
 | |
| 		b := payload[len(payload)-1-i]
 | |
| 		good &^= mask&paddingLen ^ mask&b
 | |
| 	}
 | |
| 
 | |
| 	// We AND together the bits of good and replicate the result across
 | |
| 	// all the bits.
 | |
| 	good &= good << 4
 | |
| 	good &= good << 2
 | |
| 	good &= good << 1
 | |
| 	good = uint8(int8(good) >> 7)
 | |
| 
 | |
| 	// Zero the padding length on error. This ensures any unchecked bytes
 | |
| 	// are included in the MAC. Otherwise, an attacker that could
 | |
| 	// distinguish MAC failures from padding failures could mount an attack
 | |
| 	// similar to POODLE in SSL 3.0: given a good ciphertext that uses a
 | |
| 	// full block's worth of padding, replace the final block with another
 | |
| 	// block. If the MAC check passed but the padding check failed, the
 | |
| 	// last byte of that block decrypted to the block size.
 | |
| 	//
 | |
| 	// See also macAndPaddingGood logic below.
 | |
| 	paddingLen &= good
 | |
| 
 | |
| 	toRemove = int(paddingLen) + 1
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func roundUp(a, b int) int {
 | |
| 	return a + (b-a%b)%b
 | |
| }
 | |
| 
 | |
| // cbcMode is an interface for block ciphers using cipher block chaining.
 | |
| type cbcMode interface {
 | |
| 	cipher.BlockMode
 | |
| 	SetIV([]byte)
 | |
| }
 | |
| 
 | |
| // decrypt authenticates and decrypts the record if protection is active at
 | |
| // this stage. The returned plaintext might overlap with the input.
 | |
| func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) {
 | |
| 	var plaintext []byte
 | |
| 	typ := recordType(record[0])
 | |
| 	payload := record[recordHeaderLen:]
 | |
| 
 | |
| 	// In TLS 1.3, change_cipher_spec messages are to be ignored without being
 | |
| 	// decrypted. See RFC 8446, Appendix D.4.
 | |
| 	if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec {
 | |
| 		return payload, typ, nil
 | |
| 	}
 | |
| 
 | |
| 	paddingGood := byte(255)
 | |
| 	paddingLen := 0
 | |
| 
 | |
| 	explicitNonceLen := hc.explicitNonceLen()
 | |
| 
 | |
| 	if hc.cipher != nil {
 | |
| 		switch c := hc.cipher.(type) {
 | |
| 		case cipher.Stream:
 | |
| 			c.XORKeyStream(payload, payload)
 | |
| 		case aead:
 | |
| 			if len(payload) < explicitNonceLen {
 | |
| 				return nil, 0, alertBadRecordMAC
 | |
| 			}
 | |
| 			nonce := payload[:explicitNonceLen]
 | |
| 			if len(nonce) == 0 {
 | |
| 				nonce = hc.seq[:]
 | |
| 			}
 | |
| 			payload = payload[explicitNonceLen:]
 | |
| 
 | |
| 			var additionalData []byte
 | |
| 			if hc.version == VersionTLS13 {
 | |
| 				additionalData = record[:recordHeaderLen]
 | |
| 			} else {
 | |
| 				additionalData = append(hc.scratchBuf[:0], hc.seq[:]...)
 | |
| 				additionalData = append(additionalData, record[:3]...)
 | |
| 				n := len(payload) - c.Overhead()
 | |
| 				additionalData = append(additionalData, byte(n>>8), byte(n))
 | |
| 			}
 | |
| 
 | |
| 			var err error
 | |
| 			plaintext, err = c.Open(payload[:0], nonce, payload, additionalData)
 | |
| 			if err != nil {
 | |
| 				return nil, 0, alertBadRecordMAC
 | |
| 			}
 | |
| 		case cbcMode:
 | |
| 			blockSize := c.BlockSize()
 | |
| 			minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize)
 | |
| 			if len(payload)%blockSize != 0 || len(payload) < minPayload {
 | |
| 				return nil, 0, alertBadRecordMAC
 | |
| 			}
 | |
| 
 | |
| 			if explicitNonceLen > 0 {
 | |
| 				c.SetIV(payload[:explicitNonceLen])
 | |
| 				payload = payload[explicitNonceLen:]
 | |
| 			}
 | |
| 			c.CryptBlocks(payload, payload)
 | |
| 
 | |
| 			// In a limited attempt to protect against CBC padding oracles like
 | |
| 			// Lucky13, the data past paddingLen (which is secret) is passed to
 | |
| 			// the MAC function as extra data, to be fed into the HMAC after
 | |
| 			// computing the digest. This makes the MAC roughly constant time as
 | |
| 			// long as the digest computation is constant time and does not
 | |
| 			// affect the subsequent write, modulo cache effects.
 | |
| 			paddingLen, paddingGood = extractPadding(payload)
 | |
| 		default:
 | |
| 			panic("unknown cipher type")
 | |
| 		}
 | |
| 
 | |
| 		if hc.version == VersionTLS13 {
 | |
| 			if typ != recordTypeApplicationData {
 | |
| 				return nil, 0, alertUnexpectedMessage
 | |
| 			}
 | |
| 			if len(plaintext) > maxPlaintext+1 {
 | |
| 				return nil, 0, alertRecordOverflow
 | |
| 			}
 | |
| 			// Remove padding and find the ContentType scanning from the end.
 | |
| 			for i := len(plaintext) - 1; i >= 0; i-- {
 | |
| 				if plaintext[i] != 0 {
 | |
| 					typ = recordType(plaintext[i])
 | |
| 					plaintext = plaintext[:i]
 | |
| 					break
 | |
| 				}
 | |
| 				if i == 0 {
 | |
| 					return nil, 0, alertUnexpectedMessage
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		plaintext = payload
 | |
| 	}
 | |
| 
 | |
| 	if hc.mac != nil {
 | |
| 		macSize := hc.mac.Size()
 | |
| 		if len(payload) < macSize {
 | |
| 			return nil, 0, alertBadRecordMAC
 | |
| 		}
 | |
| 
 | |
| 		n := len(payload) - macSize - paddingLen
 | |
| 		n = subtle.ConstantTimeSelect(int(uint32(n)>>31), 0, n) // if n < 0 { n = 0 }
 | |
| 		record[3] = byte(n >> 8)
 | |
| 		record[4] = byte(n)
 | |
| 		remoteMAC := payload[n : n+macSize]
 | |
| 		localMAC := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload[:n], payload[n+macSize:])
 | |
| 
 | |
| 		// This is equivalent to checking the MACs and paddingGood
 | |
| 		// separately, but in constant-time to prevent distinguishing
 | |
| 		// padding failures from MAC failures. Depending on what value
 | |
| 		// of paddingLen was returned on bad padding, distinguishing
 | |
| 		// bad MAC from bad padding can lead to an attack.
 | |
| 		//
 | |
| 		// See also the logic at the end of extractPadding.
 | |
| 		macAndPaddingGood := subtle.ConstantTimeCompare(localMAC, remoteMAC) & int(paddingGood)
 | |
| 		if macAndPaddingGood != 1 {
 | |
| 			return nil, 0, alertBadRecordMAC
 | |
| 		}
 | |
| 
 | |
| 		plaintext = payload[:n]
 | |
| 	}
 | |
| 
 | |
| 	hc.incSeq()
 | |
| 	return plaintext, typ, nil
 | |
| }
 | |
| 
 | |
| // sliceForAppend extends the input slice by n bytes. head is the full extended
 | |
| // slice, while tail is the appended part. If the original slice has sufficient
 | |
| // capacity no allocation is performed.
 | |
| func sliceForAppend(in []byte, n int) (head, tail []byte) {
 | |
| 	if total := len(in) + n; cap(in) >= total {
 | |
| 		head = in[:total]
 | |
| 	} else {
 | |
| 		head = make([]byte, total)
 | |
| 		copy(head, in)
 | |
| 	}
 | |
| 	tail = head[len(in):]
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // encrypt encrypts payload, adding the appropriate nonce and/or MAC, and
 | |
| // appends it to record, which must already contain the record header.
 | |
| func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) {
 | |
| 	if hc.cipher == nil {
 | |
| 		return append(record, payload...), nil
 | |
| 	}
 | |
| 
 | |
| 	var explicitNonce []byte
 | |
| 	if explicitNonceLen := hc.explicitNonceLen(); explicitNonceLen > 0 {
 | |
| 		record, explicitNonce = sliceForAppend(record, explicitNonceLen)
 | |
| 		if _, isCBC := hc.cipher.(cbcMode); !isCBC && explicitNonceLen < 16 {
 | |
| 			// The AES-GCM construction in TLS has an explicit nonce so that the
 | |
| 			// nonce can be random. However, the nonce is only 8 bytes which is
 | |
| 			// too small for a secure, random nonce. Therefore we use the
 | |
| 			// sequence number as the nonce. The 3DES-CBC construction also has
 | |
| 			// an 8 bytes nonce but its nonces must be unpredictable (see RFC
 | |
| 			// 5246, Appendix F.3), forcing us to use randomness. That's not
 | |
| 			// 3DES' biggest problem anyway because the birthday bound on block
 | |
| 			// collision is reached first due to its similarly small block size
 | |
| 			// (see the Sweet32 attack).
 | |
| 			copy(explicitNonce, hc.seq[:])
 | |
| 		} else {
 | |
| 			if _, err := io.ReadFull(rand, explicitNonce); err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	var dst []byte
 | |
| 	switch c := hc.cipher.(type) {
 | |
| 	case cipher.Stream:
 | |
| 		mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
 | |
| 		record, dst = sliceForAppend(record, len(payload)+len(mac))
 | |
| 		c.XORKeyStream(dst[:len(payload)], payload)
 | |
| 		c.XORKeyStream(dst[len(payload):], mac)
 | |
| 	case aead:
 | |
| 		nonce := explicitNonce
 | |
| 		if len(nonce) == 0 {
 | |
| 			nonce = hc.seq[:]
 | |
| 		}
 | |
| 
 | |
| 		if hc.version == VersionTLS13 {
 | |
| 			record = append(record, payload...)
 | |
| 
 | |
| 			// Encrypt the actual ContentType and replace the plaintext one.
 | |
| 			record = append(record, record[0])
 | |
| 			record[0] = byte(recordTypeApplicationData)
 | |
| 
 | |
| 			n := len(payload) + 1 + c.Overhead()
 | |
| 			record[3] = byte(n >> 8)
 | |
| 			record[4] = byte(n)
 | |
| 
 | |
| 			record = c.Seal(record[:recordHeaderLen],
 | |
| 				nonce, record[recordHeaderLen:], record[:recordHeaderLen])
 | |
| 		} else {
 | |
| 			additionalData := append(hc.scratchBuf[:0], hc.seq[:]...)
 | |
| 			additionalData = append(additionalData, record[:recordHeaderLen]...)
 | |
| 			record = c.Seal(record, nonce, payload, additionalData)
 | |
| 		}
 | |
| 	case cbcMode:
 | |
| 		mac := tls10MAC(hc.mac, hc.scratchBuf[:0], hc.seq[:], record[:recordHeaderLen], payload, nil)
 | |
| 		blockSize := c.BlockSize()
 | |
| 		plaintextLen := len(payload) + len(mac)
 | |
| 		paddingLen := blockSize - plaintextLen%blockSize
 | |
| 		record, dst = sliceForAppend(record, plaintextLen+paddingLen)
 | |
| 		copy(dst, payload)
 | |
| 		copy(dst[len(payload):], mac)
 | |
| 		for i := plaintextLen; i < len(dst); i++ {
 | |
| 			dst[i] = byte(paddingLen - 1)
 | |
| 		}
 | |
| 		if len(explicitNonce) > 0 {
 | |
| 			c.SetIV(explicitNonce)
 | |
| 		}
 | |
| 		c.CryptBlocks(dst, dst)
 | |
| 	default:
 | |
| 		panic("unknown cipher type")
 | |
| 	}
 | |
| 
 | |
| 	// Update length to include nonce, MAC and any block padding needed.
 | |
| 	n := len(record) - recordHeaderLen
 | |
| 	record[3] = byte(n >> 8)
 | |
| 	record[4] = byte(n)
 | |
| 	hc.incSeq()
 | |
| 
 | |
| 	return record, nil
 | |
| }
 | |
| 
 | |
| // RecordHeaderError is returned when a TLS record header is invalid.
 | |
| type RecordHeaderError struct {
 | |
| 	// Msg contains a human readable string that describes the error.
 | |
| 	Msg string
 | |
| 	// RecordHeader contains the five bytes of TLS record header that
 | |
| 	// triggered the error.
 | |
| 	RecordHeader [5]byte
 | |
| 	// Conn provides the underlying net.Conn in the case that a client
 | |
| 	// sent an initial handshake that didn't look like TLS.
 | |
| 	// It is nil if there's already been a handshake or a TLS alert has
 | |
| 	// been written to the connection.
 | |
| 	Conn net.Conn
 | |
| }
 | |
| 
 | |
| func (e RecordHeaderError) Error() string { return "tls: " + e.Msg }
 | |
| 
 | |
| func (c *Conn) newRecordHeaderError(conn net.Conn, msg string) (err RecordHeaderError) {
 | |
| 	err.Msg = msg
 | |
| 	err.Conn = conn
 | |
| 	copy(err.RecordHeader[:], c.rawInput.Bytes())
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (c *Conn) readRecord() error {
 | |
| 	return c.readRecordOrCCS(false)
 | |
| }
 | |
| 
 | |
| func (c *Conn) readChangeCipherSpec() error {
 | |
| 	return c.readRecordOrCCS(true)
 | |
| }
 | |
| 
 | |
| // readRecordOrCCS reads one or more TLS records from the connection and
 | |
| // updates the record layer state. Some invariants:
 | |
| //   - c.in must be locked
 | |
| //   - c.input must be empty
 | |
| //
 | |
| // During the handshake one and only one of the following will happen:
 | |
| //   - c.hand grows
 | |
| //   - c.in.changeCipherSpec is called
 | |
| //   - an error is returned
 | |
| //
 | |
| // After the handshake one and only one of the following will happen:
 | |
| //   - c.hand grows
 | |
| //   - c.input is set
 | |
| //   - an error is returned
 | |
| func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
 | |
| 	if c.in.err != nil {
 | |
| 		return c.in.err
 | |
| 	}
 | |
| 	handshakeComplete := c.isHandshakeComplete.Load()
 | |
| 
 | |
| 	// This function modifies c.rawInput, which owns the c.input memory.
 | |
| 	if c.input.Len() != 0 {
 | |
| 		return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with pending application data"))
 | |
| 	}
 | |
| 	c.input.Reset(nil)
 | |
| 
 | |
| 	if c.quic != nil {
 | |
| 		return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with QUIC transport"))
 | |
| 	}
 | |
| 
 | |
| 	// Read header, payload.
 | |
| 	if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil {
 | |
| 		// RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
 | |
| 		// is an error, but popular web sites seem to do this, so we accept it
 | |
| 		// if and only if at the record boundary.
 | |
| 		if err == io.ErrUnexpectedEOF && c.rawInput.Len() == 0 {
 | |
| 			err = io.EOF
 | |
| 		}
 | |
| 		if e, ok := err.(net.Error); !ok || !e.Temporary() {
 | |
| 			c.in.setErrorLocked(err)
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 	hdr := c.rawInput.Bytes()[:recordHeaderLen]
 | |
| 	typ := recordType(hdr[0])
 | |
| 
 | |
| 	// No valid TLS record has a type of 0x80, however SSLv2 handshakes
 | |
| 	// start with a uint16 length where the MSB is set and the first record
 | |
| 	// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
 | |
| 	// an SSLv2 client.
 | |
| 	if !handshakeComplete && typ == 0x80 {
 | |
| 		c.sendAlert(alertProtocolVersion)
 | |
| 		return c.in.setErrorLocked(c.newRecordHeaderError(nil, "unsupported SSLv2 handshake received"))
 | |
| 	}
 | |
| 
 | |
| 	vers := uint16(hdr[1])<<8 | uint16(hdr[2])
 | |
| 	n := int(hdr[3])<<8 | int(hdr[4])
 | |
| 	if c.haveVers && c.vers != VersionTLS13 && vers != c.vers {
 | |
| 		c.sendAlert(alertProtocolVersion)
 | |
| 		msg := fmt.Sprintf("received record with version %x when expecting version %x", vers, c.vers)
 | |
| 		return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
 | |
| 	}
 | |
| 	if !c.haveVers {
 | |
| 		// First message, be extra suspicious: this might not be a TLS
 | |
| 		// client. Bail out before reading a full 'body', if possible.
 | |
| 		// The current max version is 3.3 so if the version is >= 16.0,
 | |
| 		// it's probably not real.
 | |
| 		if (typ != recordTypeAlert && typ != recordTypeHandshake) || vers >= 0x1000 {
 | |
| 			return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
 | |
| 		}
 | |
| 	}
 | |
| 	if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
 | |
| 		c.sendAlert(alertRecordOverflow)
 | |
| 		msg := fmt.Sprintf("oversized record received with length %d", n)
 | |
| 		return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
 | |
| 	}
 | |
| 	if err := c.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
 | |
| 		if e, ok := err.(net.Error); !ok || !e.Temporary() {
 | |
| 			c.in.setErrorLocked(err)
 | |
| 		}
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Process message.
 | |
| 	record := c.rawInput.Next(recordHeaderLen + n)
 | |
| 	data, typ, err := c.in.decrypt(record)
 | |
| 	if err != nil {
 | |
| 		return c.in.setErrorLocked(c.sendAlert(err.(alert)))
 | |
| 	}
 | |
| 	if len(data) > maxPlaintext {
 | |
| 		return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow))
 | |
| 	}
 | |
| 
 | |
| 	// Application Data messages are always protected.
 | |
| 	if c.in.cipher == nil && typ == recordTypeApplicationData {
 | |
| 		return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 	}
 | |
| 
 | |
| 	if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
 | |
| 		// This is a state-advancing message: reset the retry count.
 | |
| 		c.retryCount = 0
 | |
| 	}
 | |
| 
 | |
| 	// Handshake messages MUST NOT be interleaved with other record types in TLS 1.3.
 | |
| 	if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.hand.Len() > 0 {
 | |
| 		return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 	}
 | |
| 
 | |
| 	switch typ {
 | |
| 	default:
 | |
| 		return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 
 | |
| 	case recordTypeAlert:
 | |
| 		if c.quic != nil {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 		if len(data) != 2 {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 		if alert(data[1]) == alertCloseNotify {
 | |
| 			return c.in.setErrorLocked(io.EOF)
 | |
| 		}
 | |
| 		if c.vers == VersionTLS13 {
 | |
| 			return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
 | |
| 		}
 | |
| 		switch data[0] {
 | |
| 		case alertLevelWarning:
 | |
| 			// Drop the record on the floor and retry.
 | |
| 			return c.retryReadRecord(expectChangeCipherSpec)
 | |
| 		case alertLevelError:
 | |
| 			return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
 | |
| 		default:
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 
 | |
| 	case recordTypeChangeCipherSpec:
 | |
| 		if len(data) != 1 || data[0] != 1 {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertDecodeError))
 | |
| 		}
 | |
| 		// Handshake messages are not allowed to fragment across the CCS.
 | |
| 		if c.hand.Len() > 0 {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 		// In TLS 1.3, change_cipher_spec records are ignored until the
 | |
| 		// Finished. See RFC 8446, Appendix D.4. Note that according to Section
 | |
| 		// 5, a server can send a ChangeCipherSpec before its ServerHello, when
 | |
| 		// c.vers is still unset. That's not useful though and suspicious if the
 | |
| 		// server then selects a lower protocol version, so don't allow that.
 | |
| 		if c.vers == VersionTLS13 {
 | |
| 			return c.retryReadRecord(expectChangeCipherSpec)
 | |
| 		}
 | |
| 		if !expectChangeCipherSpec {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 		if err := c.in.changeCipherSpec(); err != nil {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(err.(alert)))
 | |
| 		}
 | |
| 
 | |
| 	case recordTypeApplicationData:
 | |
| 		if !handshakeComplete || expectChangeCipherSpec {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 		// Some OpenSSL servers send empty records in order to randomize the
 | |
| 		// CBC IV. Ignore a limited number of empty records.
 | |
| 		if len(data) == 0 {
 | |
| 			return c.retryReadRecord(expectChangeCipherSpec)
 | |
| 		}
 | |
| 		// Note that data is owned by c.rawInput, following the Next call above,
 | |
| 		// to avoid copying the plaintext. This is safe because c.rawInput is
 | |
| 		// not read from or written to until c.input is drained.
 | |
| 		c.input.Reset(data)
 | |
| 
 | |
| 	case recordTypeHandshake:
 | |
| 		if len(data) == 0 || expectChangeCipherSpec {
 | |
| 			return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 		}
 | |
| 		c.hand.Write(data)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // retryReadRecord recurs into readRecordOrCCS to drop a non-advancing record, like
 | |
| // a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
 | |
| func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error {
 | |
| 	c.retryCount++
 | |
| 	if c.retryCount > maxUselessRecords {
 | |
| 		c.sendAlert(alertUnexpectedMessage)
 | |
| 		return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
 | |
| 	}
 | |
| 	return c.readRecordOrCCS(expectChangeCipherSpec)
 | |
| }
 | |
| 
 | |
| // atLeastReader reads from R, stopping with EOF once at least N bytes have been
 | |
| // read. It is different from an io.LimitedReader in that it doesn't cut short
 | |
| // the last Read call, and in that it considers an early EOF an error.
 | |
| type atLeastReader struct {
 | |
| 	R io.Reader
 | |
| 	N int64
 | |
| }
 | |
| 
 | |
| func (r *atLeastReader) Read(p []byte) (int, error) {
 | |
| 	if r.N <= 0 {
 | |
| 		return 0, io.EOF
 | |
| 	}
 | |
| 	n, err := r.R.Read(p)
 | |
| 	r.N -= int64(n) // won't underflow unless len(p) >= n > 9223372036854775809
 | |
| 	if r.N > 0 && err == io.EOF {
 | |
| 		return n, io.ErrUnexpectedEOF
 | |
| 	}
 | |
| 	if r.N <= 0 && err == nil {
 | |
| 		return n, io.EOF
 | |
| 	}
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| // readFromUntil reads from r into c.rawInput until c.rawInput contains
 | |
| // at least n bytes or else returns an error.
 | |
| func (c *Conn) readFromUntil(r io.Reader, n int) error {
 | |
| 	if c.rawInput.Len() >= n {
 | |
| 		return nil
 | |
| 	}
 | |
| 	needs := n - c.rawInput.Len()
 | |
| 	// There might be extra input waiting on the wire. Make a best effort
 | |
| 	// attempt to fetch it so that it can be used in (*Conn).Read to
 | |
| 	// "predict" closeNotify alerts.
 | |
| 	c.rawInput.Grow(needs + bytes.MinRead)
 | |
| 	_, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)})
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // sendAlert sends a TLS alert message.
 | |
| func (c *Conn) sendAlertLocked(err alert) error {
 | |
| 	if c.quic != nil {
 | |
| 		return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
 | |
| 	}
 | |
| 	switch err {
 | |
| 	case alertNoRenegotiation, alertCloseNotify:
 | |
| 		c.tmp[0] = alertLevelWarning
 | |
| 	default:
 | |
| 		c.tmp[0] = alertLevelError
 | |
| 	}
 | |
| 	c.tmp[1] = byte(err)
 | |
| 
 | |
| 	_, writeErr := c.writeRecordLocked(recordTypeAlert, c.tmp[0:2])
 | |
| 	if err == alertCloseNotify {
 | |
| 		// closeNotify is a special case in that it isn't an error.
 | |
| 		return writeErr
 | |
| 	}
 | |
| 
 | |
| 	return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
 | |
| }
 | |
| 
 | |
| // sendAlert sends a TLS alert message.
 | |
| func (c *Conn) sendAlert(err alert) error {
 | |
| 	c.out.Lock()
 | |
| 	defer c.out.Unlock()
 | |
| 	return c.sendAlertLocked(err)
 | |
| }
 | |
| 
 | |
| const (
 | |
| 	// tcpMSSEstimate is a conservative estimate of the TCP maximum segment
 | |
| 	// size (MSS). A constant is used, rather than querying the kernel for
 | |
| 	// the actual MSS, to avoid complexity. The value here is the IPv6
 | |
| 	// minimum MTU (1280 bytes) minus the overhead of an IPv6 header (40
 | |
| 	// bytes) and a TCP header with timestamps (32 bytes).
 | |
| 	tcpMSSEstimate = 1208
 | |
| 
 | |
| 	// recordSizeBoostThreshold is the number of bytes of application data
 | |
| 	// sent after which the TLS record size will be increased to the
 | |
| 	// maximum.
 | |
| 	recordSizeBoostThreshold = 128 * 1024
 | |
| )
 | |
| 
 | |
| // maxPayloadSizeForWrite returns the maximum TLS payload size to use for the
 | |
| // next application data record. There is the following trade-off:
 | |
| //
 | |
| //   - For latency-sensitive applications, such as web browsing, each TLS
 | |
| //     record should fit in one TCP segment.
 | |
| //   - For throughput-sensitive applications, such as large file transfers,
 | |
| //     larger TLS records better amortize framing and encryption overheads.
 | |
| //
 | |
| // A simple heuristic that works well in practice is to use small records for
 | |
| // the first 1MB of data, then use larger records for subsequent data, and
 | |
| // reset back to smaller records after the connection becomes idle. See "High
 | |
| // Performance Web Networking", Chapter 4, or:
 | |
| // https://www.igvita.com/2013/10/24/optimizing-tls-record-size-and-buffering-latency/
 | |
| //
 | |
| // In the interests of simplicity and determinism, this code does not attempt
 | |
| // to reset the record size once the connection is idle, however.
 | |
| func (c *Conn) maxPayloadSizeForWrite(typ recordType) int {
 | |
| 	if c.config.DynamicRecordSizingDisabled || typ != recordTypeApplicationData {
 | |
| 		return maxPlaintext
 | |
| 	}
 | |
| 
 | |
| 	if c.bytesSent >= recordSizeBoostThreshold {
 | |
| 		return maxPlaintext
 | |
| 	}
 | |
| 
 | |
| 	// Subtract TLS overheads to get the maximum payload size.
 | |
| 	payloadBytes := tcpMSSEstimate - recordHeaderLen - c.out.explicitNonceLen()
 | |
| 	if c.out.cipher != nil {
 | |
| 		switch ciph := c.out.cipher.(type) {
 | |
| 		case cipher.Stream:
 | |
| 			payloadBytes -= c.out.mac.Size()
 | |
| 		case cipher.AEAD:
 | |
| 			payloadBytes -= ciph.Overhead()
 | |
| 		case cbcMode:
 | |
| 			blockSize := ciph.BlockSize()
 | |
| 			// The payload must fit in a multiple of blockSize, with
 | |
| 			// room for at least one padding byte.
 | |
| 			payloadBytes = (payloadBytes & ^(blockSize - 1)) - 1
 | |
| 			// The MAC is appended before padding so affects the
 | |
| 			// payload size directly.
 | |
| 			payloadBytes -= c.out.mac.Size()
 | |
| 		default:
 | |
| 			panic("unknown cipher type")
 | |
| 		}
 | |
| 	}
 | |
| 	if c.vers == VersionTLS13 {
 | |
| 		payloadBytes-- // encrypted ContentType
 | |
| 	}
 | |
| 
 | |
| 	// Allow packet growth in arithmetic progression up to max.
 | |
| 	pkt := c.packetsSent
 | |
| 	c.packetsSent++
 | |
| 	if pkt > 1000 {
 | |
| 		return maxPlaintext // avoid overflow in multiply below
 | |
| 	}
 | |
| 
 | |
| 	n := payloadBytes * int(pkt+1)
 | |
| 	if n > maxPlaintext {
 | |
| 		n = maxPlaintext
 | |
| 	}
 | |
| 	return n
 | |
| }
 | |
| 
 | |
| func (c *Conn) write(data []byte) (int, error) {
 | |
| 	if c.buffering {
 | |
| 		c.sendBuf = append(c.sendBuf, data...)
 | |
| 		return len(data), nil
 | |
| 	}
 | |
| 
 | |
| 	n, err := c.conn.Write(data)
 | |
| 	c.bytesSent += int64(n)
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| func (c *Conn) flush() (int, error) {
 | |
| 	if len(c.sendBuf) == 0 {
 | |
| 		return 0, nil
 | |
| 	}
 | |
| 
 | |
| 	n, err := c.conn.Write(c.sendBuf)
 | |
| 	c.bytesSent += int64(n)
 | |
| 	c.sendBuf = nil
 | |
| 	c.buffering = false
 | |
| 	return n, err
 | |
| }
 | |
| 
 | |
| // outBufPool pools the record-sized scratch buffers used by writeRecordLocked.
 | |
| var outBufPool = sync.Pool{
 | |
| 	New: func() any {
 | |
| 		return new([]byte)
 | |
| 	},
 | |
| }
 | |
| 
 | |
| // writeRecordLocked writes a TLS record with the given type and payload to the
 | |
| // connection and updates the record layer state.
 | |
| func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) {
 | |
| 	if c.quic != nil {
 | |
| 		if typ != recordTypeHandshake {
 | |
| 			return 0, errors.New("tls: internal error: sending non-handshake message to QUIC transport")
 | |
| 		}
 | |
| 		c.quicWriteCryptoData(c.out.level, data)
 | |
| 		if !c.buffering {
 | |
| 			if _, err := c.flush(); err != nil {
 | |
| 				return 0, err
 | |
| 			}
 | |
| 		}
 | |
| 		return len(data), nil
 | |
| 	}
 | |
| 
 | |
| 	outBufPtr := outBufPool.Get().(*[]byte)
 | |
| 	outBuf := *outBufPtr
 | |
| 	defer func() {
 | |
| 		// You might be tempted to simplify this by just passing &outBuf to Put,
 | |
| 		// but that would make the local copy of the outBuf slice header escape
 | |
| 		// to the heap, causing an allocation. Instead, we keep around the
 | |
| 		// pointer to the slice header returned by Get, which is already on the
 | |
| 		// heap, and overwrite and return that.
 | |
| 		*outBufPtr = outBuf
 | |
| 		outBufPool.Put(outBufPtr)
 | |
| 	}()
 | |
| 
 | |
| 	var n int
 | |
| 	for len(data) > 0 {
 | |
| 		m := len(data)
 | |
| 		if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
 | |
| 			m = maxPayload
 | |
| 		}
 | |
| 
 | |
| 		_, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen)
 | |
| 		outBuf[0] = byte(typ)
 | |
| 		vers := c.vers
 | |
| 		if vers == 0 {
 | |
| 			// Some TLS servers fail if the record version is
 | |
| 			// greater than TLS 1.0 for the initial ClientHello.
 | |
| 			vers = VersionTLS10
 | |
| 		} else if vers == VersionTLS13 {
 | |
| 			// TLS 1.3 froze the record layer version to 1.2.
 | |
| 			// See RFC 8446, Section 5.1.
 | |
| 			vers = VersionTLS12
 | |
| 		}
 | |
| 		outBuf[1] = byte(vers >> 8)
 | |
| 		outBuf[2] = byte(vers)
 | |
| 		outBuf[3] = byte(m >> 8)
 | |
| 		outBuf[4] = byte(m)
 | |
| 
 | |
| 		var err error
 | |
| 		outBuf, err = c.out.encrypt(outBuf, data[:m], c.config.rand())
 | |
| 		if err != nil {
 | |
| 			return n, err
 | |
| 		}
 | |
| 		if _, err := c.write(outBuf); err != nil {
 | |
| 			return n, err
 | |
| 		}
 | |
| 		n += m
 | |
| 		data = data[m:]
 | |
| 	}
 | |
| 
 | |
| 	if typ == recordTypeChangeCipherSpec && c.vers != VersionTLS13 {
 | |
| 		if err := c.out.changeCipherSpec(); err != nil {
 | |
| 			return n, c.sendAlertLocked(err.(alert))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| // writeHandshakeRecord writes a handshake message to the connection and updates
 | |
| // the record layer state. If transcript is non-nil the marshalled message is
 | |
| // written to it.
 | |
| func (c *Conn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptHash) (int, error) {
 | |
| 	c.out.Lock()
 | |
| 	defer c.out.Unlock()
 | |
| 
 | |
| 	data, err := msg.marshal()
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	if transcript != nil {
 | |
| 		transcript.Write(data)
 | |
| 	}
 | |
| 
 | |
| 	return c.writeRecordLocked(recordTypeHandshake, data)
 | |
| }
 | |
| 
 | |
| // writeChangeCipherRecord writes a ChangeCipherSpec message to the connection and
 | |
| // updates the record layer state.
 | |
| func (c *Conn) writeChangeCipherRecord() error {
 | |
| 	c.out.Lock()
 | |
| 	defer c.out.Unlock()
 | |
| 	_, err := c.writeRecordLocked(recordTypeChangeCipherSpec, []byte{1})
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| // readHandshakeBytes reads handshake data until c.hand contains at least n bytes.
 | |
| func (c *Conn) readHandshakeBytes(n int) error {
 | |
| 	if c.quic != nil {
 | |
| 		return c.quicReadHandshakeBytes(n)
 | |
| 	}
 | |
| 	for c.hand.Len() < n {
 | |
| 		if err := c.readRecord(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // readHandshake reads the next handshake message from
 | |
| // the record layer. If transcript is non-nil, the message
 | |
| // is written to the passed transcriptHash.
 | |
| func (c *Conn) readHandshake(transcript transcriptHash) (any, error) {
 | |
| 	if err := c.readHandshakeBytes(4); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	data := c.hand.Bytes()
 | |
| 	n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
 | |
| 	if n > maxHandshake {
 | |
| 		c.sendAlertLocked(alertInternalError)
 | |
| 		return nil, c.in.setErrorLocked(fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake))
 | |
| 	}
 | |
| 	if err := c.readHandshakeBytes(4 + n); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	data = c.hand.Next(4 + n)
 | |
| 	return c.unmarshalHandshakeMessage(data, transcript)
 | |
| }
 | |
| 
 | |
| func (c *Conn) unmarshalHandshakeMessage(data []byte, transcript transcriptHash) (handshakeMessage, error) {
 | |
| 	var m handshakeMessage
 | |
| 	switch data[0] {
 | |
| 	case typeHelloRequest:
 | |
| 		m = new(helloRequestMsg)
 | |
| 	case typeClientHello:
 | |
| 		m = new(clientHelloMsg)
 | |
| 	case typeServerHello:
 | |
| 		m = new(serverHelloMsg)
 | |
| 	case typeNewSessionTicket:
 | |
| 		if c.vers == VersionTLS13 {
 | |
| 			m = new(newSessionTicketMsgTLS13)
 | |
| 		} else {
 | |
| 			m = new(newSessionTicketMsg)
 | |
| 		}
 | |
| 	case typeCertificate:
 | |
| 		if c.vers == VersionTLS13 {
 | |
| 			m = new(certificateMsgTLS13)
 | |
| 		} else {
 | |
| 			m = new(certificateMsg)
 | |
| 		}
 | |
| 	case typeCertificateRequest:
 | |
| 		if c.vers == VersionTLS13 {
 | |
| 			m = new(certificateRequestMsgTLS13)
 | |
| 		} else {
 | |
| 			m = &certificateRequestMsg{
 | |
| 				hasSignatureAlgorithm: c.vers >= VersionTLS12,
 | |
| 			}
 | |
| 		}
 | |
| 	case typeCertificateStatus:
 | |
| 		m = new(certificateStatusMsg)
 | |
| 	case typeServerKeyExchange:
 | |
| 		m = new(serverKeyExchangeMsg)
 | |
| 	case typeServerHelloDone:
 | |
| 		m = new(serverHelloDoneMsg)
 | |
| 	case typeClientKeyExchange:
 | |
| 		m = new(clientKeyExchangeMsg)
 | |
| 	case typeCertificateVerify:
 | |
| 		m = &certificateVerifyMsg{
 | |
| 			hasSignatureAlgorithm: c.vers >= VersionTLS12,
 | |
| 		}
 | |
| 	case typeFinished:
 | |
| 		m = new(finishedMsg)
 | |
| 	case typeEncryptedExtensions:
 | |
| 		m = new(encryptedExtensionsMsg)
 | |
| 	case typeEndOfEarlyData:
 | |
| 		m = new(endOfEarlyDataMsg)
 | |
| 	case typeKeyUpdate:
 | |
| 		m = new(keyUpdateMsg)
 | |
| 	default:
 | |
| 		return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 	}
 | |
| 
 | |
| 	// The handshake message unmarshalers
 | |
| 	// expect to be able to keep references to data,
 | |
| 	// so pass in a fresh copy that won't be overwritten.
 | |
| 	data = append([]byte(nil), data...)
 | |
| 
 | |
| 	if !m.unmarshal(data) {
 | |
| 		return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 | |
| 	}
 | |
| 
 | |
| 	if transcript != nil {
 | |
| 		transcript.Write(data)
 | |
| 	}
 | |
| 
 | |
| 	return m, nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	errShutdown = errors.New("tls: protocol is shutdown")
 | |
| )
 | |
| 
 | |
| // Write writes data to the connection.
 | |
| //
 | |
| // As Write calls Handshake, in order to prevent indefinite blocking a deadline
 | |
| // must be set for both Read and Write before Write is called when the handshake
 | |
| // has not yet completed. See SetDeadline, SetReadDeadline, and
 | |
| // SetWriteDeadline.
 | |
| func (c *Conn) Write(b []byte) (int, error) {
 | |
| 	// interlock with Close below
 | |
| 	for {
 | |
| 		x := c.activeCall.Load()
 | |
| 		if x&1 != 0 {
 | |
| 			return 0, net.ErrClosed
 | |
| 		}
 | |
| 		if c.activeCall.CompareAndSwap(x, x+2) {
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	defer c.activeCall.Add(-2)
 | |
| 
 | |
| 	if err := c.Handshake(); err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 
 | |
| 	c.out.Lock()
 | |
| 	defer c.out.Unlock()
 | |
| 
 | |
| 	if err := c.out.err; err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 
 | |
| 	if !c.isHandshakeComplete.Load() {
 | |
| 		return 0, alertInternalError
 | |
| 	}
 | |
| 
 | |
| 	if c.closeNotifySent {
 | |
| 		return 0, errShutdown
 | |
| 	}
 | |
| 
 | |
| 	// TLS 1.0 is susceptible to a chosen-plaintext
 | |
| 	// attack when using block mode ciphers due to predictable IVs.
 | |
| 	// This can be prevented by splitting each Application Data
 | |
| 	// record into two records, effectively randomizing the IV.
 | |
| 	//
 | |
| 	// https://www.openssl.org/~bodo/tls-cbc.txt
 | |
| 	// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
 | |
| 	// https://www.imperialviolet.org/2012/01/15/beastfollowup.html
 | |
| 
 | |
| 	var m int
 | |
| 	if len(b) > 1 && c.vers == VersionTLS10 {
 | |
| 		if _, ok := c.out.cipher.(cipher.BlockMode); ok {
 | |
| 			n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
 | |
| 			if err != nil {
 | |
| 				return n, c.out.setErrorLocked(err)
 | |
| 			}
 | |
| 			m, b = 1, b[1:]
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	n, err := c.writeRecordLocked(recordTypeApplicationData, b)
 | |
| 	return n + m, c.out.setErrorLocked(err)
 | |
| }
 | |
| 
 | |
| // handleRenegotiation processes a HelloRequest handshake message.
 | |
| func (c *Conn) handleRenegotiation() error {
 | |
| 	if c.vers == VersionTLS13 {
 | |
| 		return errors.New("tls: internal error: unexpected renegotiation")
 | |
| 	}
 | |
| 
 | |
| 	msg, err := c.readHandshake(nil)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	helloReq, ok := msg.(*helloRequestMsg)
 | |
| 	if !ok {
 | |
| 		c.sendAlert(alertUnexpectedMessage)
 | |
| 		return unexpectedMessageError(helloReq, msg)
 | |
| 	}
 | |
| 
 | |
| 	if !c.isClient {
 | |
| 		return c.sendAlert(alertNoRenegotiation)
 | |
| 	}
 | |
| 
 | |
| 	switch c.config.Renegotiation {
 | |
| 	case RenegotiateNever:
 | |
| 		return c.sendAlert(alertNoRenegotiation)
 | |
| 	case RenegotiateOnceAsClient:
 | |
| 		if c.handshakes > 1 {
 | |
| 			return c.sendAlert(alertNoRenegotiation)
 | |
| 		}
 | |
| 	case RenegotiateFreelyAsClient:
 | |
| 		// Ok.
 | |
| 	default:
 | |
| 		c.sendAlert(alertInternalError)
 | |
| 		return errors.New("tls: unknown Renegotiation value")
 | |
| 	}
 | |
| 
 | |
| 	c.handshakeMutex.Lock()
 | |
| 	defer c.handshakeMutex.Unlock()
 | |
| 
 | |
| 	c.isHandshakeComplete.Store(false)
 | |
| 	if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
 | |
| 		c.handshakes++
 | |
| 	}
 | |
| 	return c.handshakeErr
 | |
| }
 | |
| 
 | |
| // handlePostHandshakeMessage processes a handshake message arrived after the
 | |
| // handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
 | |
| func (c *Conn) handlePostHandshakeMessage() error {
 | |
| 	if c.vers != VersionTLS13 {
 | |
| 		return c.handleRenegotiation()
 | |
| 	}
 | |
| 
 | |
| 	msg, err := c.readHandshake(nil)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	c.retryCount++
 | |
| 	if c.retryCount > maxUselessRecords {
 | |
| 		c.sendAlert(alertUnexpectedMessage)
 | |
| 		return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
 | |
| 	}
 | |
| 
 | |
| 	switch msg := msg.(type) {
 | |
| 	case *newSessionTicketMsgTLS13:
 | |
| 		return c.handleNewSessionTicket(msg)
 | |
| 	case *keyUpdateMsg:
 | |
| 		return c.handleKeyUpdate(msg)
 | |
| 	}
 | |
| 	// The QUIC layer is supposed to treat an unexpected post-handshake CertificateRequest
 | |
| 	// as a QUIC-level PROTOCOL_VIOLATION error (RFC 9001, Section 4.4). Returning an
 | |
| 	// unexpected_message alert here doesn't provide it with enough information to distinguish
 | |
| 	// this condition from other unexpected messages. This is probably fine.
 | |
| 	c.sendAlert(alertUnexpectedMessage)
 | |
| 	return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
 | |
| }
 | |
| 
 | |
| func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
 | |
| 	if c.quic != nil {
 | |
| 		c.sendAlert(alertUnexpectedMessage)
 | |
| 		return c.in.setErrorLocked(errors.New("tls: received unexpected key update message"))
 | |
| 	}
 | |
| 
 | |
| 	cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
 | |
| 	if cipherSuite == nil {
 | |
| 		return c.in.setErrorLocked(c.sendAlert(alertInternalError))
 | |
| 	}
 | |
| 
 | |
| 	newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
 | |
| 	c.in.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
 | |
| 
 | |
| 	if keyUpdate.updateRequested {
 | |
| 		c.out.Lock()
 | |
| 		defer c.out.Unlock()
 | |
| 
 | |
| 		msg := &keyUpdateMsg{}
 | |
| 		msgBytes, err := msg.marshal()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		_, err = c.writeRecordLocked(recordTypeHandshake, msgBytes)
 | |
| 		if err != nil {
 | |
| 			// Surface the error at the next write.
 | |
| 			c.out.setErrorLocked(err)
 | |
| 			return nil
 | |
| 		}
 | |
| 
 | |
| 		newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
 | |
| 		c.out.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Read reads data from the connection.
 | |
| //
 | |
| // As Read calls Handshake, in order to prevent indefinite blocking a deadline
 | |
| // must be set for both Read and Write before Read is called when the handshake
 | |
| // has not yet completed. See SetDeadline, SetReadDeadline, and
 | |
| // SetWriteDeadline.
 | |
| func (c *Conn) Read(b []byte) (int, error) {
 | |
| 	if err := c.Handshake(); err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	if len(b) == 0 {
 | |
| 		// Put this after Handshake, in case people were calling
 | |
| 		// Read(nil) for the side effect of the Handshake.
 | |
| 		return 0, nil
 | |
| 	}
 | |
| 
 | |
| 	c.in.Lock()
 | |
| 	defer c.in.Unlock()
 | |
| 
 | |
| 	for c.input.Len() == 0 {
 | |
| 		if err := c.readRecord(); err != nil {
 | |
| 			return 0, err
 | |
| 		}
 | |
| 		for c.hand.Len() > 0 {
 | |
| 			if err := c.handlePostHandshakeMessage(); err != nil {
 | |
| 				return 0, err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	n, _ := c.input.Read(b)
 | |
| 
 | |
| 	// If a close-notify alert is waiting, read it so that we can return (n,
 | |
| 	// EOF) instead of (n, nil), to signal to the HTTP response reading
 | |
| 	// goroutine that the connection is now closed. This eliminates a race
 | |
| 	// where the HTTP response reading goroutine would otherwise not observe
 | |
| 	// the EOF until its next read, by which time a client goroutine might
 | |
| 	// have already tried to reuse the HTTP connection for a new request.
 | |
| 	// See https://golang.org/cl/76400046 and https://golang.org/issue/3514
 | |
| 	if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
 | |
| 		recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
 | |
| 		if err := c.readRecord(); err != nil {
 | |
| 			return n, err // will be io.EOF on closeNotify
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| // Close closes the connection.
 | |
| func (c *Conn) Close() error {
 | |
| 	// Interlock with Conn.Write above.
 | |
| 	var x int32
 | |
| 	for {
 | |
| 		x = c.activeCall.Load()
 | |
| 		if x&1 != 0 {
 | |
| 			return net.ErrClosed
 | |
| 		}
 | |
| 		if c.activeCall.CompareAndSwap(x, x|1) {
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	if x != 0 {
 | |
| 		// io.Writer and io.Closer should not be used concurrently.
 | |
| 		// If Close is called while a Write is currently in-flight,
 | |
| 		// interpret that as a sign that this Close is really just
 | |
| 		// being used to break the Write and/or clean up resources and
 | |
| 		// avoid sending the alertCloseNotify, which may block
 | |
| 		// waiting on handshakeMutex or the c.out mutex.
 | |
| 		return c.conn.Close()
 | |
| 	}
 | |
| 
 | |
| 	var alertErr error
 | |
| 	if c.isHandshakeComplete.Load() {
 | |
| 		if err := c.closeNotify(); err != nil {
 | |
| 			alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if err := c.conn.Close(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return alertErr
 | |
| }
 | |
| 
 | |
| var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake complete")
 | |
| 
 | |
| // CloseWrite shuts down the writing side of the connection. It should only be
 | |
| // called once the handshake has completed and does not call CloseWrite on the
 | |
| // underlying connection. Most callers should just use Close.
 | |
| func (c *Conn) CloseWrite() error {
 | |
| 	if !c.isHandshakeComplete.Load() {
 | |
| 		return errEarlyCloseWrite
 | |
| 	}
 | |
| 
 | |
| 	return c.closeNotify()
 | |
| }
 | |
| 
 | |
| func (c *Conn) closeNotify() error {
 | |
| 	c.out.Lock()
 | |
| 	defer c.out.Unlock()
 | |
| 
 | |
| 	if !c.closeNotifySent {
 | |
| 		// Set a Write Deadline to prevent possibly blocking forever.
 | |
| 		c.SetWriteDeadline(time.Now().Add(time.Second * 5))
 | |
| 		c.closeNotifyErr = c.sendAlertLocked(alertCloseNotify)
 | |
| 		c.closeNotifySent = true
 | |
| 		// Any subsequent writes will fail.
 | |
| 		c.SetWriteDeadline(time.Now())
 | |
| 	}
 | |
| 	return c.closeNotifyErr
 | |
| }
 | |
| 
 | |
| // Handshake runs the client or server handshake
 | |
| // protocol if it has not yet been run.
 | |
| //
 | |
| // Most uses of this package need not call Handshake explicitly: the
 | |
| // first Read or Write will call it automatically.
 | |
| //
 | |
| // For control over canceling or setting a timeout on a handshake, use
 | |
| // HandshakeContext or the Dialer's DialContext method instead.
 | |
| func (c *Conn) Handshake() error {
 | |
| 	return c.HandshakeContext(context.Background())
 | |
| }
 | |
| 
 | |
| // HandshakeContext runs the client or server handshake
 | |
| // protocol if it has not yet been run.
 | |
| //
 | |
| // The provided Context must be non-nil. If the context is canceled before
 | |
| // the handshake is complete, the handshake is interrupted and an error is returned.
 | |
| // Once the handshake has completed, cancellation of the context will not affect the
 | |
| // connection.
 | |
| //
 | |
| // Most uses of this package need not call HandshakeContext explicitly: the
 | |
| // first Read or Write will call it automatically.
 | |
| func (c *Conn) HandshakeContext(ctx context.Context) error {
 | |
| 	// Delegate to unexported method for named return
 | |
| 	// without confusing documented signature.
 | |
| 	return c.handshakeContext(ctx)
 | |
| }
 | |
| 
 | |
| func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
 | |
| 	// Fast sync/atomic-based exit if there is no handshake in flight and the
 | |
| 	// last one succeeded without an error. Avoids the expensive context setup
 | |
| 	// and mutex for most Read and Write calls.
 | |
| 	if c.isHandshakeComplete.Load() {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	handshakeCtx, cancel := context.WithCancel(ctx)
 | |
| 	// Note: defer this before starting the "interrupter" goroutine
 | |
| 	// so that we can tell the difference between the input being canceled and
 | |
| 	// this cancellation. In the former case, we need to close the connection.
 | |
| 	defer cancel()
 | |
| 
 | |
| 	if c.quic != nil {
 | |
| 		c.quic.cancelc = handshakeCtx.Done()
 | |
| 		c.quic.cancel = cancel
 | |
| 	} else if ctx.Done() != nil {
 | |
| 		// Start the "interrupter" goroutine, if this context might be canceled.
 | |
| 		// (The background context cannot).
 | |
| 		//
 | |
| 		// The interrupter goroutine waits for the input context to be done and
 | |
| 		// closes the connection if this happens before the function returns.
 | |
| 		done := make(chan struct{})
 | |
| 		interruptRes := make(chan error, 1)
 | |
| 		defer func() {
 | |
| 			close(done)
 | |
| 			if ctxErr := <-interruptRes; ctxErr != nil {
 | |
| 				// Return context error to user.
 | |
| 				ret = ctxErr
 | |
| 			}
 | |
| 		}()
 | |
| 		go func() {
 | |
| 			select {
 | |
| 			case <-handshakeCtx.Done():
 | |
| 				// Close the connection, discarding the error
 | |
| 				_ = c.conn.Close()
 | |
| 				interruptRes <- handshakeCtx.Err()
 | |
| 			case <-done:
 | |
| 				interruptRes <- nil
 | |
| 			}
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	c.handshakeMutex.Lock()
 | |
| 	defer c.handshakeMutex.Unlock()
 | |
| 
 | |
| 	if err := c.handshakeErr; err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if c.isHandshakeComplete.Load() {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	c.in.Lock()
 | |
| 	defer c.in.Unlock()
 | |
| 
 | |
| 	c.handshakeErr = c.handshakeFn(handshakeCtx)
 | |
| 	if c.handshakeErr == nil {
 | |
| 		c.handshakes++
 | |
| 	} else {
 | |
| 		// If an error occurred during the handshake try to flush the
 | |
| 		// alert that might be left in the buffer.
 | |
| 		c.flush()
 | |
| 	}
 | |
| 
 | |
| 	if c.handshakeErr == nil && !c.isHandshakeComplete.Load() {
 | |
| 		c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
 | |
| 	}
 | |
| 	if c.handshakeErr != nil && c.isHandshakeComplete.Load() {
 | |
| 		panic("tls: internal error: handshake returned an error but is marked successful")
 | |
| 	}
 | |
| 
 | |
| 	if c.quic != nil {
 | |
| 		if c.handshakeErr == nil {
 | |
| 			c.quicHandshakeComplete()
 | |
| 			// Provide the 1-RTT read secret now that the handshake is complete.
 | |
| 			// The QUIC layer MUST NOT decrypt 1-RTT packets prior to completing
 | |
| 			// the handshake (RFC 9001, Section 5.7).
 | |
| 			c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret)
 | |
| 		} else {
 | |
| 			var a alert
 | |
| 			c.out.Lock()
 | |
| 			if !errors.As(c.out.err, &a) {
 | |
| 				a = alertInternalError
 | |
| 			}
 | |
| 			c.out.Unlock()
 | |
| 			// Return an error which wraps both the handshake error and
 | |
| 			// any alert error we may have sent, or alertInternalError
 | |
| 			// if we didn't send an alert.
 | |
| 			// Truncate the text of the alert to 0 characters.
 | |
| 			c.handshakeErr = fmt.Errorf("%w%.0w", c.handshakeErr, AlertError(a))
 | |
| 		}
 | |
| 		close(c.quic.blockedc)
 | |
| 		close(c.quic.signalc)
 | |
| 	}
 | |
| 
 | |
| 	return c.handshakeErr
 | |
| }
 | |
| 
 | |
| // ConnectionState returns basic TLS details about the connection.
 | |
| func (c *Conn) ConnectionState() ConnectionState {
 | |
| 	c.handshakeMutex.Lock()
 | |
| 	defer c.handshakeMutex.Unlock()
 | |
| 	return c.connectionStateLocked()
 | |
| }
 | |
| 
 | |
| func (c *Conn) connectionStateLocked() ConnectionState {
 | |
| 	var state connectionState
 | |
| 	state.HandshakeComplete = c.isHandshakeComplete.Load()
 | |
| 	state.Version = c.vers
 | |
| 	state.NegotiatedProtocol = c.clientProtocol
 | |
| 	state.DidResume = c.didResume
 | |
| 	state.NegotiatedProtocolIsMutual = true
 | |
| 	state.ServerName = c.serverName
 | |
| 	state.CipherSuite = c.cipherSuite
 | |
| 	state.PeerCertificates = c.peerCertificates
 | |
| 	state.VerifiedChains = c.verifiedChains
 | |
| 	state.SignedCertificateTimestamps = c.scts
 | |
| 	state.OCSPResponse = c.ocspResponse
 | |
| 	if !c.didResume && c.vers != VersionTLS13 {
 | |
| 		if c.clientFinishedIsFirst {
 | |
| 			state.TLSUnique = c.clientFinished[:]
 | |
| 		} else {
 | |
| 			state.TLSUnique = c.serverFinished[:]
 | |
| 		}
 | |
| 	}
 | |
| 	if c.config.Renegotiation != RenegotiateNever {
 | |
| 		state.ekm = noExportedKeyingMaterial
 | |
| 	} else {
 | |
| 		state.ekm = c.ekm
 | |
| 	}
 | |
| 	return toConnectionState(state)
 | |
| }
 | |
| 
 | |
| // OCSPResponse returns the stapled OCSP response from the TLS server, if
 | |
| // any. (Only valid for client connections.)
 | |
| func (c *Conn) OCSPResponse() []byte {
 | |
| 	c.handshakeMutex.Lock()
 | |
| 	defer c.handshakeMutex.Unlock()
 | |
| 
 | |
| 	return c.ocspResponse
 | |
| }
 | |
| 
 | |
| // VerifyHostname checks that the peer certificate chain is valid for
 | |
| // connecting to host. If so, it returns nil; if not, it returns an error
 | |
| // describing the problem.
 | |
| func (c *Conn) VerifyHostname(host string) error {
 | |
| 	c.handshakeMutex.Lock()
 | |
| 	defer c.handshakeMutex.Unlock()
 | |
| 	if !c.isClient {
 | |
| 		return errors.New("tls: VerifyHostname called on TLS server connection")
 | |
| 	}
 | |
| 	if !c.isHandshakeComplete.Load() {
 | |
| 		return errors.New("tls: handshake has not yet been performed")
 | |
| 	}
 | |
| 	if len(c.verifiedChains) == 0 {
 | |
| 		return errors.New("tls: handshake did not verify certificate chain")
 | |
| 	}
 | |
| 	return c.peerCertificates[0].VerifyHostname(host)
 | |
| }
 |