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/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
+}