yuhan6665
2026-09-20 21:57:26 +00:00
committed by RPRX
parent 8cdf7bf9c7
commit 9124ee149d
25 changed files with 1491 additions and 1118 deletions
+218 -75
View File
@@ -25,11 +25,13 @@ import (
// A Conn represents a secured connection.
// It implements the net.Conn interface.
type Conn struct {
//////////////////////////////////// [REALITY] SECTION: define var
AuthKey []byte
ClientVer [3]byte
ClientTime time.Time
ClientShortId [8]byte
MaxUselessRecords int
//////////////////////////////////// [REALITY] SECTION END
// constant
conn net.Conn
@@ -56,9 +58,11 @@ type Conn struct {
didHRR bool // whether a HelloRetryRequest was sent/received
cipherSuite uint16
curveID CurveID
peerSigAlg SignatureScheme
ocspResponse []byte // stapled OCSP response
scts [][]byte // signed certificate timestamps from server
peerCertificates []*x509.Certificate
localCertificate [][]byte
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
@@ -103,12 +107,27 @@ type Conn struct {
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
in, out halfConn
// rawInput holds raw input, starting with a record header.
// It is nil when no input is buffered, in which case the buffer has
// been returned to rawInputPool so that connections idle in Read do
// not pin a record-sized buffer. It is lazily repopulated from the
// pool by readFromUntil.
rawInput *bytes.Buffer
// smallInput is a small buffer that serves as rawInput while
// waiting for a record header after rawInput has been returned to
// rawInputPool. It is lazily allocated by readFromUntil and then
// kept for the life of the connection.
smallInput *bytes.Buffer
// input holds application data waiting to be read, from rawInput.Next.
input bytes.Reader
// hand holds handshake data waiting to be read.
// It is nil when no handshake data is buffered, in which case the
// buffer has been returned to handPool. Use handBuf and handLen to
// access it.
hand *bytes.Buffer
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.
@@ -171,8 +190,10 @@ func (c *Conn) NetConn() net.Conn {
// A halfConn represents one direction of the record layer
// connection, either sending or receiving.
type halfConn struct {
//////////////////////////////////// [REALITY] SECTION: define var
handshakeLen [7]int
handshakeBuf []byte
//////////////////////////////////// [REALITY] SECTION END
sync.Mutex
@@ -227,20 +248,19 @@ func (hc *halfConn) changeCipherSpec() error {
hc.mac = hc.nextMac
hc.nextCipher = nil
hc.nextMac = nil
for i := range hc.seq {
hc.seq[i] = 0
}
clear(hc.seq[:])
return nil
}
// setTrafficSecret sets the traffic secret for the given encryption level. setTrafficSecret
// should not be called directly, but rather through the Conn setWriteTrafficSecret and
// setReadTrafficSecret wrapper methods.
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
}
clear(hc.seq[:])
}
// incSeq increments the sequence number.
@@ -528,6 +548,7 @@ func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, err
// Encrypt the actual ContentType and replace the plaintext one.
record = append(record, record[0])
//////////////////////////////////// [REALITY] SECTION: mimic recorded handshakeLen
padding := 0
if recordType(record[0]) == recordTypeHandshake && hc.handshakeLen[1] != 0 {
switch payload[0] {
@@ -558,6 +579,7 @@ func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, err
record[0] = byte(recordTypeApplicationData)
n := len(record) + c.Overhead() - recordHeaderLen
//////////////////////////////////// [REALITY] SECTION END
record[3] = byte(n >> 8)
record[4] = byte(n)
@@ -615,7 +637,9 @@ 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())
if c.rawInput != nil {
copy(err.RecordHeader[:], c.rawInput.Bytes())
}
return err
}
@@ -657,6 +681,19 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
return c.in.setErrorLocked(errors.New("tls: internal error: attempted to read record with QUIC transport"))
}
// If rawInput is empty, we are about to block in a Read on the
// underlying connection waiting for the next record, possibly for a
// long time. A previous record may have grown rawInput to the maximum
// record size; don't pin that memory while idle. Return the buffer to
// the pool, and let readFromUntil read the header into a small buffer
// and switch back to a pooled record-sized buffer only once the
// payload length is known.
if c.rawInput != nil && c.rawInput.Len() == 0 && c.rawInput != c.smallInput && c.rawInput.Cap() > maxIdleInputCap {
c.rawInput.Reset()
rawInputPool.Put(c.rawInput)
c.rawInput = nil
}
// Read header, payload.
if err := c.readFromUntil(c.conn, recordHeaderLen); err != nil {
// RFC 8446, Section 6.1 suggests that EOF without an alertCloseNotify
@@ -731,13 +768,13 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && len(data) > 0 {
if (typ == recordTypeApplicationData || (typ == recordTypeHandshake && !handshakeComplete)) && 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 {
if c.vers == VersionTLS13 && typ != recordTypeHandshake && c.handLen() > 0 {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
@@ -782,7 +819,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
return c.in.setErrorLocked(c.sendAlert(alertDecodeError))
}
// Handshake messages are not allowed to fragment across the CCS.
if c.hand.Len() > 0 {
if c.handLen() > 0 {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
// In TLS 1.3, change_cipher_spec records are ignored until the
@@ -790,7 +827,9 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
// 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.
//////////////////////////////////// [REALITY] SECTION: reject change_cipher_spec record after handshake in TLS 1.3
if c.vers == VersionTLS13 && !handshakeComplete {
//////////////////////////////////// [REALITY] SECTION END
return c.retryReadRecord(expectChangeCipherSpec)
}
if !expectChangeCipherSpec {
@@ -818,7 +857,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
if len(data) == 0 || expectChangeCipherSpec {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
c.hand.Write(data)
c.handBuf().Write(data)
}
return nil
@@ -828,52 +867,125 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
func (c *Conn) retryReadRecord(expectChangeCipherSpec bool) error {
c.retryCount++
//////////////////////////////////// [REALITY] SECTION: mimic recorded maxUselessRecords
if c.MaxUselessRecords <= 0 {
c.MaxUselessRecords = maxUselessRecords
}
if c.retryCount > c.MaxUselessRecords {
//////////////////////////////////// [REALITY] SECTION END
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
// rawInputPool pools the record-sized buffers that back Conn.rawInput
// while records are being received. A connection returns its buffer to
// the pool before blocking to wait for a new record, often for a long
// time, so that idle connections do not each pin a record-sized buffer.
// Only buffers with capacity above maxIdleInputCap are pooled; smaller
// buffers stay attached to their connection.
var rawInputPool = sync.Pool{New: func() any { return new(bytes.Buffer) }}
// maxIdleInputCap is the largest rawInput capacity that a connection
// keeps while waiting for a new record to arrive. It is large enough to
// hold a record header and small records, so that only connections
// receiving larger records pay for the pooled buffer switch below.
const maxIdleInputCap = 1024
// handPool pools the buffers that back Conn.hand, which typically grow
// to hold the peer's largest flight of handshake messages. A connection
// returns its buffer to the pool once the handshake completes and after
// buffered post-handshake messages have been consumed, so that
// established connections do not pin it.
var handPool = sync.Pool{New: func() any { return new(bytes.Buffer) }}
// handBuf returns c.hand for writing, getting a buffer from handPool if
// c.hand is nil.
func (c *Conn) handBuf() *bytes.Buffer {
if c.hand == nil {
c.hand = handPool.Get().(*bytes.Buffer)
}
return c.hand
}
func (r *atLeastReader) Read(p []byte) (int, error) {
if r.N <= 0 {
return 0, io.EOF
// handLen returns the number of buffered handshake bytes.
func (c *Conn) handLen() int {
if c.hand == nil {
return 0
}
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
return c.hand.Len()
}
// releaseHand returns c.hand to handPool if it is empty.
func (c *Conn) releaseHand() {
if c.hand != nil && c.hand.Len() == 0 {
c.hand.Reset()
handPool.Put(c.hand)
c.hand = nil
}
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 == nil {
// The record buffer was released while waiting for a new
// record. Block for the header using the connection's small
// buffer; the switch to a pooled record-sized buffer below
// happens only once the payload length is known and data is
// flowing.
if c.smallInput == nil {
c.smallInput = new(bytes.Buffer)
}
c.rawInput = c.smallInput
}
if c.rawInput.Len() >= n {
return nil
}
needs := n - c.rawInput.Len()
if want := c.rawInput.Len() + needs + bytes.MinRead; want > maxIdleInputCap && want > c.rawInput.Cap() {
// Growing past maxIdleInputCap: switch to a pooled buffer so
// that record-sized buffers are recycled across connections
// rather than allocated for every record.
b := rawInputPool.Get().(*bytes.Buffer)
b.Write(c.rawInput.Bytes())
if c.rawInput == c.smallInput {
c.smallInput.Reset()
} else if c.rawInput.Cap() > maxIdleInputCap {
c.rawInput.Reset()
rawInputPool.Put(c.rawInput)
}
c.rawInput = b
}
// 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.
// TODO(dmo): we use bytes.MinRead here because we used the buffer
// ReadFrom mechanism to avoid allocations, but we've hoisted this
// loop for performance. We really should use our own heuristic here
// for how much to read ahead.
c.rawInput.Grow(needs + bytes.MinRead)
_, err := c.rawInput.ReadFrom(&atLeastReader{r, int64(needs)})
return err
for {
buf := c.rawInput.AvailableBuffer()[:c.rawInput.Available()]
n, err := r.Read(buf)
// This write is just to update the internal state of the
// rawInput bytes.Buffer. It cannot fail.
c.rawInput.Write(buf[:n])
needs -= n
if needs <= 0 {
if err == io.EOF {
err = nil
}
return err
}
if err == io.EOF {
return io.ErrUnexpectedEOF
}
if err != nil {
return err
}
}
}
// sendAlertLocked sends a TLS alert message.
@@ -1100,6 +1212,7 @@ func (c *Conn) writeHandshakeRecord(msg handshakeMessage, transcript transcriptH
transcript.Write(data)
}
//////////////////////////////////// [REALITY] SECTION: mimic recorded handshakeBuf
if c.out.handshakeBuf != nil && len(data) > 0 && data[0] != typeServerHello {
c.out.handshakeBuf = append(c.out.handshakeBuf, data...)
if data[0] != typeFinished {
@@ -1131,6 +1244,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (int, error) {
return c.writeRecordLocked(typ, data)
}
//////////////////////////////////// [REALITY] SECTION END
// writeChangeCipherRecord writes a ChangeCipherSpec message to the connection and
// updates the record layer state.
@@ -1146,7 +1260,7 @@ func (c *Conn) readHandshakeBytes(n int) error {
if c.quic != nil {
return c.quicReadHandshakeBytes(n)
}
for c.hand.Len() < n {
for c.handLen() < n {
if err := c.readRecord(); err != nil {
return err
}
@@ -1379,7 +1493,9 @@ func (c *Conn) handlePostHandshakeMessage() error {
return err
}
c.retryCount++
//////////////////////////////////// [REALITY] SECTION: mimic recorded maxUselessRecords
if c.retryCount > c.MaxUselessRecords {
//////////////////////////////////// [REALITY] SECTION END
c.sendAlert(alertUnexpectedMessage)
return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
}
@@ -1409,9 +1525,6 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
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()
@@ -1429,7 +1542,12 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
}
newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
c.out.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
c.setWriteTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
}
newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
if err := c.setReadTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret, keyUpdate.updateRequested); err != nil {
return err
}
return nil
@@ -1458,11 +1576,12 @@ func (c *Conn) Read(b []byte) (int, error) {
if err := c.readRecord(); err != nil {
return 0, err
}
for c.hand.Len() > 0 {
for c.handLen() > 0 {
if err := c.handlePostHandshakeMessage(); err != nil {
return 0, err
}
}
c.releaseHand()
}
n, _ := c.input.Read(b)
@@ -1590,37 +1709,23 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
}
handshakeCtx, cancel := context.WithCancel(ctx)
// Note: defer this before starting the "interrupter" goroutine
// Note: defer this before calling context.AfterFunc
// 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.ctx = handshakeCtx
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)
// Close the connection if ctx is canceled before the function returns.
stop := context.AfterFunc(ctx, func() {
_ = c.conn.Close()
})
defer func() {
close(done)
if ctxErr := <-interruptRes; ctxErr != nil {
if !stop() {
// 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
ret = ctx.Err()
}
}()
}
@@ -1654,17 +1759,27 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
panic("tls: internal error: handshake returned an error but is marked successful")
}
// The handshake buffer typically grew to hold the peer's largest
// flight of handshake messages and is now empty. Post-handshake
// messages are rare and small, so release the buffer rather than
// pinning it for the life of the connection.
if c.handshakeErr == nil {
c.releaseHand()
}
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)
if err := c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret); err != nil {
return err
}
} else {
var a alert
c.out.Lock()
if !errors.As(c.out.err, &a) {
a, ok := errors.AsType[alert](c.out.err)
if !ok {
a = alertInternalError
}
c.out.Unlock()
@@ -1682,6 +1797,12 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
}
// ConnectionState returns basic TLS details about the connection.
//
// The returned [ConnectionState] is only meaningful after the handshake has
// completed, as reported by [ConnectionState.HandshakeComplete]; before then
// its fields are not populated. The handshake is run automatically by the
// first [Conn.Read] or [Conn.Write], or it can be triggered explicitly with
// [Conn.Handshake].
func (c *Conn) ConnectionState() ConnectionState {
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
@@ -1694,12 +1815,14 @@ func (c *Conn) connectionStateLocked() ConnectionState {
state.Version = c.vers
state.NegotiatedProtocol = c.clientProtocol
state.DidResume = c.didResume
state.testingOnlyDidHRR = c.didHRR
state.HelloRetryRequest = c.didHRR
state.testingOnlyPeerSignatureAlgorithm = c.peerSigAlg
state.CurveID = c.curveID
state.NegotiatedProtocolIsMutual = true
state.ServerName = c.serverName
state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates
state.LocalCertificate = c.localCertificate
state.VerifiedChains = c.verifiedChains
state.SignedCertificateTimestamps = c.scts
state.OCSPResponse = c.ocspResponse
@@ -1713,13 +1836,7 @@ func (c *Conn) connectionStateLocked() ConnectionState {
if c.config.Renegotiation != RenegotiateNever {
state.ekm = noEKMBecauseRenegotiation
} else if c.vers != VersionTLS13 && !c.extMasterSecret {
state.ekm = func(label string, context []byte, length int) ([]byte, error) {
// if ekmgodebug.Value() == "1" {
// ekmgodebug.IncNonDefault()
// return c.ekm(label, context, length)
// }
return noEKMBecauseNoEMS(label, context, length)
}
state.ekm = noEKMBecauseNoEMS
} else {
state.ekm = c.ekm
}
@@ -1753,3 +1870,29 @@ func (c *Conn) VerifyHostname(host string) error {
}
return c.peerCertificates[0].VerifyHostname(host)
}
// setReadTrafficSecret sets the read traffic secret for the given encryption level. If
// being called at the same time as setWriteTrafficSecret, the caller must ensure the call
// to setWriteTrafficSecret happens first so any alerts are sent at the write level.
func (c *Conn) setReadTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte, locked bool) error {
// Ensure that there are no buffered handshake messages before changing the
// read keys, since that can cause messages to be parsed that were encrypted
// using old keys which are no longer appropriate.
if c.handLen() != 0 {
if locked {
c.sendAlertLocked(alertUnexpectedMessage)
} else {
c.sendAlert(alertUnexpectedMessage)
}
return errors.New("tls: handshake buffer not empty before setting read traffic secret")
}
c.in.setTrafficSecret(suite, level, secret)
return nil
}
// setWriteTrafficSecret sets the write traffic secret for the given encryption level. If
// being called at the same time as setReadTrafficSecret, the caller must ensure the call
// to setWriteTrafficSecret happens first so any alerts are sent at the write level.
func (c *Conn) setWriteTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) {
c.out.setTrafficSecret(suite, level, secret)
}