1
0
mirror of https://github.com/fumiama/terasu.git synced 2026-06-05 01:00:23 +08:00
Files
terasu/handshake_1.20.go
2025-09-30 23:16:01 +08:00

329 lines
9.5 KiB
Go

//go:build !go1.21
package terasu
import (
"context"
"crypto/ecdh"
"crypto/tls"
"crypto/x509"
"errors"
"hash"
"time"
"unsafe"
)
//go:linkname defaultConfig crypto/tls.defaultConfig
func defaultConfig() *tls.Config
type clientHelloMsg struct {
raw []byte
vers uint16
random []byte
sessionId []byte
cipherSuites []uint16
compressionMethods []uint8
serverName string
}
//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 makeClientHello crypto/tls.(*Conn).makeClientHello
func makeClientHello(c *_trsconn) (*clientHelloMsg, *ecdh.PrivateKey, error)
func (c *_trsconn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
return makeClientHello(c)
}
// ClientSessionState contains the state needed by clients to resume TLS
// sessions.
type sessionState struct {
sessionTicket []uint8 // Encrypted ticket used for session resumption with server
vers uint16 // TLS version negotiated for the session
cipherSuite uint16 // Ciphersuite negotiated for the session
masterSecret []byte // Full handshake MasterSecret, or TLS 1.3 resumption_master_secret
serverCertificates []*x509.Certificate // Certificate chain presented by the server
verifiedChains [][]*x509.Certificate // Certificate chains we built for verification
receivedAt time.Time // When the session ticket was received from the server
ocspResponse []byte // Stapled OCSP response presented by the server
scts [][]byte // SCTs presented by the server
// TLS 1.3 fields.
nonce []byte // Ticket nonce sent by the server, to derive PSK
useBy time.Time // Expiration of the ticket lifetime as set by the server
ageAdd uint32 // Random obfuscation factor for sending the ticket age
}
//go:linkname loadSession crypto/tls.(*Conn).loadSession
func loadSession(c *_trsconn, hello *clientHelloMsg) (cacheKey string,
session *sessionState, earlySecret, binderKey []byte, err error,
)
func (c *_trsconn) loadSession(hello *clientHelloMsg) (cacheKey string,
session *sessionState, earlySecret, binderKey []byte, err error,
) {
return loadSession(c, hello)
}
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
//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)
}
type serverHelloMsg struct {
raw []byte
vers uint16
random []byte
}
//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
ecdheKey *ecdh.PrivateKey
session *sessionState
earlySecret []byte
binderKey []byte
certReq unsafe.Pointer
usingPSK bool
sentDummyCCS bool
suite unsafe.Pointer
transcript hash.Hash
masterSecret []byte
trafficSecret []byte // client_application_traffic_secret_0
}
//go:linkname handshake13 crypto/tls.(*clientHandshakeStateTLS13).handshake
func handshake13(hs *clientHandshakeStateTLS13) error
func (hs *clientHandshakeStateTLS13) handshake() error {
return handshake13(hs)
}
// 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 func(result, secret, label, seed []byte)
}
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)
}
// 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, ecdheKey, err := c.makeClientHello()
if err != nil {
return err
}
c.serverName = hello.serverName
cacheKey, session, earlySecret, binderKey, err := c.loadSession(hello)
if err != nil {
return err
}
if cacheKey != "" && 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 {
c.config.ClientSessionCache.Put(cacheKey, nil)
}
}()
}
if _, err := c.writeHandshakeRecord(hello, nil, firstFragmentLen); err != nil {
return err
}
// 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,
ecdheKey: ecdheKey,
session: session,
earlySecret: earlySecret,
binderKey: binderKey,
}
// 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
}
// If we had a successful handshake and hs.session is different from
// the one already cached - cache a new one.
if cacheKey != "" && hs.session != nil && session != hs.session {
c.config.ClientSessionCache.Put(cacheKey, (*tls.ClientSessionState)(unsafe.Pointer(hs.session)))
}
return nil
}
}