From 824987c5ada49c2db185bdd6455141f3e8352acf Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 12 Nov 2017 07:44:45 +0000 Subject: [PATCH] tris: implement draft-22 middlebox compatibility mode Send/Skip CCS, set legacy record version to 3,3 and echo session ID. CCS must be ignored while the handshake is running, but not thereafter: https://tools.ietf.org/html/draft-ietf-tls-tls13-22#section-5 Unconditionally send CCS as server because bogo requires it, even if no session ID is included in the Client Hello. TLS 1.3 clients MUST ignore it anyway, so it should not hurt. Fixes interop with boringssl and openssl and passes bogo. --- 13.go | 11 +++++++++++ conn.go | 15 +++++++++++++-- handshake_client.go | 5 +++++ handshake_server.go | 1 + 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/13.go b/13.go index 58e534f..98f037b 100644 --- a/13.go +++ b/13.go @@ -197,6 +197,11 @@ CurvePreferenceLoop: return err } + // middlebox compatibility mode: send CCS after first handshake message + if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { + return err + } + hs.keySchedule.setSecret(ecdheSecret) clientCipher, cTrafficSecret := hs.keySchedule.prepareCipher(secretHandshakeClient) hs.hsClientCipher = clientCipher @@ -792,6 +797,12 @@ func (hs *clientHandshakeState) doTLS13Handshake() error { hash := hashForSuite(hs.suite) hashSize := hash.Size() serverHello := hs.serverHello + + // middlebox compatibility mode, send CCS before second flight. + if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil { + return err + } + // TODO check if keyshare is unacceptable, raise HRR. clientKS := hs.hello.keyShares[0] diff --git a/conn.go b/conn.go index a8d4ed2..b252cbf 100644 --- a/conn.go +++ b/conn.go @@ -713,6 +713,16 @@ Again: // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) + + // TLS 1.3 middlebox compatibility: skip over unencrypted CCS. + if c.vers >= VersionTLS13 && typ == recordTypeChangeCipherSpec && c.phase != handshakeConfirmed { + if len(b.data) != 6 || b.data[5] != 1 { + c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + c.in.freeBlock(b) + return c.in.err + } + peekedAlert := peekAlert(b) // peek at a possible alert before decryption ok, off, alertValue := c.in.decrypt(b) switch { @@ -1044,7 +1054,8 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { 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 + // But for draft 22, this was changed to { 3, 3 }. + vers = VersionTLS12 } b.data[1] = byte(vers >> 8) b.data[2] = byte(vers) @@ -1069,7 +1080,7 @@ func (c *Conn) writeRecordLocked(typ recordType, data []byte) (int, error) { data = data[m:] } - if typ == recordTypeChangeCipherSpec { + if typ == recordTypeChangeCipherSpec && c.vers < VersionTLS13 { if err := c.out.changeCipherSpec(); err != nil { return n, c.sendAlertLocked(err.(alert)) } diff --git a/handshake_client.go b/handshake_client.go index c328fdd..d1264c4 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -197,6 +197,11 @@ func (c *Conn) clientHandshake() error { return err } hello.keyShares = []keyShare{clientKS} + // middlebox compatibility mode, provide a non-empty session ID + hello.sessionId = make([]byte, 16) + if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil { + return errors.New("tls: short read from Rand: " + err.Error()) + } } if err = hs.handshake(); err != nil { diff --git a/handshake_server.go b/handshake_server.go index fc129ec..48d4389 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -262,6 +262,7 @@ Curves: hs.hello13Enc = new(encryptedExtensionsMsg) hs.hello.vers = c.vers hs.hello.random = make([]byte, 32) + hs.hello.sessionId = hs.clientHello.sessionId _, err = io.ReadFull(c.config.rand(), hs.hello.random) if err != nil { c.sendAlert(alertInternalError)