Implement KeyUpdate in Go. Implemented in preparation for testing the C implementation. Tested against itself. BUG=74 Change-Id: Iec1b9ad22e09711fa4e67c97cc3eb257585c3ae5 Reviewed-on: https://boringssl-review.googlesource.com/8873 Reviewed-by: Nick Harper <nharper@chromium.org> Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go index fd95781..1014dea 100644 --- a/ssl/test/runner/common.go +++ b/ssl/test/runner/common.go
@@ -68,6 +68,7 @@ typeClientKeyExchange uint8 = 16 typeFinished uint8 = 20 typeCertificateStatus uint8 = 22 + typeKeyUpdate uint8 = 24 // draft-ietf-tls-tls13-13 typeNextProtocol uint8 = 67 // Not IANA assigned typeChannelID uint8 = 203 // Not IANA assigned )
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go index 969c50a..cefdde3 100644 --- a/ssl/test/runner/conn.go +++ b/ssl/test/runner/conn.go
@@ -159,6 +159,9 @@ // used to save allocating a new buffer for each MAC. inDigestBuf, outDigestBuf []byte + trafficSecret []byte + keyUpdateGeneration int + config *Config } @@ -203,13 +206,23 @@ return nil } -// updateKeys sets the current cipher state. -func (hc *halfConn) updateKeys(cipher interface{}, version uint16) { +// useTrafficSecret sets the current cipher state for TLS 1.3. +func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret, phase []byte, side trafficDirection) { hc.version = version - hc.cipher = cipher + hc.cipher = deriveTrafficAEAD(version, suite, secret, phase, side) + hc.trafficSecret = secret hc.incEpoch() } +func (hc *halfConn) doKeyUpdate(c *Conn, isOutgoing bool) { + side := serverWrite + if c.isClient == isOutgoing { + side = clientWrite + } + hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), applicationPhase, side) + hc.keyUpdateGeneration++ +} + // incSeq increments the sequence number. func (hc *halfConn) incSeq(isOutgoing bool) { limit := 0 @@ -1175,6 +1188,8 @@ m = new(helloVerifyRequestMsg) case typeChannelID: m = new(channelIDMsg) + case typeKeyUpdate: + m = new(keyUpdateMsg) default: return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) } @@ -1280,6 +1295,13 @@ return 0, alertInternalError } + // Catch up with KeyUpdates from the peer. + for c.out.keyUpdateGeneration < c.in.keyUpdateGeneration { + if err := c.sendKeyUpdateLocked(); err != nil { + return 0, err + } + } + if c.config.Bugs.SendSpuriousAlert != 0 { c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert) } @@ -1357,6 +1379,11 @@ } } + if _, ok := msg.(*keyUpdateMsg); ok { + c.in.doKeyUpdate(c, true) + return nil + } + // TODO(davidben): Add support for KeyUpdate. c.sendAlert(alertUnexpectedMessage) return alertUnexpectedMessage @@ -1648,3 +1675,21 @@ _, err := c.writeRecord(recordTypeHandshake, m.marshal()) return err } + +func (c *Conn) SendKeyUpdate() error { + c.out.Lock() + defer c.out.Unlock() + return c.sendKeyUpdateLocked() +} + +func (c *Conn) sendKeyUpdateLocked() error { + m := new(keyUpdateMsg) + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { + return err + } + if err := c.flushHandshake(); err != nil { + return err + } + c.out.doKeyUpdate(c, false) + return nil +}
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go index 7718447..b32be0e 100644 --- a/ssl/test/runner/handshake_client.go +++ b/ssl/test/runner/handshake_client.go
@@ -594,8 +594,8 @@ // Switch to handshake traffic keys. handshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, handshakeTrafficLabel) - c.out.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, clientWrite), c.vers) - c.in.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, serverWrite), c.vers) + c.out.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, clientWrite) + c.in.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, serverWrite) msg, err := c.readHandshake() if err != nil { @@ -767,10 +767,9 @@ c.flushHandshake() // Switch to application data keys. - c.out.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite), c.vers) - c.in.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, trafficSecret, applicationPhase, serverWrite), c.vers) + c.out.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite) + c.in.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, serverWrite) - // TODO(davidben): Save the traffic secret for KeyUpdate. c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel) c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel) return nil
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go index 41a8fb2..8e73a3c 100644 --- a/ssl/test/runner/handshake_messages.go +++ b/ssl/test/runner/handshake_messages.go
@@ -1924,6 +1924,17 @@ return len(data) == 4 } +type keyUpdateMsg struct { +} + +func (*keyUpdateMsg) marshal() []byte { + return []byte{typeKeyUpdate, 0, 0, 0} +} + +func (*keyUpdateMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + func eqUint16s(x, y []uint16) bool { if len(x) != len(y) { return false
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go index a660f72..012c836 100644 --- a/ssl/test/runner/handshake_server.go +++ b/ssl/test/runner/handshake_server.go
@@ -490,8 +490,8 @@ // Switch to handshake traffic keys. handshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, handshakeTrafficLabel) - c.out.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, serverWrite), c.vers) - c.in.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, clientWrite), c.vers) + c.out.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, serverWrite) + c.in.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, clientWrite) if hs.suite.flags&suitePSK != 0 { return errors.New("tls: PSK ciphers not implemented for TLS 1.3") @@ -591,7 +591,7 @@ // Switch to application data keys on write. In particular, any alerts // from the client certificate are sent over these keys. - c.out.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, trafficSecret, applicationPhase, serverWrite), c.vers) + c.out.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, serverWrite) // If we requested a client certificate, then the client must send a // certificate message, even if it's empty. @@ -664,9 +664,8 @@ hs.writeClientHash(clientFinished.marshal()) // Switch to application data keys on read. - c.in.updateKeys(deriveTrafficAEAD(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite), c.vers) + c.in.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite) - // TODO(davidben): Save the traffic secret for KeyUpdate. c.cipherSuite = hs.suite c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel) c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go index abee6e6..220aa44 100644 --- a/ssl/test/runner/prf.go +++ b/ssl/test/runner/prf.go
@@ -493,3 +493,7 @@ return suite.aead(version, key, iv) } + +func updateTrafficSecret(hash crypto.Hash, secret []byte) []byte { + return hkdfExpandLabel(hash, secret, applicationTrafficLabel, nil, hash.Size()) +}