From 7743362eba78ff5eb992992f9178d12ee4b6e987 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Sat, 12 Nov 2016 12:35:50 -0800 Subject: [PATCH] [dev.tls] crypto/tls: implement TLS 1.3 record layer Opening the 1.3 dances with the record layer because it has been the most stable through the drafts, has the least dependencies, and has been tricky in my experience. Note that the record layer version check is entirely removed according to https://tools.ietf.org/html/draft-ietf-tls-tls13-18#appendix-C.2. A test that happened to hit that check (but was not made to test for it) has changed to the next error in the stack. There are no 1.3 tests at the moment, and I suspect they will all have to wait for the patch cycle to reach interoperability. Using > / <= VersionTLS13 for all conditionals to transparently support draft versions and hypotetical future versions. See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-5. Updates #9671 Change-Id: I97f0a59439728f194a1c50b48cff041469a0f00b --- common.go | 1 + conn.go | 82 ++++++++++++++++++++++++++++++---------- handshake_server_test.go | 2 +- 3 files changed, 64 insertions(+), 21 deletions(-) diff --git a/common.go b/common.go index 5860838..a34b700 100644 --- a/common.go +++ b/common.go @@ -26,6 +26,7 @@ const ( VersionTLS10 = 0x0301 VersionTLS11 = 0x0302 VersionTLS12 = 0x0303 + VersionTLS13 = 0x0304 ) const ( diff --git a/conn.go b/conn.go index e6d85aa..decd43e 100644 --- a/conn.go +++ b/conn.go @@ -297,13 +297,17 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) nonce = hc.seq[:] } - copy(hc.additionalData[:], hc.seq[:]) - copy(hc.additionalData[8:], b.data[:3]) - n := len(payload) - c.Overhead() - hc.additionalData[11] = byte(n >> 8) - hc.additionalData[12] = byte(n) + var additionalData []byte + if hc.version < VersionTLS13 { + copy(hc.additionalData[:], hc.seq[:]) + copy(hc.additionalData[8:], b.data[:3]) + n := len(payload) - c.Overhead() + hc.additionalData[11] = byte(n >> 8) + hc.additionalData[12] = byte(n) + additionalData = hc.additionalData[:] + } var err error - payload, err = c.Open(payload[:0], nonce, payload, hc.additionalData[:]) + payload, err = c.Open(payload[:0], nonce, payload, additionalData) if err != nil { return false, 0, alertBadRecordMAC } @@ -404,20 +408,36 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { c.XORKeyStream(payload, payload) case aead: payloadLen := len(b.data) - recordHeaderLen - explicitIVLen - b.resize(len(b.data) + c.Overhead()) + overhead := c.Overhead() + if hc.version >= VersionTLS13 { + overhead++ + } + b.resize(len(b.data) + overhead) + nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] if len(nonce) == 0 { nonce = hc.seq[:] } - payload := b.data[recordHeaderLen+explicitIVLen:] + payload = b.data[recordHeaderLen+explicitIVLen:] payload = payload[:payloadLen] - copy(hc.additionalData[:], hc.seq[:]) - copy(hc.additionalData[8:], b.data[:3]) - hc.additionalData[11] = byte(payloadLen >> 8) - hc.additionalData[12] = byte(payloadLen) + var additionalData []byte + if hc.version < VersionTLS13 { + copy(hc.additionalData[:], hc.seq[:]) + copy(hc.additionalData[8:], b.data[:3]) + hc.additionalData[11] = byte(payloadLen >> 8) + hc.additionalData[12] = byte(payloadLen) + additionalData = hc.additionalData[:] + } - c.Seal(payload[:0], nonce, payload, hc.additionalData[:]) + if hc.version >= VersionTLS13 { + // opaque type + payload = payload[:len(payload)+1] + payload[len(payload)-1] = b.data[0] + b.data[0] = byte(recordTypeApplicationData) + } + + c.Seal(payload[:0], nonce, payload, additionalData) case cbcMode: blockSize := c.BlockSize() if explicitIVLen > 0 { @@ -612,11 +632,6 @@ Again: vers := uint16(b.data[1])<<8 | uint16(b.data[2]) n := int(b.data[3])<<8 | int(b.data[4]) - if c.haveVers && 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(msg)) - } if n > maxCiphertext { c.sendAlert(alertRecordOverflow) msg := fmt.Sprintf("oversized record received with length %d", n) @@ -652,9 +667,28 @@ Again: b.off = off data := b.data[b.off:] if len(data) > maxPlaintext { - err := c.sendAlert(alertRecordOverflow) c.in.freeBlock(b) - return c.in.setErrorLocked(err) + return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow)) + } + + // After checking the plaintext length, remove 1.3 padding and + // extract the real content type. + // See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-5.4. + if c.vers >= VersionTLS13 { + i := len(data) - 1 + for i >= 0 { + if data[i] != 0 { + break + } + i-- + } + if i < 0 { + c.in.freeBlock(b) + return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + typ = recordType(data[i]) + data = data[:i] + b.resize(b.off + i) // shrinks, guaranteed not to reallocate } switch typ { @@ -795,6 +829,9 @@ func (c *Conn) maxPayloadSizeForWrite(typ recordType, explicitIVLen int) int { payloadBytes -= macSize case cipher.AEAD: payloadBytes -= ciph.Overhead() + if c.vers >= VersionTLS13 { + payloadBytes -= 1 // ContentType + } case cbcMode: blockSize := ciph.BlockSize() // The payload must fit in a multiple of blockSize, with @@ -890,6 +927,11 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { // greater than TLS 1.0 for the initial ClientHello. vers = VersionTLS10 } + if c.vers >= VersionTLS13 { + // TLS 1.3 froze the record layer version at { 3, 1 }. + // See https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-5.1. + vers = VersionTLS10 + } b.data[1] = byte(vers >> 8) b.data[2] = byte(vers) b.data[3] = byte(m >> 8) diff --git a/handshake_server_test.go b/handshake_server_test.go index 63845c1..65f3599 100644 --- a/handshake_server_test.go +++ b/handshake_server_test.go @@ -1173,7 +1173,7 @@ var getConfigForClientTests = []struct { config.MaxVersion = VersionTLS11 return config, nil }, - "version 301 when expecting version 302", + "protocol version not supported", nil, }, {