[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
This commit is contained in:
parent
b0bcb44715
commit
7743362eba
@ -26,6 +26,7 @@ const (
|
|||||||
VersionTLS10 = 0x0301
|
VersionTLS10 = 0x0301
|
||||||
VersionTLS11 = 0x0302
|
VersionTLS11 = 0x0302
|
||||||
VersionTLS12 = 0x0303
|
VersionTLS12 = 0x0303
|
||||||
|
VersionTLS13 = 0x0304
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
64
conn.go
64
conn.go
@ -297,13 +297,17 @@ func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert)
|
|||||||
nonce = hc.seq[:]
|
nonce = hc.seq[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var additionalData []byte
|
||||||
|
if hc.version < VersionTLS13 {
|
||||||
copy(hc.additionalData[:], hc.seq[:])
|
copy(hc.additionalData[:], hc.seq[:])
|
||||||
copy(hc.additionalData[8:], b.data[:3])
|
copy(hc.additionalData[8:], b.data[:3])
|
||||||
n := len(payload) - c.Overhead()
|
n := len(payload) - c.Overhead()
|
||||||
hc.additionalData[11] = byte(n >> 8)
|
hc.additionalData[11] = byte(n >> 8)
|
||||||
hc.additionalData[12] = byte(n)
|
hc.additionalData[12] = byte(n)
|
||||||
|
additionalData = hc.additionalData[:]
|
||||||
|
}
|
||||||
var err error
|
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 {
|
if err != nil {
|
||||||
return false, 0, alertBadRecordMAC
|
return false, 0, alertBadRecordMAC
|
||||||
}
|
}
|
||||||
@ -404,20 +408,36 @@ func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) {
|
|||||||
c.XORKeyStream(payload, payload)
|
c.XORKeyStream(payload, payload)
|
||||||
case aead:
|
case aead:
|
||||||
payloadLen := len(b.data) - recordHeaderLen - explicitIVLen
|
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]
|
nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen]
|
||||||
if len(nonce) == 0 {
|
if len(nonce) == 0 {
|
||||||
nonce = hc.seq[:]
|
nonce = hc.seq[:]
|
||||||
}
|
}
|
||||||
payload := b.data[recordHeaderLen+explicitIVLen:]
|
payload = b.data[recordHeaderLen+explicitIVLen:]
|
||||||
payload = payload[:payloadLen]
|
payload = payload[:payloadLen]
|
||||||
|
|
||||||
|
var additionalData []byte
|
||||||
|
if hc.version < VersionTLS13 {
|
||||||
copy(hc.additionalData[:], hc.seq[:])
|
copy(hc.additionalData[:], hc.seq[:])
|
||||||
copy(hc.additionalData[8:], b.data[:3])
|
copy(hc.additionalData[8:], b.data[:3])
|
||||||
hc.additionalData[11] = byte(payloadLen >> 8)
|
hc.additionalData[11] = byte(payloadLen >> 8)
|
||||||
hc.additionalData[12] = byte(payloadLen)
|
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:
|
case cbcMode:
|
||||||
blockSize := c.BlockSize()
|
blockSize := c.BlockSize()
|
||||||
if explicitIVLen > 0 {
|
if explicitIVLen > 0 {
|
||||||
@ -612,11 +632,6 @@ Again:
|
|||||||
|
|
||||||
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
|
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
|
||||||
n := int(b.data[3])<<8 | int(b.data[4])
|
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 {
|
if n > maxCiphertext {
|
||||||
c.sendAlert(alertRecordOverflow)
|
c.sendAlert(alertRecordOverflow)
|
||||||
msg := fmt.Sprintf("oversized record received with length %d", n)
|
msg := fmt.Sprintf("oversized record received with length %d", n)
|
||||||
@ -652,9 +667,28 @@ Again:
|
|||||||
b.off = off
|
b.off = off
|
||||||
data := b.data[b.off:]
|
data := b.data[b.off:]
|
||||||
if len(data) > maxPlaintext {
|
if len(data) > maxPlaintext {
|
||||||
err := c.sendAlert(alertRecordOverflow)
|
|
||||||
c.in.freeBlock(b)
|
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 {
|
switch typ {
|
||||||
@ -795,6 +829,9 @@ func (c *Conn) maxPayloadSizeForWrite(typ recordType, explicitIVLen int) int {
|
|||||||
payloadBytes -= macSize
|
payloadBytes -= macSize
|
||||||
case cipher.AEAD:
|
case cipher.AEAD:
|
||||||
payloadBytes -= ciph.Overhead()
|
payloadBytes -= ciph.Overhead()
|
||||||
|
if c.vers >= VersionTLS13 {
|
||||||
|
payloadBytes -= 1 // ContentType
|
||||||
|
}
|
||||||
case cbcMode:
|
case cbcMode:
|
||||||
blockSize := ciph.BlockSize()
|
blockSize := ciph.BlockSize()
|
||||||
// The payload must fit in a multiple of blockSize, with
|
// 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.
|
// greater than TLS 1.0 for the initial ClientHello.
|
||||||
vers = VersionTLS10
|
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[1] = byte(vers >> 8)
|
||||||
b.data[2] = byte(vers)
|
b.data[2] = byte(vers)
|
||||||
b.data[3] = byte(m >> 8)
|
b.data[3] = byte(m >> 8)
|
||||||
|
@ -1173,7 +1173,7 @@ var getConfigForClientTests = []struct {
|
|||||||
config.MaxVersion = VersionTLS11
|
config.MaxVersion = VersionTLS11
|
||||||
return config, nil
|
return config, nil
|
||||||
},
|
},
|
||||||
"version 301 when expecting version 302",
|
"protocol version not supported",
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user