1
0
mirror of https://github.com/fumiama/terasu.git synced 2026-06-05 01:00:23 +08:00

feat: support go1.24&go1.25

This commit is contained in:
源文雨
2025-09-30 23:16:01 +08:00
parent 3ac40e382e
commit 1d9b679c36
7 changed files with 898 additions and 12 deletions

View File

@@ -148,10 +148,10 @@ type clientHandshakeStateTLS13 struct {
earlySecret []byte earlySecret []byte
binderKey []byte binderKey []byte
certReq *uintptr certReq unsafe.Pointer
usingPSK bool usingPSK bool
sentDummyCCS bool sentDummyCCS bool
suite *uintptr suite unsafe.Pointer
transcript hash.Hash transcript hash.Hash
masterSecret []byte masterSecret []byte
trafficSecret []byte // client_application_traffic_secret_0 trafficSecret []byte // client_application_traffic_secret_0
@@ -186,7 +186,7 @@ type clientHandshakeState struct {
ctx context.Context ctx context.Context
serverHello *serverHelloMsg serverHello *serverHelloMsg
hello *clientHelloMsg hello *clientHelloMsg
suite *uintptr suite unsafe.Pointer
finishedHash finishedHash finishedHash finishedHash
masterSecret []byte masterSecret []byte
session *sessionState // the session being resumed session *sessionState // the session being resumed

View File

@@ -1,4 +1,4 @@
//go:build go1.21 //go:build go1.21 && !go1.24
package terasu package terasu
@@ -240,7 +240,7 @@ type clientHandshakeStateTLS13 struct {
earlySecret []byte earlySecret []byte
binderKey []byte binderKey []byte
certReq *uintptr certReq unsafe.Pointer
usingPSK bool usingPSK bool
sentDummyCCS bool sentDummyCCS bool
suite *cipherSuiteTLS13 suite *cipherSuiteTLS13
@@ -278,7 +278,7 @@ type clientHandshakeState struct {
ctx context.Context ctx context.Context
serverHello *serverHelloMsg serverHello *serverHelloMsg
hello *clientHelloMsg hello *clientHelloMsg
suite *uintptr suite unsafe.Pointer
finishedHash finishedHash finishedHash finishedHash
masterSecret []byte masterSecret []byte
session *sessionState // the session being resumed session *sessionState // the session being resumed

609
handshake_1.24.go Normal file
View File

@@ -0,0 +1,609 @@
//go:build go1.24
package terasu
import (
"context"
"crypto"
"crypto/cipher"
"crypto/ecdh"
"crypto/mlkem"
"crypto/tls"
"crypto/x509"
"errors"
"hash"
"io"
"unsafe"
)
//go:linkname defaultConfig crypto/tls.defaultConfig
func defaultConfig() *tls.Config
// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved
// session. See RFC 8446, Section 4.2.11.
type pskIdentity struct {
label []byte
obfuscatedTicketAge uint32
}
type clientHelloMsg struct {
original []byte
vers uint16
random []byte
sessionId []byte
cipherSuites []uint16
compressionMethods []uint8
serverName string
ocspStapling bool
supportedCurves []tls.CurveID
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
supportedSignatureAlgorithms []tls.SignatureScheme
supportedSignatureAlgorithmsCert []tls.SignatureScheme
secureRenegotiationSupported bool
secureRenegotiation []byte
extendedMasterSecret bool
alpnProtocols []string
scts bool
supportedVersions []uint16
cookie []byte
keyShares []byte
earlyData bool
pskModes []uint8
pskIdentities []pskIdentity
pskBinders [][]byte
quicTransportParameters []byte
encryptedClientHello []byte
// extensions are only populated on the server-side of a handshake
extensions []uint16
}
//go:linkname marshal crypto/tls.(*clientHelloMsg).marshal
func marshal(m *clientHelloMsg) ([]byte, error)
func (m *clientHelloMsg) marshal() ([]byte, error) {
return marshal(m)
}
//go:linkname unmarshal crypto/tls.(*clientHelloMsg).unmarshal
func unmarshal(m *clientHelloMsg, data []byte) bool
func (m *clientHelloMsg) unmarshal(data []byte) bool {
return unmarshal(m, data)
}
//go:linkname clone crypto/tls.(*clientHelloMsg).clone
func clone(m *clientHelloMsg) *clientHelloMsg
func (m *clientHelloMsg) clone() *clientHelloMsg {
return clone(m)
}
type keySharePrivateKeys struct {
curveID tls.CurveID
ecdhe *ecdh.PrivateKey
mlkem *mlkem.DecapsulationKey768
}
type echCipher struct {
KDFID uint16
AEADID uint16
}
type echExtension struct {
Type uint16
Data []byte
}
type echConfig struct {
raw []byte
Version uint16
Length uint16
ConfigID uint8
KemID uint16
PublicKey []byte
SymmetricCipherSuite []echCipher
MaxNameLength uint8
PublicName []byte
Extensions []echExtension
}
type uint128 struct {
hi, lo uint64
}
type hpkecontext struct {
aead cipher.AEAD
sharedSecret []byte
suiteID []byte
key []byte
baseNonce []byte
exporterSecret []byte
seqNum uint128
}
type hpkeSender struct {
*hpkecontext
}
type echClientContext struct {
config *echConfig
hpkeContext *hpkeSender
encapsulatedKey []byte
innerHello *clientHelloMsg
innerTranscript hash.Hash
kdfID uint16
aeadID uint16
echRejected bool
retryConfigs []byte
}
//go:linkname makeClientHello crypto/tls.(*Conn).makeClientHello
func makeClientHello(c *_trsconn) (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error)
func (c *_trsconn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
return makeClientHello(c)
}
// activeCert is a handle to a certificate held in the cache. Once there are
// no alive activeCerts for a given certificate, the certificate is removed
// from the cache by a finalizer.
type activeCert struct {
cert *x509.Certificate
}
// A sessionState is a resumable session.
type sessionState struct {
// Encoded as a SessionState (in the language of RFC 8446, Section 3).
//
// enum { server(1), client(2) } SessionStateType;
//
// opaque Certificate<1..2^24-1>;
//
// Certificate CertificateChain<0..2^24-1>;
//
// opaque Extra<0..2^24-1>;
//
// struct {
// uint16 version;
// SessionStateType type;
// uint16 cipher_suite;
// uint64 created_at;
// opaque secret<1..2^8-1>;
// Extra extra<0..2^24-1>;
// uint8 ext_master_secret = { 0, 1 };
// uint8 early_data = { 0, 1 };
// CertificateEntry certificate_list<0..2^24-1>;
// CertificateChain verified_chains<0..2^24-1>; /* excluding leaf */
// select (SessionState.early_data) {
// case 0: Empty;
// case 1: opaque alpn<1..2^8-1>;
// };
// select (SessionState.type) {
// case server: Empty;
// case client: struct {
// select (SessionState.version) {
// case VersionTLS10..VersionTLS12: Empty;
// case VersionTLS13: struct {
// uint64 use_by;
// uint32 age_add;
// };
// };
// };
// };
// } SessionState;
//
// Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
// and parsed by [ParseSessionState].
//
// This allows [Config.UnwrapSession]/[Config.WrapSession] and
// [ClientSessionCache] implementations to store and retrieve additional
// data alongside this session.
//
// To allow different layers in a protocol stack to share this field,
// applications must only append to it, not replace it, and must use entries
// that can be recognized even if out of order (for example, by starting
// with an id and version prefix).
Extra [][]byte
// EarlyData indicates whether the ticket can be used for 0-RTT in a QUIC
// connection. The application may set this to false if it is true to
// decline to offer 0-RTT even if supported.
EarlyData bool
version uint16
isClient bool
cipherSuite uint16
// createdAt is the generation time of the secret on the sever (which for
// TLS 1.01.2 might be earlier than the current session) and the time at
// which the ticket was received on the client.
createdAt uint64 // seconds since UNIX epoch
secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
extMasterSecret bool
peerCertificates []*x509.Certificate
activeCertHandles []*activeCert
ocspResponse []byte
scts [][]byte
verifiedChains [][]*x509.Certificate
alpnProtocol string // only set if EarlyData is true
// Client-side TLS 1.3-only fields.
useBy uint64 // seconds since UNIX epoch
ageAdd uint32
ticket []byte
}
type earlySecret struct {
secret []byte
hash func() any
}
//go:linkname clientEarlyTrafficSecret crypto/internal/fips140/tls13.(*EarlySecret).ClientEarlyTrafficSecret
func clientEarlyTrafficSecret(s *earlySecret, transcript any) []byte
//go:linkname loadSession crypto/tls.(*Conn).loadSession
func loadSession(c *_trsconn, hello *clientHelloMsg) (
session *sessionState, earlySecret *earlySecret, binderKey []byte, err error,
)
func (c *_trsconn) loadSession(hello *clientHelloMsg) (
session *sessionState, earlySecret *earlySecret, binderKey []byte, err error,
) {
return loadSession(c, hello)
}
//go:linkname clientSessionCacheKey crypto/tls.(*Conn).clientSessionCacheKey
func clientSessionCacheKey(c *_trsconn) string
func (c *_trsconn) clientSessionCacheKey() string {
return clientSessionCacheKey(c)
}
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
type cipherSuiteTLS13 struct {
id uint16
keyLen int
aead func(key, fixedNonce []byte) any
hash crypto.Hash
}
//go:linkname deriveSecret crypto/tls.(*cipherSuiteTLS13).deriveSecret
func deriveSecret(c *cipherSuiteTLS13, secret []byte, label string, transcript hash.Hash) []byte
func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
return deriveSecret(c, secret, label, transcript)
}
//go:linkname cipherSuiteTLS13ByID crypto/tls.cipherSuiteTLS13ByID
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13
type handshakeMessage interface {
marshal() ([]byte, error)
unmarshal([]byte) bool
}
type transcriptHash interface {
Write([]byte) (int, error)
}
//go:linkname transcriptMsg crypto/tls.transcriptMsg
func transcriptMsg(msg handshakeMessage, h transcriptHash) error
const clientEarlyTrafficLabel = "c e traffic"
//go:linkname quicSetWriteSecret crypto/tls.(*Conn).quicSetWriteSecret
func quicSetWriteSecret(c *_trsconn, level tls.QUICEncryptionLevel, suite uint16, secret []byte)
//go:linkname readHandshake crypto/tls.(*Conn).readHandshake
func readHandshake(c *_trsconn, transcript transcriptHash) (any, error)
func (c *_trsconn) readHandshake(transcript transcriptHash) (any, error) {
return readHandshake(c, transcript)
}
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
type keyShare struct {
group tls.CurveID
data []byte
}
type serverHelloMsg struct {
original []byte
vers uint16
random []byte
sessionId []byte
cipherSuite uint16
compressionMethod uint8
ocspStapling bool
ticketSupported bool
secureRenegotiationSupported bool
secureRenegotiation []byte
extendedMasterSecret bool
alpnProtocol string
scts [][]byte
supportedVersion uint16
serverShare keyShare
selectedIdentityPresent bool
selectedIdentity uint16
supportedPoints []uint8
encryptedClientHello []byte
serverNameAck bool
// HelloRetryRequest extensions
cookie []byte
selectedGroup tls.CurveID
}
//go:linkname sendAlert crypto/tls.(*Conn).sendAlert
func sendAlert(c *_trsconn, err alert) error
func (c *_trsconn) sendAlert(err alert) error {
return sendAlert(c, err)
}
//go:linkname unexpectedMessageError crypto/tls.unexpectedMessageError
func unexpectedMessageError(wanted, got any) error
const (
alertUnexpectedMessage alert = 10
alertIllegalParameter alert = 47
)
//go:linkname pickTLSVersion crypto/tls.(*Conn).pickTLSVersion
func pickTLSVersion(c *_trsconn, serverHello *serverHelloMsg) error
func (c *_trsconn) pickTLSVersion(serverHello *serverHelloMsg) error {
return pickTLSVersion(c, serverHello)
}
//go:linkname maxSupportedVersion crypto/tls.(*Config).maxSupportedVersion
func maxSupportedVersion(c *tls.Config, isClient bool) uint16
const roleClient = true
const (
// downgradeCanaryTLS12 or downgradeCanaryTLS11 is embedded in the server
// random as a downgrade protection if the server would be capable of
// negotiating a higher version. See RFC 8446, Section 4.1.3.
downgradeCanaryTLS12 = "DOWNGRD\x01"
downgradeCanaryTLS11 = "DOWNGRD\x00"
)
type clientHandshakeStateTLS13 struct {
c *Conn
ctx context.Context
serverHello *serverHelloMsg
hello *clientHelloMsg
keyShareKeys *keySharePrivateKeys
session *sessionState
earlySecret *earlySecret
binderKey []byte
certReq unsafe.Pointer
usingPSK bool
sentDummyCCS bool
suite *cipherSuiteTLS13
transcript hash.Hash
masterSecret unsafe.Pointer
trafficSecret []byte // client_application_traffic_secret_0
echContext *echClientContext
}
//go:linkname handshake13 crypto/tls.(*clientHandshakeStateTLS13).handshake
func handshake13(hs *clientHandshakeStateTLS13) error
func (hs *clientHandshakeStateTLS13) handshake() error {
return handshake13(hs)
}
type prfFunc func(secret []byte, label string, seed []byte, keyLen int) []byte
// A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type finishedHash struct {
client hash.Hash
server hash.Hash
// Prior to TLS 1.2, an additional MD5 hash is required.
clientMD5 hash.Hash
serverMD5 hash.Hash
// In TLS 1.2, a full buffer is sadly required.
buffer []byte
version uint16
prf prfFunc
}
type clientHandshakeState struct {
c *Conn
ctx context.Context
serverHello *serverHelloMsg
hello *clientHelloMsg
suite unsafe.Pointer
finishedHash finishedHash
masterSecret []byte
session *sessionState // the session being resumed
ticket []byte // a fresh ticket received during this handshake
}
//go:linkname handshake crypto/tls.(*clientHandshakeState).handshake
func handshake(hs *clientHandshakeState) error
func (hs *clientHandshakeState) handshake() error {
return handshake(hs)
}
//go:linkname computeAndUpdateOuterECHExtension crypto/tls.computeAndUpdateOuterECHExtension
func computeAndUpdateOuterECHExtension(outer, inner *clientHelloMsg, ech *echClientContext, useKey bool) error
// 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 *_trsconn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptHash, firstFragmentLen uint8) (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, firstFragmentLen, data)
}
func (cout *Conn) clientHandshake(firstFragmentLen uint8) func(context.Context) error {
return func(ctx context.Context) (err error) {
c := (*_trsconn)(unsafe.Pointer(cout))
if c.config == nil {
c.config = defaultConfig()
}
// This may be a renegotiation handshake, in which case some fields
// need to be reset.
c.didResume = false
hello, keyShareKeys, ech, err := c.makeClientHello()
if err != nil {
return err
}
c.serverName = hello.serverName
session, earlySecret, binderKey, err := c.loadSession(hello)
if err != nil {
return err
}
if session != nil {
defer func() {
// If we got a handshake failure when resuming a session, throw away
// the session ticket. See RFC 5077, Section 3.2.
//
// RFC 8446 makes no mention of dropping tickets on failure, but it
// does require servers to abort on invalid binders, so we need to
// delete tickets to recover from a corrupted PSK.
if err != nil {
if cacheKey := c.clientSessionCacheKey(); cacheKey != "" {
c.config.ClientSessionCache.Put(cacheKey, nil)
}
}
}()
}
if ech != nil {
// Split hello into inner and outer
ech.innerHello = hello.clone()
// Overwrite the server name in the outer hello with the public facing
// name.
hello.serverName = string(ech.config.PublicName)
// Generate a new random for the outer hello.
hello.random = make([]byte, 32)
_, err = io.ReadFull(tlsConfigRand(c.config), hello.random)
if err != nil {
return errors.New("tls: short read from Rand: " + err.Error())
}
// NOTE: we don't do PSK GREASE, in line with boringssl, it's meant to
// work around _possibly_ broken middleboxes, but there is little-to-no
// evidence that this is actually a problem.
if err := computeAndUpdateOuterECHExtension(hello, ech.innerHello, ech, true); err != nil {
return err
}
}
c.serverName = hello.serverName
if _, err := c.writeHandshakeRecord(hello, nil, firstFragmentLen); err != nil {
return err
}
if hello.earlyData {
suite := cipherSuiteTLS13ByID(session.cipherSuite)
transcript := suite.hash.New()
if err := transcriptMsg(hello, transcript); err != nil {
return err
}
earlyTrafficSecret := clientEarlyTrafficSecret(earlySecret, transcript)
quicSetWriteSecret(c, tls.QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret)
}
// serverHelloMsg is not included in the transcript
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
var serverHello *serverHelloMsg
if !isTypeEqual(msg, "*tls.serverHelloMsg") {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(serverHello, msg)
}
serverHello = (*serverHelloMsg)(*(*unsafe.Pointer)(
unsafe.Add(unsafe.Pointer(&msg), unsafe.Sizeof(uintptr(0))),
))
if err := c.pickTLSVersion(serverHello); err != nil {
return err
}
// If we are negotiating a protocol version that's lower than what we
// support, check for the server downgrade canaries.
// See RFC 8446, Section 4.1.3.
maxVers := maxSupportedVersion(c.config, roleClient)
tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
if maxVers == tls.VersionTLS13 && c.vers <= tls.VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
maxVers == tls.VersionTLS12 && c.vers <= tls.VersionTLS11 && tls11Downgrade {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
}
if c.vers == tls.VersionTLS13 {
hs := &clientHandshakeStateTLS13{
c: cout,
ctx: ctx,
serverHello: serverHello,
hello: hello,
keyShareKeys: keyShareKeys,
session: session,
earlySecret: earlySecret,
binderKey: binderKey,
echContext: ech,
}
// In TLS 1.3, session tickets are delivered after the handshake.
return hs.handshake()
}
hs := &clientHandshakeState{
c: cout,
ctx: ctx,
serverHello: serverHello,
hello: hello,
session: session,
}
if err := hs.handshake(); err != nil {
return err
}
return nil
}
}

View File

@@ -87,7 +87,7 @@ type _trsconn struct {
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
// activeCertHandles contains the cache handles to certificates in // activeCertHandles contains the cache handles to certificates in
// peerCertificates that are used to track active references. // peerCertificates that are used to track active references.
activeCertHandles []*uintptr activeCertHandles []unsafe.Pointer
// verifiedChains contains the certificate chains that we built, as // verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server. // opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate verifiedChains [][]*x509.Certificate

View File

@@ -66,7 +66,7 @@ type _trsconn struct {
conn net.Conn conn net.Conn
isClient bool isClient bool
handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
quic *uintptr // nil for non-QUIC connections quic unsafe.Pointer // nil for non-QUIC connections
// isHandshakeComplete is true if the connection is currently transferring // isHandshakeComplete is true if the connection is currently transferring
// application data (i.e. is not currently processing a handshake). // application data (i.e. is not currently processing a handshake).
@@ -90,7 +90,7 @@ type _trsconn struct {
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
// activeCertHandles contains the cache handles to certificates in // activeCertHandles contains the cache handles to certificates in
// peerCertificates that are used to track active references. // peerCertificates that are used to track active references.
activeCertHandles []*uintptr activeCertHandles []unsafe.Pointer
// verifiedChains contains the certificate chains that we built, as // verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server. // opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate verifiedChains [][]*x509.Certificate

View File

@@ -1,4 +1,4 @@
//go:build go1.23 //go:build go1.23 && !go1.24
package terasu package terasu
@@ -66,7 +66,7 @@ type _trsconn struct {
conn net.Conn conn net.Conn
isClient bool isClient bool
handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
quic *uintptr // nil for non-QUIC connections quic unsafe.Pointer // nil for non-QUIC connections
// isHandshakeComplete is true if the connection is currently transferring // isHandshakeComplete is true if the connection is currently transferring
// application data (i.e. is not currently processing a handshake). // application data (i.e. is not currently processing a handshake).
@@ -92,7 +92,7 @@ type _trsconn struct {
peerCertificates []*x509.Certificate peerCertificates []*x509.Certificate
// activeCertHandles contains the cache handles to certificates in // activeCertHandles contains the cache handles to certificates in
// peerCertificates that are used to track active references. // peerCertificates that are used to track active references.
activeCertHandles []*uintptr activeCertHandles []unsafe.Pointer
// verifiedChains contains the certificate chains that we built, as // verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server. // opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate verifiedChains [][]*x509.Certificate

277
tls_1.24.go Normal file
View File

@@ -0,0 +1,277 @@
//go:build go1.24
package terasu
import (
"context"
"crypto/tls"
"crypto/x509"
"hash"
"io"
"net"
"sync"
"sync/atomic"
"unsafe"
_ "unsafe"
)
type recordType uint8
const (
recordTypeChangeCipherSpec recordType = 20
recordTypeAlert recordType = 21
recordTypeHandshake recordType = 22
recordTypeApplicationData recordType = 23
)
const (
recordHeaderLen = 5 // record header length
)
type alert uint8
//go:linkname tlsConfigRand crypto/tls.(*Config).rand
func tlsConfigRand(c *tls.Config) io.Reader
//go:linkname alertError tls.(tls.alert).Error
func alertError(e alert) string
func (e alert) Error() string {
return alertError(e)
}
// 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 tls.QUICEncryptionLevel // current QUIC encryption level
trafficSecret []byte // current TLS 1.3 traffic secret
}
type Conn tls.Conn
// A _trsconn represents a secured connection.
// It implements the net._trsconn interface.
type _trsconn struct {
// constant
conn net.Conn
isClient bool
handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
quic unsafe.Pointer // 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 *tls.Config // configuration passed to constructor
// 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
extMasterSecret bool
didResume bool // whether this connection was a session resumption
didHRR bool // whether a HelloRetryRequest was sent/received
cipherSuite uint16
curveID tls.CurveID
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 []unsafe.Pointer
// 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
echAccepted bool
// 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 []byte
// 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
}
//go:linkname outBufPool crypto/tls.outBufPool
var outBufPool sync.Pool
//go:linkname tlsWriteRecordLocked crypto/tls.(*Conn).writeRecordLocked
func tlsWriteRecordLocked(c *_trsconn, typ recordType, data []byte) (int, error)
//go:linkname maxPayloadSizeForWrite crypto/tls.(*Conn).maxPayloadSizeForWrite
func maxPayloadSizeForWrite(c *_trsconn, typ recordType) int
func (c *_trsconn) maxPayloadSizeForWrite(typ recordType) int {
return maxPayloadSizeForWrite(c, typ)
}
//go:linkname sliceForAppend crypto/tls.sliceForAppend
func sliceForAppend(in []byte, n int) (head, tail []byte)
//go:linkname encrypt crypto/tls.(*halfConn).encrypt
func encrypt(hc *halfConn, record, payload []byte, rand io.Reader) ([]byte, error)
func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) {
return encrypt(hc, record, payload, rand)
}
//go:linkname rand crypto/tls.(*Config).rand
func rand(c *tls.Config) io.Reader
//go:linkname write crypto/tls.(*Conn).write
func write(c *_trsconn, data []byte) (int, error)
func (c *_trsconn) write(data []byte) (int, error) {
return write(c, data)
}
//go:linkname flush crypto/tls.(*Conn).flush
func flush(c *_trsconn) (int, error)
func (c *_trsconn) flush() (int, error) {
return flush(c)
}
//go:linkname changeCipherSpec crypto/tls.(*halfConn).changeCipherSpec
func changeCipherSpec(hc *halfConn) error
func (hc *halfConn) changeCipherSpec() error {
return changeCipherSpec(hc)
}
//go:linkname sendAlertLocked crypto/tls.(*Conn).sendAlertLocked
func sendAlertLocked(c *_trsconn, err alert) error
func (c *_trsconn) sendAlertLocked(err alert) error {
return sendAlertLocked(c, err)
}
// writeRecordLocked writes a TLS record with the given type and payload to the
// connection and updates the record layer state.
func (c *_trsconn) writeRecordLocked(typ recordType, firstFragmentLen uint8, data []byte) (int, error) {
if c.quic != nil {
return tlsWriteRecordLocked(c, typ, data)
}
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
isFirstLoop := true
for len(data) > 0 {
m := len(data)
if !isFirstLoop {
if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload {
m = maxPayload
}
} else {
m = int(firstFragmentLen)
}
_, 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 = tls.VersionTLS10
} else if vers == tls.VersionTLS13 {
// TLS 1.3 froze the record layer version to 1.2.
// See RFC 8446, Section 5.1.
vers = tls.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], rand(c.config))
if err != nil {
return n, err
}
if _, err := c.write(outBuf); err != nil {
return n, err
}
n += m
data = data[m:]
if isFirstLoop {
isFirstLoop = false
if _, err := c.flush(); err != nil {
return n, err
}
}
}
if typ == recordTypeChangeCipherSpec && c.vers != tls.VersionTLS13 {
if err := c.out.changeCipherSpec(); err != nil {
return n, c.sendAlertLocked(alert(
*(*uintptr)(
unsafe.Add(unsafe.Pointer(&err), unsafe.Sizeof(uintptr(0))),
),
))
}
}
return n, nil
}