package tls
import (
+ "crypto"
"crypto/aes"
"crypto/cipher"
"crypto/des"
suiteDefaultOff
)
-// A cipherSuite is a specific combination of key agreement, cipher and MAC
-// function. All cipher suites currently assume RSA key agreement.
+// A cipherSuite is a specific combination of key agreement, cipher and MAC function.
type cipherSuite struct {
id uint16
// the lengths, in bytes, of the key material needed for each component.
flags int
cipher func(key, iv []byte, isRead bool) interface{}
mac func(version uint16, macKey []byte) macFunction
- aead func(key, fixedNonce []byte) cipher.AEAD
+ aead func(key, fixedNonce []byte) aead
}
var cipherSuites = []*cipherSuite{
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteDefaultOff, cipherRC4, macSHA1, nil},
}
+// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
+// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
+type cipherSuiteTLS13 struct {
+ id uint16
+ keyLen int
+ aead func(key, fixedNonce []byte) aead
+ hash crypto.Hash
+}
+
+var cipherSuitesTLS13 = []*cipherSuiteTLS13{
+ {TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
+ {TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
+ {TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
+}
+
func cipherRC4(key, iv []byte, isRead bool) interface{} {
cipher, _ := rc4.NewCipher(key)
return cipher
explicitNonceLen() int
}
-// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
+const (
+ aeadNonceLength = 12
+ noncePrefixLength = 4
+)
+
+// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
// each call.
-type fixedNonceAEAD struct {
+type prefixNonceAEAD struct {
// nonce contains the fixed part of the nonce in the first four bytes.
- nonce [12]byte
+ nonce [aeadNonceLength]byte
aead cipher.AEAD
}
-func (f *fixedNonceAEAD) NonceSize() int { return 8 }
-func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() }
-func (f *fixedNonceAEAD) explicitNonceLen() int { return 8 }
+func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
+func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
+func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
-func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
+func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
copy(f.nonce[4:], nonce)
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
}
-func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
+func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
copy(f.nonce[4:], nonce)
- return f.aead.Open(out, f.nonce[:], plaintext, additionalData)
+ return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
}
// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
// before each call.
type xorNonceAEAD struct {
- nonceMask [12]byte
+ nonceMask [aeadNonceLength]byte
aead cipher.AEAD
}
-func (f *xorNonceAEAD) NonceSize() int { return 8 }
+func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
return result
}
-func (f *xorNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) {
+func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
for i, b := range nonce {
f.nonceMask[4+i] ^= b
}
- result, err := f.aead.Open(out, f.nonceMask[:], plaintext, additionalData)
+ result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
for i, b := range nonce {
f.nonceMask[4+i] ^= b
}
return result, err
}
-func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD {
+func aeadAESGCM(key, noncePrefix []byte) aead {
+ if len(noncePrefix) != noncePrefixLength {
+ panic("tls: internal error: wrong nonce length")
+ }
aes, err := aes.NewCipher(key)
if err != nil {
panic(err)
panic(err)
}
- ret := &fixedNonceAEAD{aead: aead}
- copy(ret.nonce[:], fixedNonce)
+ ret := &prefixNonceAEAD{aead: aead}
+ copy(ret.nonce[:], noncePrefix)
return ret
}
-func aeadChaCha20Poly1305(key, fixedNonce []byte) cipher.AEAD {
+func aeadAESGCMTLS13(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
+ aes, err := aes.NewCipher(key)
+ if err != nil {
+ panic(err)
+ }
+ aead, err := cipher.NewGCM(aes)
+ if err != nil {
+ panic(err)
+ }
+
+ ret := &xorNonceAEAD{aead: aead}
+ copy(ret.nonceMask[:], nonceMask)
+ return ret
+}
+
+func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
+ if len(nonceMask) != aeadNonceLength {
+ panic("tls: internal error: wrong nonce length")
+ }
aead, err := chacha20poly1305.New(key)
if err != nil {
panic(err)
}
ret := &xorNonceAEAD{aead: aead}
- copy(ret.nonceMask[:], fixedNonce)
+ copy(ret.nonceMask[:], nonceMask)
return ret
}
//
// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const (
+ // TLS 1.0 - 1.2 cipher suites.
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 uint16 = 0xcca8
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 uint16 = 0xcca9
+ // TLS 1.3 cipher suites.
+ TLS_AES_128_GCM_SHA256 uint16 = 0x1301
+ TLS_AES_256_GCM_SHA384 uint16 = 0x1302
+ TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
+
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
// that the client is doing version fallback. See RFC 7507.
TLS_FALLBACK_SCSV uint16 = 0x5600
VersionTLS10 = 0x0301
VersionTLS11 = 0x0302
VersionTLS12 = 0x0303
+
+ // VersionTLS13 is under development in this library and can't be selected
+ // nor negotiated yet on either side.
+ VersionTLS13 = 0x0304
)
const (
- maxPlaintext = 16384 // maximum plaintext payload length
- maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
- recordHeaderLen = 5 // record header length
- maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
- maxWarnAlertCount = 5 // maximum number of consecutive warning alerts
+ maxPlaintext = 16384 // maximum plaintext payload length
+ maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
+ maxCiphertextTLS13 = 16384 + 256 // maximum ciphertext length in TLS 1.3
+ recordHeaderLen = 5 // record header length
+ maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
+ maxUselessRecords = 5 // maximum number of consecutive non-advancing records
minVersion = VersionTLS10
maxVersion = VersionTLS12
}
var (
- once sync.Once
- varDefaultCipherSuites []uint16
+ once sync.Once
+ varDefaultCipherSuites []uint16
+ varDefaultCipherSuitesTLS13 []uint16
)
func defaultCipherSuites() []uint16 {
return varDefaultCipherSuites
}
+func defaultCipherSuitesTLS13() []uint16 {
+ once.Do(initDefaultCipherSuites)
+ return varDefaultCipherSuitesTLS13
+}
+
func initDefaultCipherSuites() {
var topCipherSuites []uint16
// Check the cpu flags for each platform that has optimized GCM implementations.
- // Worst case, these variables will just all be false
- hasGCMAsmAMD64 := cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
+ // Worst case, these variables will just all be false.
+ var (
+ hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
+ hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
+ // Keep in sync with crypto/aes/cipher_s390x.go.
+ hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
- hasGCMAsmARM64 := cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
-
- // Keep in sync with crypto/aes/cipher_s390x.go.
- hasGCMAsmS390X := cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
-
- hasGCMAsm := hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
+ hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
+ )
if hasGCMAsm {
// If AES-GCM hardware is provided then prioritise AES-GCM
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
+ varDefaultCipherSuitesTLS13 = []uint16{
+ TLS_AES_128_GCM_SHA256,
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ }
} else {
// Without AES-GCM hardware, we put the ChaCha20-Poly1305
// cipher suites first.
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
}
+ varDefaultCipherSuitesTLS13 = []uint16{
+ TLS_CHACHA20_POLY1305_SHA256,
+ TLS_AES_128_GCM_SHA256,
+ TLS_AES_256_GCM_SHA384,
+ }
}
varDefaultCipherSuites = make([]uint16, 0, len(cipherSuites))
bytesSent int64
packetsSent int64
- // warnCount counts the number of consecutive warning alerts received
+ // retryCount counts the number of consecutive warning alerts received
// by Conn.readRecord. Protected by in.Mutex.
- warnCount int
+ retryCount int
// activeCall is an atomic int32; the low bit is whether Close has
// been called. the rest of the bits are the number of goroutines
// decrypt authenticates and decrypts the record if protection is active at
// this stage. The returned plaintext might overlap with the input.
-func (hc *halfConn) decrypt(record []byte) (plaintext []byte, err error) {
+func (hc *halfConn) decrypt(record []byte) ([]byte, recordType, error) {
+ var plaintext []byte
+ typ := recordType(record[0])
payload := record[recordHeaderLen:]
+ // In TLS 1.3, change_cipher_spec messages are to be ignored without being
+ // decrypted. See RFC 8446, Appendix D.4.
+ if hc.version == VersionTLS13 && typ == recordTypeChangeCipherSpec {
+ return payload, typ, nil
+ }
+
paddingGood := byte(255)
paddingLen := 0
c.XORKeyStream(payload, payload)
case aead:
if len(payload) < explicitNonceLen {
- return nil, alertBadRecordMAC
+ return nil, 0, alertBadRecordMAC
}
nonce := payload[:explicitNonceLen]
if len(nonce) == 0 {
}
payload = payload[explicitNonceLen:]
- copy(hc.additionalData[:], hc.seq[:])
- copy(hc.additionalData[8:], record[:3])
- n := len(payload) - c.Overhead()
- hc.additionalData[11] = byte(n >> 8)
- hc.additionalData[12] = byte(n)
+ additionalData := hc.additionalData[:]
+ if hc.version == VersionTLS13 {
+ additionalData = record[:recordHeaderLen]
+ } else {
+ copy(additionalData, hc.seq[:])
+ copy(additionalData[8:], record[:3])
+ n := len(payload) - c.Overhead()
+ additionalData[11] = byte(n >> 8)
+ additionalData[12] = byte(n)
+ }
var err error
- plaintext, err = c.Open(payload[:0], nonce, payload, hc.additionalData[:])
+ plaintext, err = c.Open(payload[:0], nonce, payload, additionalData)
if err != nil {
- return nil, alertBadRecordMAC
+ return nil, 0, alertBadRecordMAC
}
case cbcMode:
blockSize := c.BlockSize()
minPayload := explicitNonceLen + roundUp(hc.mac.Size()+1, blockSize) // TODO: vuln?
if len(payload)%blockSize != 0 || len(payload) < minPayload {
- return nil, alertBadRecordMAC
+ return nil, 0, alertBadRecordMAC
}
if explicitNonceLen > 0 {
default:
panic("unknown cipher type")
}
+
+ if hc.version == VersionTLS13 {
+ if typ != recordTypeApplicationData {
+ return nil, 0, alertUnexpectedMessage
+ }
+ if len(plaintext) > maxPlaintext+1 {
+ return nil, 0, alertRecordOverflow
+ }
+ // Remove padding and find the ContentType scanning from the end.
+ for i := len(plaintext) - 1; i >= 0; i-- {
+ if plaintext[i] != 0 {
+ typ = recordType(plaintext[i])
+ plaintext = plaintext[:i]
+ break
+ }
+ if i == 0 {
+ return nil, 0, alertUnexpectedMessage
+ }
+ }
+ }
} else {
plaintext = payload
}
if hc.mac != nil {
macSize := hc.mac.Size()
if len(payload) < macSize {
- return nil, alertBadRecordMAC
+ return nil, 0, alertBadRecordMAC
}
n := len(payload) - macSize - paddingLen
localMAC := hc.mac.MAC(hc.seq[0:], record[:recordHeaderLen], payload[:n], payload[n+macSize:])
if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
- return nil, alertBadRecordMAC
+ return nil, 0, alertBadRecordMAC
}
plaintext = payload[:n]
}
hc.incSeq()
- return plaintext, nil
+ return plaintext, typ, nil
}
// sliceForAppend extends the input slice by n bytes. head is the full extended
nonce = hc.seq[:]
}
- copy(hc.additionalData[:], hc.seq[:])
- copy(hc.additionalData[8:], record[:3])
- hc.additionalData[11] = byte(len(payload) >> 8)
- hc.additionalData[12] = byte(len(payload))
+ if hc.version == VersionTLS13 {
+ record = append(record, payload...)
+
+ // Encrypt the actual ContentType and replace the plaintext one.
+ record = append(record, record[0])
+ record[0] = byte(recordTypeApplicationData)
- record = c.Seal(record, nonce, payload, hc.additionalData[:])
+ n := len(payload) + 1 + c.Overhead()
+ record[3] = byte(n >> 8)
+ record[4] = byte(n)
+
+ record = c.Seal(record[:recordHeaderLen],
+ nonce, record[recordHeaderLen:], record[:recordHeaderLen])
+ } else {
+ copy(hc.additionalData[:], hc.seq[:])
+ copy(hc.additionalData[8:], record)
+ record = c.Seal(record, nonce, payload, hc.additionalData[:])
+ }
case cbcMode:
blockSize := c.BlockSize()
plaintextLen := len(payload) + len(mac)
vers := uint16(hdr[1])<<8 | uint16(hdr[2])
n := int(hdr[3])<<8 | int(hdr[4])
- if c.haveVers && vers != c.vers {
+ if c.haveVers && c.vers != VersionTLS13 && 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(nil, msg))
return c.in.setErrorLocked(c.newRecordHeaderError(c.conn, "first record does not look like a TLS handshake"))
}
}
- if n > maxCiphertext {
+ if c.vers == VersionTLS13 && n > maxCiphertextTLS13 || n > maxCiphertext {
c.sendAlert(alertRecordOverflow)
msg := fmt.Sprintf("oversized record received with length %d", n)
return c.in.setErrorLocked(c.newRecordHeaderError(nil, msg))
// Process message.
record := c.rawInput.Next(recordHeaderLen + n)
- data, err := c.in.decrypt(record)
+ data, typ, err := c.in.decrypt(record)
if err != nil {
return c.in.setErrorLocked(c.sendAlert(err.(alert)))
}
return c.in.setErrorLocked(c.sendAlert(alertRecordOverflow))
}
- if typ != recordTypeAlert && len(data) > 0 {
- // this is a valid non-alert message: reset the count of alerts
- c.warnCount = 0
+ // Application Data messages are always protected.
+ if c.in.cipher == nil && typ == recordTypeApplicationData {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ if typ != recordTypeAlert && typ != recordTypeChangeCipherSpec && 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 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+
+ // In TLS 1.3, change_cipher_spec records are ignored until the Finished.
+ // See RFC 8446, Appendix D.4. Note that according to Section 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.
+ if c.vers == VersionTLS13 && typ == recordTypeChangeCipherSpec {
+ if len(data) != 1 || data[0] != 1 || c.handshakeComplete() {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
+ return c.retryReadRecord(want)
}
switch typ {
if alert(data[1]) == alertCloseNotify {
return c.in.setErrorLocked(io.EOF)
}
+ if c.vers == VersionTLS13 {
+ return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
+ }
switch data[0] {
case alertLevelWarning:
- c.warnCount++
- if c.warnCount > maxWarnAlertCount {
- c.sendAlert(alertUnexpectedMessage)
- return c.in.setErrorLocked(errors.New("tls: too many warn alerts"))
- }
- return c.readRecord(want) // Drop the record on the floor and retry.
+ return c.retryReadRecord(want) // Drop the record on the floor and retry.
case alertLevelError:
return c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
default:
if typ != want {
return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
+ if len(data) == 0 {
+ return c.retryReadRecord(want)
+ }
// Note that data is owned by c.rawInput, following the Next call above,
// to avoid copying the plaintext. This is safe because c.rawInput is
// not read from or written to until c.input is drained.
return nil
case recordTypeHandshake:
- if typ != want && !(c.isClient && c.config.Renegotiation != RenegotiateNever) {
+ if typ != want && !c.isRenegotiationAcceptable() {
return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
}
+ if len(data) == 0 {
+ return c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
+ }
c.hand.Write(data)
return nil
}
}
+// retryReadRecord recurses into readRecord to drop a non-advancing record, like
+// a warning alert, empty application_data, or a change_cipher_spec in TLS 1.3.
+func (c *Conn) retryReadRecord(want recordType) error {
+ c.retryCount++
+ if c.retryCount > maxUselessRecords {
+ c.sendAlert(alertUnexpectedMessage)
+ return c.in.setErrorLocked(errors.New("tls: too many ignored records"))
+ }
+ return c.readRecord(want)
+}
+
+func (c *Conn) isRenegotiationAcceptable() bool {
+ return c.isClient &&
+ c.vers != VersionTLS13 &&
+ c.handshakeComplete() &&
+ c.config.Renegotiation != RenegotiateNever
+}
+
// 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.
panic("unknown cipher type")
}
}
+ if c.vers == VersionTLS13 {
+ payloadBytes-- // encrypted ContentType
+ }
// Allow packet growth in arithmetic progression up to max.
pkt := c.packetsSent
// Some TLS servers fail if the record version is
// greater than TLS 1.0 for the initial ClientHello.
vers = VersionTLS10
+ } else if vers == VersionTLS13 {
+ // TLS 1.3 froze the record layer version to 1.2.
+ // See RFC 8446, Section 5.1.
+ vers = VersionTLS12
}
c.outBuf[1] = byte(vers >> 8)
c.outBuf[2] = byte(vers)