github.com/fumiama/terasu@v0.0.0-20240507144117-547a591149c0/tls_1.20.go (about) 1 //go:build !go1.21 2 3 package terasu 4 5 import ( 6 "context" 7 "crypto/tls" 8 "crypto/x509" 9 "hash" 10 "io" 11 "net" 12 "sync" 13 "sync/atomic" 14 "unsafe" 15 _ "unsafe" 16 ) 17 18 type recordType uint8 19 20 const ( 21 recordTypeChangeCipherSpec recordType = 20 22 recordTypeAlert recordType = 21 23 recordTypeHandshake recordType = 22 24 recordTypeApplicationData recordType = 23 25 ) 26 27 const ( 28 recordHeaderLen = 5 // record header length 29 ) 30 31 type alert uint8 32 33 //go:linkname alertError tls.(tls.alert).Error 34 func alertError(e alert) string 35 36 func (e alert) Error() string { 37 return alertError(e) 38 } 39 40 // A halfConn represents one direction of the record layer 41 // connection, either sending or receiving. 42 type halfConn struct { 43 sync.Mutex 44 45 err error // first permanent error 46 version uint16 // protocol version 47 cipher any // cipher algorithm 48 mac hash.Hash 49 seq [8]byte // 64-bit sequence number 50 51 scratchBuf [13]byte // to avoid allocs; interface method args escape 52 53 nextCipher any // next encryption state 54 nextMac hash.Hash // next MAC algorithm 55 56 trafficSecret []byte // current TLS 1.3 traffic secret 57 } 58 59 type Conn tls.Conn 60 61 // A _trsconn represents a secured connection. 62 // It implements the net._trsconn interface. 63 type _trsconn struct { 64 // constant 65 conn net.Conn 66 isClient bool 67 handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake 68 69 // isHandshakeComplete is true if the connection is currently transferring 70 // application data (i.e. is not currently processing a handshake). 71 // isHandshakeComplete is true implies handshakeErr == nil. 72 isHandshakeComplete atomic.Bool 73 // constant after handshake; protected by handshakeMutex 74 handshakeMutex sync.Mutex 75 handshakeErr error // error resulting from handshake 76 vers uint16 // TLS version 77 haveVers bool // version has been negotiated 78 config *tls.Config // configuration passed to constructor 79 // handshakes counts the number of handshakes performed on the 80 // connection so far. If renegotiation is disabled then this is either 81 // zero or one. 82 handshakes int 83 didResume bool // whether this connection was a session resumption 84 cipherSuite uint16 85 ocspResponse []byte // stapled OCSP response 86 scts [][]byte // signed certificate timestamps from server 87 peerCertificates []*x509.Certificate 88 // activeCertHandles contains the cache handles to certificates in 89 // peerCertificates that are used to track active references. 90 activeCertHandles []*uintptr 91 // verifiedChains contains the certificate chains that we built, as 92 // opposed to the ones presented by the server. 93 verifiedChains [][]*x509.Certificate 94 // serverName contains the server name indicated by the client, if any. 95 serverName string 96 // secureRenegotiation is true if the server echoed the secure 97 // renegotiation extension. (This is meaningless as a server because 98 // renegotiation is not supported in that case.) 99 secureRenegotiation bool 100 // ekm is a closure for exporting keying material. 101 ekm func(label string, context []byte, length int) ([]byte, error) 102 // resumptionSecret is the resumption_master_secret for handling 103 // or sending NewSessionTicket messages. 104 resumptionSecret []byte 105 106 // ticketKeys is the set of active session ticket keys for this 107 // connection. The first one is used to encrypt new tickets and 108 // all are tried to decrypt tickets. 109 ticketKeys []byte 110 111 // clientFinishedIsFirst is true if the client sent the first Finished 112 // message during the most recent handshake. This is recorded because 113 // the first transmitted Finished message is the tls-unique 114 // channel-binding value. 115 clientFinishedIsFirst bool 116 117 // closeNotifyErr is any error from sending the alertCloseNotify record. 118 closeNotifyErr error 119 // closeNotifySent is true if the Conn attempted to send an 120 // alertCloseNotify record. 121 closeNotifySent bool 122 123 // clientFinished and serverFinished contain the Finished message sent 124 // by the client or server in the most recent handshake. This is 125 // retained to support the renegotiation extension and tls-unique 126 // channel-binding. 127 clientFinished [12]byte 128 serverFinished [12]byte 129 130 // clientProtocol is the negotiated ALPN protocol. 131 clientProtocol string 132 133 // input/output 134 in, out halfConn 135 } 136 137 //go:linkname outBufPool crypto/tls.outBufPool 138 var outBufPool sync.Pool 139 140 //go:linkname maxPayloadSizeForWrite crypto/tls.(*Conn).maxPayloadSizeForWrite 141 func maxPayloadSizeForWrite(c *_trsconn, typ recordType) int 142 143 func (c *_trsconn) maxPayloadSizeForWrite(typ recordType) int { 144 return maxPayloadSizeForWrite(c, typ) 145 } 146 147 //go:linkname sliceForAppend crypto/tls.sliceForAppend 148 func sliceForAppend(in []byte, n int) (head, tail []byte) 149 150 //go:linkname encrypt crypto/tls.(*halfConn).encrypt 151 func encrypt(hc *halfConn, record, payload []byte, rand io.Reader) ([]byte, error) 152 153 func (hc *halfConn) encrypt(record, payload []byte, rand io.Reader) ([]byte, error) { 154 return encrypt(hc, record, payload, rand) 155 } 156 157 //go:linkname rand crypto/tls.(*Config).rand 158 func rand(c *tls.Config) io.Reader 159 160 //go:linkname write crypto/tls.(*Conn).write 161 func write(c *_trsconn, data []byte) (int, error) 162 163 func (c *_trsconn) write(data []byte) (int, error) { 164 return write(c, data) 165 } 166 167 //go:linkname flush crypto/tls.(*Conn).flush 168 func flush(c *_trsconn) (int, error) 169 170 func (c *_trsconn) flush() (int, error) { 171 return flush(c) 172 } 173 174 //go:linkname changeCipherSpec crypto/tls.(*halfConn).changeCipherSpec 175 func changeCipherSpec(hc *halfConn) error 176 177 func (hc *halfConn) changeCipherSpec() error { 178 return changeCipherSpec(hc) 179 } 180 181 //go:linkname sendAlertLocked crypto/tls.(*Conn).sendAlertLocked 182 func sendAlertLocked(c *_trsconn, err alert) error 183 184 func (c *_trsconn) sendAlertLocked(err alert) error { 185 return sendAlertLocked(c, err) 186 } 187 188 // writeRecordLocked writes a TLS record with the given type and payload to the 189 // connection and updates the record layer state. 190 func (c *_trsconn) writeRecordLocked(typ recordType, firstFragmentLen uint8, data []byte) (int, error) { 191 outBufPtr := outBufPool.Get().(*[]byte) 192 outBuf := *outBufPtr 193 defer func() { 194 // You might be tempted to simplify this by just passing &outBuf to Put, 195 // but that would make the local copy of the outBuf slice header escape 196 // to the heap, causing an allocation. Instead, we keep around the 197 // pointer to the slice header returned by Get, which is already on the 198 // heap, and overwrite and return that. 199 *outBufPtr = outBuf 200 outBufPool.Put(outBufPtr) 201 }() 202 203 var n int 204 isFirstLoop := true 205 for len(data) > 0 { 206 m := len(data) 207 if !isFirstLoop { 208 if maxPayload := c.maxPayloadSizeForWrite(typ); m > maxPayload { 209 m = maxPayload 210 } 211 } else { 212 m = int(firstFragmentLen) 213 } 214 215 _, outBuf = sliceForAppend(outBuf[:0], recordHeaderLen) 216 outBuf[0] = byte(typ) 217 vers := c.vers 218 if vers == 0 { 219 // Some TLS servers fail if the record version is 220 // greater than TLS 1.0 for the initial ClientHello. 221 vers = tls.VersionTLS10 222 } else if vers == tls.VersionTLS13 { 223 // TLS 1.3 froze the record layer version to 1.2. 224 // See RFC 8446, Section 5.1. 225 vers = tls.VersionTLS12 226 } 227 outBuf[1] = byte(vers >> 8) 228 outBuf[2] = byte(vers) 229 outBuf[3] = byte(m >> 8) 230 outBuf[4] = byte(m) 231 232 var err error 233 outBuf, err = c.out.encrypt(outBuf, data[:m], rand(c.config)) 234 if err != nil { 235 return n, err 236 } 237 if _, err := c.write(outBuf); err != nil { 238 return n, err 239 } 240 n += m 241 data = data[m:] 242 if isFirstLoop { 243 isFirstLoop = false 244 if _, err := c.flush(); err != nil { 245 return n, err 246 } 247 } 248 } 249 250 if typ == recordTypeChangeCipherSpec && c.vers != tls.VersionTLS13 { 251 if err := c.out.changeCipherSpec(); err != nil { 252 return n, c.sendAlertLocked(alert( 253 *(*uintptr)( 254 unsafe.Add(unsafe.Pointer(&err), unsafe.Sizeof(uintptr(0))), 255 ), 256 )) 257 } 258 } 259 260 return n, nil 261 }