Updating Key Schedule and KeyUpdate to draft 16.

This doesn't currently honor the required KeyUpdate response. That will
be done in a follow-up.

BUG=74

Change-Id: I750fc41278736cb24230303815e839c6f6967b6a
Reviewed-on: https://boringssl-review.googlesource.com/11412
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h
index 0243ac4..f615073 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -896,7 +896,8 @@
   size_t hash_len;
   uint8_t resumption_hash[EVP_MAX_MD_SIZE];
   uint8_t secret[EVP_MAX_MD_SIZE];
-  uint8_t traffic_secret_0[EVP_MAX_MD_SIZE];
+  uint8_t client_traffic_secret_0[EVP_MAX_MD_SIZE];
+  uint8_t server_traffic_secret_0[EVP_MAX_MD_SIZE];
 
   union {
     /* sent is a bitset where the bits correspond to elements of kExtensions
@@ -1347,6 +1348,11 @@
 #define SSL_PSK_AUTH      0x0
 #define SSL_PSK_SIGN_AUTH 0x1
 
+/* From draft-ietf-tls-tls13-16, used in determining whether to respond with a
+ * KeyUpdate. */
+#define SSL_KEY_UPDATE_NOT_REQUESTED 0
+#define SSL_KEY_UPDATE_REQUESTED 1
+
 CERT *ssl_cert_new(void);
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index cb5afab..2d5fc80 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -148,7 +148,10 @@
   }
 
   OPENSSL_cleanse(hs->secret, sizeof(hs->secret));
-  OPENSSL_cleanse(hs->traffic_secret_0, sizeof(hs->traffic_secret_0));
+  OPENSSL_cleanse(hs->client_traffic_secret_0,
+                  sizeof(hs->client_traffic_secret_0));
+  OPENSSL_cleanse(hs->server_traffic_secret_0,
+                  sizeof(hs->server_traffic_secret_0));
   SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
   OPENSSL_free(hs->peer_key);
   OPENSSL_free(hs->server_params);
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 8fe61a4..c03cedb 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -206,6 +206,12 @@
 	pskSignAuthMode = 1
 )
 
+// KeyUpdateRequest values (see draft-ietf-tls-tls13-16, section 4.5.3)
+const (
+	keyUpdateNotRequested = 0
+	keyUpdateRequested    = 1
+)
+
 // ConnectionState records basic TLS details about the connection.
 type ConnectionState struct {
 	Version                    uint16                // TLS version used by the connection (e.g. VersionTLS12)
@@ -956,10 +962,6 @@
 	// message. This only makes sense for a server.
 	SendHelloRequestBeforeEveryHandshakeMessage bool
 
-	// SendKeyUpdateBeforeEveryAppDataRecord, if true, causes a KeyUpdate
-	// handshake message to be sent before each application data record.
-	SendKeyUpdateBeforeEveryAppDataRecord bool
-
 	// RequireDHPublicValueLen causes a fatal error if the length (in
 	// bytes) of the server's Diffie-Hellman public value is not equal to
 	// this.
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 24f0d60..f5014d4 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -92,6 +92,8 @@
 	handMsgLen       int      // handshake message length, not including the header
 	pendingFragments [][]byte // pending outgoing handshake fragments.
 
+	keyUpdateRequested bool
+
 	tmp [16]byte
 }
 
@@ -159,8 +161,7 @@
 	// used to save allocating a new buffer for each MAC.
 	inDigestBuf, outDigestBuf []byte
 
-	trafficSecret       []byte
-	keyUpdateGeneration int
+	trafficSecret []byte
 
 	config *Config
 }
@@ -223,7 +224,6 @@
 		side = clientWrite
 	}
 	hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), applicationPhase, side)
-	hc.keyUpdateGeneration++
 }
 
 // incSeq increments the sequence number.
@@ -1328,11 +1328,11 @@
 		return 0, alertInternalError
 	}
 
-	// Catch up with KeyUpdates from the peer.
-	for c.out.keyUpdateGeneration < c.in.keyUpdateGeneration {
-		if err := c.sendKeyUpdateLocked(); err != nil {
+	if c.keyUpdateRequested {
+		if err := c.sendKeyUpdateLocked(keyUpdateNotRequested); err != nil {
 			return 0, err
 		}
+		c.keyUpdateRequested = false
 	}
 
 	if c.config.Bugs.SendSpuriousAlert != 0 {
@@ -1344,12 +1344,6 @@
 		c.flushHandshake()
 	}
 
-	if c.config.Bugs.SendKeyUpdateBeforeEveryAppDataRecord {
-		if err := c.sendKeyUpdateLocked(); err != nil {
-			return 0, err
-		}
-	}
-
 	// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
 	// attack when using block mode ciphers due to predictable IVs.
 	// This can be prevented by splitting each Application Data
@@ -1441,8 +1435,11 @@
 		}
 	}
 
-	if _, ok := msg.(*keyUpdateMsg); ok {
+	if keyUpdate, ok := msg.(*keyUpdateMsg); ok {
 		c.in.doKeyUpdate(c, false)
+		if keyUpdate.keyUpdateRequest == keyUpdateRequested {
+			c.keyUpdateRequested = true
+		}
 		return nil
 	}
 
@@ -1751,18 +1748,20 @@
 	return err
 }
 
-func (c *Conn) SendKeyUpdate() error {
+func (c *Conn) SendKeyUpdate(keyUpdateRequest byte) error {
 	c.out.Lock()
 	defer c.out.Unlock()
-	return c.sendKeyUpdateLocked()
+	return c.sendKeyUpdateLocked(keyUpdateRequest)
 }
 
-func (c *Conn) sendKeyUpdateLocked() error {
+func (c *Conn) sendKeyUpdateLocked(keyUpdateRequest byte) error {
 	if c.vers < VersionTLS13 {
 		return errors.New("tls: attempted to send KeyUpdate before TLS 1.3")
 	}
 
-	m := new(keyUpdateMsg)
+	m := keyUpdateMsg{
+		keyUpdateRequest: keyUpdateRequest,
+	}
 	if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
 		return err
 	}
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index c5be2b7..291a3b4 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -644,9 +644,10 @@
 	handshakeSecret := hs.finishedHash.extractKey(earlySecret, ecdheSecret)
 
 	// Switch to handshake traffic keys.
-	handshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, handshakeTrafficLabel)
-	c.out.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, clientWrite)
-	c.in.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, serverWrite)
+	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
+	c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
+	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
+	c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
 
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -756,7 +757,7 @@
 		return unexpectedMessageError(serverFinished, msg)
 	}
 
-	verify := hs.finishedHash.serverSum(handshakeTrafficSecret)
+	verify := hs.finishedHash.serverSum(serverHandshakeTrafficSecret)
 	if len(verify) != len(serverFinished.verifyData) ||
 		subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 {
 		c.sendAlert(alertHandshakeFailure)
@@ -768,7 +769,8 @@
 	// The various secrets do not incorporate the client's final leg, so
 	// derive them now before updating the handshake context.
 	masterSecret := hs.finishedHash.extractKey(handshakeSecret, zeroSecret)
-	trafficSecret := hs.finishedHash.deriveSecret(masterSecret, applicationTrafficLabel)
+	clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
+	serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -813,7 +815,7 @@
 
 	// Send a client Finished message.
 	finished := new(finishedMsg)
-	finished.verifyData = hs.finishedHash.clientSum(handshakeTrafficSecret)
+	finished.verifyData = hs.finishedHash.clientSum(clientHandshakeTrafficSecret)
 	if c.config.Bugs.BadFinished {
 		finished.verifyData[0]++
 	}
@@ -830,8 +832,8 @@
 	c.flushHandshake()
 
 	// Switch to application data keys.
-	c.out.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite)
-	c.in.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, serverWrite)
+	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
+	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
 
 	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
 	c.resumptionSecret = hs.finishedHash.deriveSecret(masterSecret, resumptionLabel)
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 1e91ac3..b92735e 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -2094,14 +2094,32 @@
 }
 
 type keyUpdateMsg struct {
+	raw              []byte
+	keyUpdateRequest byte
 }
 
-func (*keyUpdateMsg) marshal() []byte {
-	return []byte{typeKeyUpdate, 0, 0, 0}
+func (m *keyUpdateMsg) marshal() []byte {
+	if m.raw != nil {
+		return m.raw
+	}
+
+	return []byte{typeKeyUpdate, 0, 0, 1, m.keyUpdateRequest}
 }
 
-func (*keyUpdateMsg) unmarshal(data []byte) bool {
-	return len(data) == 4
+func (m *keyUpdateMsg) unmarshal(data []byte) bool {
+	m.raw = data
+
+	if len(data) != 5 {
+		return false
+	}
+
+	length := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
+	if len(data)-4 != length {
+		return false
+	}
+
+	m.keyUpdateRequest = data[4]
+	return m.keyUpdateRequest == keyUpdateNotRequested || m.keyUpdateRequest == keyUpdateRequested
 }
 
 func eqUint16s(x, y []uint16) bool {
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 59b34fa..7b26cb6 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -693,9 +693,10 @@
 	handshakeSecret := hs.finishedHash.extractKey(earlySecret, ecdheSecret)
 
 	// Switch to handshake traffic keys.
-	handshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, handshakeTrafficLabel)
-	c.out.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, serverWrite)
-	c.in.useTrafficSecret(c.vers, hs.suite, handshakeTrafficSecret, handshakePhase, clientWrite)
+	serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, serverHandshakeTrafficLabel)
+	c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, handshakePhase, serverWrite)
+	clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(handshakeSecret, clientHandshakeTrafficLabel)
+	c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, handshakePhase, clientWrite)
 
 	if hs.hello.useCertAuth {
 		if hs.clientHello.ocspStapling {
@@ -793,7 +794,7 @@
 	}
 
 	finished := new(finishedMsg)
-	finished.verifyData = hs.finishedHash.serverSum(handshakeTrafficSecret)
+	finished.verifyData = hs.finishedHash.serverSum(serverHandshakeTrafficSecret)
 	if config.Bugs.BadFinished {
 		finished.verifyData[0]++
 	}
@@ -807,11 +808,12 @@
 	// The various secrets do not incorporate the client's final leg, so
 	// derive them now before updating the handshake context.
 	masterSecret := hs.finishedHash.extractKey(handshakeSecret, hs.finishedHash.zeroSecret())
-	trafficSecret := hs.finishedHash.deriveSecret(masterSecret, applicationTrafficLabel)
+	clientTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, clientApplicationTrafficLabel)
+	serverTrafficSecret := hs.finishedHash.deriveSecret(masterSecret, serverApplicationTrafficLabel)
 
 	// Switch to application data keys on write. In particular, any alerts
 	// from the client certificate are sent over these keys.
-	c.out.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, serverWrite)
+	c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, applicationPhase, serverWrite)
 
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
@@ -875,7 +877,7 @@
 		return unexpectedMessageError(clientFinished, msg)
 	}
 
-	verify := hs.finishedHash.clientSum(handshakeTrafficSecret)
+	verify := hs.finishedHash.clientSum(clientHandshakeTrafficSecret)
 	if len(verify) != len(clientFinished.verifyData) ||
 		subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
 		c.sendAlert(alertHandshakeFailure)
@@ -884,7 +886,7 @@
 	hs.writeClientHash(clientFinished.marshal())
 
 	// Switch to application data keys on read.
-	c.in.useTrafficSecret(c.vers, hs.suite, trafficSecret, applicationPhase, clientWrite)
+	c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, applicationPhase, clientWrite)
 
 	c.cipherSuite = hs.suite
 	c.exporterSecret = hs.finishedHash.deriveSecret(masterSecret, exporterLabel)
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index 5c7b3ab..99ef64f 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -119,6 +119,7 @@
 var keyExpansionLabel = []byte("key expansion")
 var clientFinishedLabel = []byte("client finished")
 var serverFinishedLabel = []byte("server finished")
+var finishedLabel = []byte("finished")
 var channelIDLabel = []byte("TLS Channel ID signature\x00")
 var channelIDResumeLabel = []byte("Resumption\x00")
 
@@ -311,7 +312,7 @@
 		return out
 	}
 
-	clientFinishedKey := hkdfExpandLabel(h.hash, baseKey, clientFinishedLabel, nil, h.hash.Size())
+	clientFinishedKey := hkdfExpandLabel(h.hash, baseKey, finishedLabel, nil, h.hash.Size())
 	finishedHMAC := hmac.New(h.hash.New, clientFinishedKey)
 	finishedHMAC.Write(h.appendContextHashes(nil))
 	return finishedHMAC.Sum(nil)
@@ -330,7 +331,7 @@
 		return out
 	}
 
-	serverFinishedKey := hkdfExpandLabel(h.hash, baseKey, serverFinishedLabel, nil, h.hash.Size())
+	serverFinishedKey := hkdfExpandLabel(h.hash, baseKey, finishedLabel, nil, h.hash.Size())
 	finishedHMAC := hmac.New(h.hash.New, serverFinishedKey)
 	finishedHMAC.Write(h.appendContextHashes(nil))
 	return finishedHMAC.Sum(nil)
@@ -417,11 +418,14 @@
 
 // The following are labels for traffic secret derivation in TLS 1.3.
 var (
-	earlyTrafficLabel       = []byte("early traffic secret")
-	handshakeTrafficLabel   = []byte("handshake traffic secret")
-	applicationTrafficLabel = []byte("application traffic secret")
-	exporterLabel           = []byte("exporter master secret")
-	resumptionLabel         = []byte("resumption master secret")
+	earlyTrafficLabel             = []byte("client early traffic secret")
+	clientHandshakeTrafficLabel   = []byte("client handshake traffic secret")
+	serverHandshakeTrafficLabel   = []byte("server handshake traffic secret")
+	clientApplicationTrafficLabel = []byte("client application traffic secret")
+	serverApplicationTrafficLabel = []byte("server application traffic secret")
+	applicationTrafficLabel       = []byte("application traffic secret")
+	exporterLabel                 = []byte("exporter master secret")
+	resumptionLabel               = []byte("resumption master secret")
 )
 
 // deriveSecret implements TLS 1.3's Derive-Secret function, as defined in
@@ -474,11 +478,7 @@
 func deriveTrafficAEAD(version uint16, suite *cipherSuite, secret, phase []byte, side trafficDirection) interface{} {
 	label := make([]byte, 0, len(phase)+2+16)
 	label = append(label, phase...)
-	if side == clientWrite {
-		label = append(label, []byte(", client write key")...)
-	} else {
-		label = append(label, []byte(", server write key")...)
-	}
+	label = append(label, []byte(", key")...)
 	key := hkdfExpandLabel(suite.hash(), secret, label, nil, suite.keyLen)
 
 	label = label[:len(label)-3] // Remove "key" from the end.
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index fe2cf84..2256346 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -359,6 +359,8 @@
 	// sendKeyUpdates is the number of consecutive key updates to send
 	// before and after the test message.
 	sendKeyUpdates int
+	// keyUpdateRequest is the KeyUpdateRequest value to send in KeyUpdate messages.
+	keyUpdateRequest byte
 	// expectMessageDropped, if true, means the test message is expected to
 	// be dropped by the client rather than echoed back.
 	expectMessageDropped bool
@@ -616,7 +618,7 @@
 	}
 
 	for i := 0; i < test.sendKeyUpdates; i++ {
-		if err := tlsConn.SendKeyUpdate(); err != nil {
+		if err := tlsConn.SendKeyUpdate(test.keyUpdateRequest); err != nil {
 			return err
 		}
 	}
@@ -678,7 +680,7 @@
 		tlsConn.Write(testMessage)
 
 		for i := 0; i < test.sendKeyUpdates; i++ {
-			tlsConn.SendKeyUpdate()
+			tlsConn.SendKeyUpdate(test.keyUpdateRequest)
 		}
 
 		for i := 0; i < test.sendEmptyRecords; i++ {
@@ -1981,13 +1983,14 @@
 			expectedError:     ":TOO_MANY_WARNING_ALERTS:",
 		},
 		{
-			name: "SendKeyUpdates",
+			name: "TooManyKeyUpdates",
 			config: Config{
 				MaxVersion: VersionTLS13,
 			},
-			sendKeyUpdates: 33,
-			shouldFail:     true,
-			expectedError:  ":TOO_MANY_KEY_UPDATES:",
+			sendKeyUpdates:   33,
+			keyUpdateRequest: keyUpdateNotRequested,
+			shouldFail:       true,
+			expectedError:    ":TOO_MANY_KEY_UPDATES:",
 		},
 		{
 			name: "EmptySessionID",
@@ -2195,14 +2198,22 @@
 			expectedError: ":WRONG_VERSION_NUMBER:",
 		},
 		{
-			testType: clientTest,
-			name:     "KeyUpdate",
+			name: "KeyUpdate",
 			config: Config{
 				MaxVersion: VersionTLS13,
-				Bugs: ProtocolBugs{
-					SendKeyUpdateBeforeEveryAppDataRecord: true,
-				},
 			},
+			sendKeyUpdates:   1,
+			keyUpdateRequest: keyUpdateNotRequested,
+		},
+		{
+			name: "KeyUpdate-InvalidRequestMode",
+			config: Config{
+				MaxVersion: VersionTLS13,
+			},
+			sendKeyUpdates:   1,
+			keyUpdateRequest: 42,
+			shouldFail:       true,
+			expectedError:    ":DECODE_ERROR:",
 		},
 		{
 			name: "SendSNIWarningAlert",
@@ -8723,11 +8734,10 @@
 		name: "Peek-KeyUpdate",
 		config: Config{
 			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendKeyUpdateBeforeEveryAppDataRecord: true,
-			},
 		},
-		flags: []string{"-peek-then-read"},
+		sendKeyUpdates:   1,
+		keyUpdateRequest: keyUpdateNotRequested,
+		flags:            []string{"-peek-then-read"},
 	})
 }
 
diff --git a/ssl/tls13_both.c b/ssl/tls13_both.c
index 3928ab7..e32464d 100644
--- a/ssl/tls13_both.c
+++ b/ssl/tls13_both.c
@@ -403,13 +403,20 @@
 }
 
 static int tls13_receive_key_update(SSL *ssl) {
-  if (ssl->init_num != 0) {
+  CBS cbs;
+  uint8_t key_update_request;
+  CBS_init(&cbs, ssl->init_msg, ssl->init_num);
+  if (!CBS_get_u8(&cbs, &key_update_request) ||
+      CBS_len(&cbs) != 0 ||
+      (key_update_request != SSL_KEY_UPDATE_NOT_REQUESTED &&
+       key_update_request != SSL_KEY_UPDATE_REQUESTED)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return 0;
   }
 
-  // TODO(svaldez): Send KeyUpdate.
+  /* TODO(svaldez): Send KeyUpdate if |key_update_request| is
+   * |SSL_KEY_UPDATE_REQUESTED|. */
   return tls13_rotate_traffic_key(ssl, evp_aead_open);
 }
 
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 72cf8b1..69d2a35 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -536,9 +536,9 @@
 
 static enum ssl_hs_wait_t do_flush(SSL *ssl, SSL_HANDSHAKE *hs) {
   if (!tls13_set_traffic_key(ssl, type_data, evp_aead_open,
-                             hs->traffic_secret_0, hs->hash_len) ||
+                             hs->server_traffic_secret_0, hs->hash_len) ||
       !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
-                             hs->traffic_secret_0, hs->hash_len) ||
+                             hs->client_traffic_secret_0, hs->hash_len) ||
       !tls13_finalize_keys(ssl)) {
     return ssl_hs_error;
   }
diff --git a/ssl/tls13_enc.c b/ssl/tls13_enc.c
index 88fe8f0..1fcde51 100644
--- a/ssl/tls13_enc.c
+++ b/ssl/tls13_enc.c
@@ -134,42 +134,27 @@
     return 0;
   }
 
-  const char *phase;
+  const char *key_label, *iv_label;
   switch (type) {
     case type_early_handshake:
-      phase = "early handshake key expansion, ";
+      key_label = "early handshake key expansion, key";
+      iv_label = "early handshake key expansion, iv";
       break;
     case type_early_data:
-      phase = "early application data key expansion, ";
+      key_label = "early application data key expansion, key";
+      iv_label = "early application data key expansion, iv";
       break;
     case type_handshake:
-      phase = "handshake key expansion, ";
+      key_label = "handshake key expansion, key";
+      iv_label = "handshake key expansion, iv";
       break;
     case type_data:
-      phase = "application data key expansion, ";
+      key_label = "application data key expansion, key";
+      iv_label = "application data key expansion, iv";
       break;
     default:
       return 0;
   }
-  size_t phase_len = strlen(phase);
-
-  const char *purpose = "client write key";
-  if ((ssl->server && direction == evp_aead_seal) ||
-      (!ssl->server && direction == evp_aead_open)) {
-    purpose = "server write key";
-  }
-  size_t purpose_len = strlen(purpose);
-
-  /* The longest label has length 38 (type_early_data) + 16 (either purpose
-   * value). */
-  uint8_t label[38 + 16];
-  size_t label_len = phase_len + purpose_len;
-  if (label_len > sizeof(label)) {
-    assert(0);
-    return 0;
-  }
-  memcpy(label, phase, phase_len);
-  memcpy(label + phase_len, purpose, purpose_len);
 
   /* Look up cipher suite properties. */
   const EVP_AEAD *aead;
@@ -184,25 +169,18 @@
   /* Derive the key. */
   size_t key_len = EVP_AEAD_key_length(aead);
   uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
-  if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, label,
-                         label_len, NULL, 0, key_len)) {
+  if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len,
+                         (const uint8_t *)key_label, strlen(key_label), NULL, 0,
+                         key_len)) {
     return 0;
   }
 
-  /* The IV's label ends in "iv" instead of "key". */
-  if (label_len < 3) {
-    assert(0);
-    return 0;
-  }
-  label_len--;
-  label[label_len - 2] = 'i';
-  label[label_len - 1] = 'v';
-
   /* Derive the IV. */
   size_t iv_len = EVP_AEAD_nonce_length(aead);
   uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
-  if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, label,
-                         label_len, NULL, 0, iv_len)) {
+  if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len,
+                         (const uint8_t *)iv_label, strlen(iv_label), NULL, 0,
+                         iv_len)) {
     return 0;
   }
 
@@ -235,38 +213,69 @@
   return 1;
 }
 
-static const char kTLS13LabelHandshakeTraffic[] = "handshake traffic secret";
-static const char kTLS13LabelApplicationTraffic[] =
-    "application traffic secret";
+static const char kTLS13LabelClientHandshakeTraffic[] =
+    "client handshake traffic secret";
+static const char kTLS13LabelServerHandshakeTraffic[] =
+    "server handshake traffic secret";
+static const char kTLS13LabelClientApplicationTraffic[] =
+    "client application traffic secret";
+static const char kTLS13LabelServerApplicationTraffic[] =
+    "server application traffic secret";
 
 int tls13_set_handshake_traffic(SSL *ssl) {
   SSL_HANDSHAKE *hs = ssl->s3->hs;
 
-  uint8_t traffic_secret[EVP_MAX_MD_SIZE];
-  if (!derive_secret(ssl, traffic_secret, hs->hash_len,
-                     (const uint8_t *)kTLS13LabelHandshakeTraffic,
-                     strlen(kTLS13LabelHandshakeTraffic)) ||
-      !ssl_log_secret(ssl, "HANDSHAKE_TRAFFIC_SECRET", traffic_secret,
-                      hs->hash_len) ||
-      !tls13_set_traffic_key(ssl, type_handshake, evp_aead_open, traffic_secret,
-                             hs->hash_len) ||
-      !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal, traffic_secret,
-                             hs->hash_len)) {
+  uint8_t client_traffic_secret[EVP_MAX_MD_SIZE];
+  uint8_t server_traffic_secret[EVP_MAX_MD_SIZE];
+  if (!derive_secret(ssl, client_traffic_secret, hs->hash_len,
+                     (const uint8_t *)kTLS13LabelClientHandshakeTraffic,
+                     strlen(kTLS13LabelClientHandshakeTraffic)) ||
+      !ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
+                      client_traffic_secret, hs->hash_len) ||
+      !derive_secret(ssl, server_traffic_secret, hs->hash_len,
+                     (const uint8_t *)kTLS13LabelServerHandshakeTraffic,
+                     strlen(kTLS13LabelServerHandshakeTraffic)) ||
+      !ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET",
+                      server_traffic_secret, hs->hash_len)) {
     return 0;
   }
+
+  if (ssl->server) {
+    if (!tls13_set_traffic_key(ssl, type_handshake, evp_aead_open,
+                               client_traffic_secret, hs->hash_len) ||
+        !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal,
+                               server_traffic_secret, hs->hash_len)) {
+      return 0;
+    }
+  } else {
+    if (!tls13_set_traffic_key(ssl, type_handshake, evp_aead_open,
+                               server_traffic_secret, hs->hash_len) ||
+        !tls13_set_traffic_key(ssl, type_handshake, evp_aead_seal,
+                               client_traffic_secret, hs->hash_len)) {
+      return 0;
+    }
+  }
   return 1;
 }
 
 int tls13_derive_traffic_secret_0(SSL *ssl) {
   SSL_HANDSHAKE *hs = ssl->s3->hs;
 
-  return derive_secret(ssl, hs->traffic_secret_0, hs->hash_len,
-                       (const uint8_t *)kTLS13LabelApplicationTraffic,
-                       strlen(kTLS13LabelApplicationTraffic)) &&
-         ssl_log_secret(ssl, "TRAFFIC_SECRET_0", hs->traffic_secret_0,
-                        hs->hash_len);
+  return derive_secret(ssl, hs->client_traffic_secret_0, hs->hash_len,
+                       (const uint8_t *)kTLS13LabelClientApplicationTraffic,
+                       strlen(kTLS13LabelClientApplicationTraffic)) &&
+         ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0",
+                        hs->client_traffic_secret_0, hs->hash_len) &&
+         derive_secret(ssl, hs->server_traffic_secret_0, hs->hash_len,
+                       (const uint8_t *)kTLS13LabelServerApplicationTraffic,
+                       strlen(kTLS13LabelServerApplicationTraffic)) &&
+         ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
+                        hs->server_traffic_secret_0, hs->hash_len);
 }
 
+static const char kTLS13LabelApplicationTraffic[] =
+    "application traffic secret";
+
 int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
   const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
 
@@ -319,21 +328,11 @@
   size_t key_len = EVP_MD_size(digest);
 
   const uint8_t *traffic_secret;
-  const char *label;
-  if (is_server) {
-    label = "server finished";
-    if (ssl->server) {
-      traffic_secret = ssl->s3->write_traffic_secret;
-    } else {
-      traffic_secret = ssl->s3->read_traffic_secret;
-    }
+  const char *label = "finished";
+  if (is_server == ssl->server) {
+    traffic_secret = ssl->s3->write_traffic_secret;
   } else {
-    label = "client finished";
-    if (!ssl->server) {
-      traffic_secret = ssl->s3->write_traffic_secret;
-    } else {
-      traffic_secret = ssl->s3->read_traffic_secret;
-    }
+    traffic_secret = ssl->s3->read_traffic_secret;
   }
 
   uint8_t context_hashes[2 * EVP_MAX_MD_SIZE];
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index be485a1..532b708 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -459,7 +459,7 @@
   if (!tls13_advance_key_schedule(ssl, kZeroes, hs->hash_len) ||
       !tls13_derive_traffic_secret_0(ssl) ||
       !tls13_set_traffic_key(ssl, type_data, evp_aead_seal,
-                             hs->traffic_secret_0, hs->hash_len)) {
+                             hs->server_traffic_secret_0, hs->hash_len)) {
     return ssl_hs_error;
   }
 
@@ -523,7 +523,7 @@
       !ssl->method->hash_current_message(ssl) ||
       /* evp_aead_seal keys have already been switched. */
       !tls13_set_traffic_key(ssl, type_data, evp_aead_open,
-                             hs->traffic_secret_0, hs->hash_len) ||
+                             hs->client_traffic_secret_0, hs->hash_len) ||
       !tls13_finalize_keys(ssl)) {
     return ssl_hs_error;
   }