Browse Source

retrieve the handshake write key when receiving the ServerHello

Marten Seemann 1 month ago
parent
commit
ad3f39a5d7

+ 6 - 6
internal/handshake/crypto_setup.go

@@ -359,6 +359,12 @@ func (h *cryptoSetup) handleMessageForClient(msgType messageType) bool {
 		case <-h.handshakeErrChan:
 			return false
 		}
+		// get the handshake write key
+		select {
+		case <-h.receivedWriteKey:
+		case <-h.handshakeErrChan:
+			return false
+		}
 		return true
 	case typeEncryptedExtensions:
 		select {
@@ -372,12 +378,6 @@ func (h *cryptoSetup) handleMessageForClient(msgType messageType) bool {
 		// nothing to do
 		return false
 	case typeFinished:
-		// get the handshake write key
-		select {
-		case <-h.receivedWriteKey:
-		case <-h.handshakeErrChan:
-			return false
-		}
 		// While the order of these two is not defined by the TLS spec,
 		// we have to do it on the same order as our TLS library does it.
 		// get the handshake write key

+ 28 - 12
vendor/github.com/marten-seemann/qtls/13.go

@@ -116,9 +116,9 @@ func (ks *keySchedule13) setSecret(secret []byte) {
 	salt := ks.secret
 	if salt != nil {
 		h0 := hash.New().Sum(nil)
-		salt = HkdfExpandLabel(hash, salt, h0, "derived", hash.Size())
+		salt = hkdfExpandLabel(hash, salt, h0, "derived", hash.Size())
 	}
-	ks.secret = HkdfExtract(hash, secret, salt)
+	ks.secret = hkdfExtract(hash, secret, salt)
 }
 
 // Depending on role returns pair of key variant to be used by
@@ -168,7 +168,7 @@ func (ks *keySchedule13) deriveSecret(secretLabel secretLabel) []byte {
 		ks.handshakeCtx = ks.transcriptHash.Sum(nil)
 	}
 	hash := hashForSuite(ks.suite)
-	secret := HkdfExpandLabel(hash, ks.secret, ks.handshakeCtx, label, hash.Size())
+	secret := hkdfExpandLabel(hash, ks.secret, ks.handshakeCtx, label, hash.Size())
 	if keylogType != "" && ks.config != nil {
 		ks.config.writeKeyLog(keylogType, ks.clientRandom, secret)
 	}
@@ -177,8 +177,8 @@ func (ks *keySchedule13) deriveSecret(secretLabel secretLabel) []byte {
 
 func (ks *keySchedule13) prepareCipher(trafficSecret []byte) cipher.AEAD {
 	hash := hashForSuite(ks.suite)
-	key := HkdfExpandLabel(hash, trafficSecret, nil, "key", ks.suite.keyLen)
-	iv := HkdfExpandLabel(hash, trafficSecret, nil, "iv", ks.suite.ivLen)
+	key := hkdfExpandLabel(hash, trafficSecret, nil, "key", ks.suite.keyLen)
+	iv := hkdfExpandLabel(hash, trafficSecret, nil, "iv", ks.suite.ivLen)
 	return ks.suite.aead(key, iv)
 }
 
@@ -254,10 +254,11 @@ CurvePreferenceLoop:
 	hs.keySchedule.setSecret(ecdheSecret)
 	hs.hsClientTrafficSecret = hs.keySchedule.deriveSecret(secretHandshakeClient)
 	hsServerTrafficSecret := hs.keySchedule.deriveSecret(secretHandshakeServer)
+	c.out.exportKey(hs.keySchedule.suite, hsServerTrafficSecret)
 	c.out.setKey(c.vers, hs.keySchedule.suite, hsServerTrafficSecret)
 
-	serverFinishedKey := HkdfExpandLabel(hash, hsServerTrafficSecret, nil, "finished", hashSize)
-	hs.clientFinishedKey = HkdfExpandLabel(hash, hs.hsClientTrafficSecret, nil, "finished", hashSize)
+	serverFinishedKey := hkdfExpandLabel(hash, hsServerTrafficSecret, nil, "finished", hashSize)
+	hs.clientFinishedKey = hkdfExpandLabel(hash, hs.hsClientTrafficSecret, nil, "finished", hashSize)
 
 	// EncryptedExtensions
 	hs.keySchedule.write(hs.hello13Enc.marshal())
@@ -296,6 +297,7 @@ CurvePreferenceLoop:
 
 	hs.keySchedule.setSecret(nil) // derive master secret
 	serverAppTrafficSecret := hs.keySchedule.deriveSecret(secretApplicationServer)
+	c.out.exportKey(hs.keySchedule.suite, serverAppTrafficSecret)
 	c.out.setKey(c.vers, hs.keySchedule.suite, serverAppTrafficSecret)
 
 	if c.hand.Len() > 0 {
@@ -303,9 +305,11 @@ CurvePreferenceLoop:
 	}
 	hs.appClientTrafficSecret = hs.keySchedule.deriveSecret(secretApplicationClient)
 	if hs.hello13Enc.earlyData {
+		c.in.exportKey(hs.keySchedule.suite, earlyClientTrafficSecret)
 		c.in.setKey(c.vers, hs.keySchedule.suite, earlyClientTrafficSecret)
 		c.phase = readingEarlyData
 	} else {
+		c.in.exportKey(hs.keySchedule.suite, hs.hsClientTrafficSecret)
 		c.in.setKey(c.vers, hs.keySchedule.suite, hs.hsClientTrafficSecret)
 		if hs.clientHello.earlyData {
 			c.phase = discardingEarlyData
@@ -418,6 +422,7 @@ func (hs *serverHandshakeState) readClientFinished13(hasConfirmLock bool) error
 	if c.hand.Len() > 0 {
 		return c.sendAlert(alertUnexpectedMessage)
 	}
+	c.in.exportKey(hs.keySchedule.suite, hs.appClientTrafficSecret)
 	c.in.setKey(c.vers, hs.keySchedule.suite, hs.appClientTrafficSecret)
 	c.in.traceErr, c.out.traceErr = nil, nil
 	c.phase = handshakeConfirmed
@@ -514,6 +519,7 @@ func (c *Conn) handleEndOfEarlyData() error {
 	}
 	c.hs.keySchedule.write(endOfEarlyData.marshal())
 	c.phase = waitingClientFinished
+	c.in.exportKey(c.hs.keySchedule.suite, c.hs.hsClientTrafficSecret)
 	c.in.setKey(c.vers, c.hs.keySchedule.suite, c.hs.hsClientTrafficSecret)
 	return nil
 }
@@ -618,6 +624,10 @@ func (c *Conn) deriveDHESecret(ks keyShare, secretKey []byte) []byte {
 
 // HkdfExpandLabel HKDF expands a label
 func HkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
+	return hkdfExpandLabel(hash, secret, hashValue, label, L)
+}
+
+func hkdfExpandLabel(hash crypto.Hash, secret, hashValue []byte, label string, L int) []byte {
 	prefix := "tls13 "
 	hkdfLabel := make([]byte, 4+len(prefix)+len(label)+len(hashValue))
 	hkdfLabel[0] = byte(L >> 8)
@@ -710,7 +720,7 @@ func (hs *serverHandshakeState) checkPSK() (isResumed bool, alert alert) {
 
 		hs.keySchedule.setSecret(s.pskSecret)
 		binderKey := hs.keySchedule.deriveSecret(secretResumptionPskBinder)
-		binderFinishedKey := HkdfExpandLabel(hash, binderKey, nil, "finished", hashSize)
+		binderFinishedKey := hkdfExpandLabel(hash, binderKey, nil, "finished", hashSize)
 		chHash := hash.New()
 		chHash.Write(hs.clientHello.rawTruncated)
 		expectedBinder := hmacOfSum(hash, chHash, binderFinishedKey)
@@ -781,7 +791,7 @@ func (hs *serverHandshakeState) sendSessionTicket13() error {
 		// tickets might have the same PSK which could be a problem if
 		// one of them is compromised.
 		ticketNonce := []byte{byte(i)}
-		sessionState.pskSecret = HkdfExpandLabel(hash, resumptionMasterSecret, ticketNonce, "resumption", hash.Size())
+		sessionState.pskSecret = hkdfExpandLabel(hash, resumptionMasterSecret, ticketNonce, "resumption", hash.Size())
 		ticket := sessionState.marshal()
 		var err error
 		if c.config.SessionTicketSealer != nil {
@@ -1006,13 +1016,17 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
 		c.sendAlert(alertUnexpectedMessage)
 		return errors.New("tls: unexpected data after Server Hello")
 	}
-	// Do not change the sender key yet, the server must authenticate first.
 	serverHandshakeSecret := hs.keySchedule.deriveSecret(secretHandshakeServer)
+	c.in.exportKey(hs.keySchedule.suite, serverHandshakeSecret)
+	// Already the sender key yet, when using an alternative record layer.
+	// QUIC needs the handshake write key in order to acknowlege Handshake packets.
+	c.out.exportKey(hs.keySchedule.suite, clientHandshakeSecret)
+	// Do not change the sender key yet, the server must authenticate first.
 	c.in.setKey(c.vers, hs.keySchedule.suite, serverHandshakeSecret)
 
 	// Calculate MAC key for Finished messages.
-	serverFinishedKey := HkdfExpandLabel(hash, serverHandshakeSecret, nil, "finished", hashSize)
-	clientFinishedKey := HkdfExpandLabel(hash, clientHandshakeSecret, nil, "finished", hashSize)
+	serverFinishedKey := hkdfExpandLabel(hash, serverHandshakeSecret, nil, "finished", hashSize)
+	clientFinishedKey := hkdfExpandLabel(hash, clientHandshakeSecret, nil, "finished", hashSize)
 
 	msg, err := c.readHandshake()
 	if err != nil {
@@ -1155,11 +1169,13 @@ func (hs *clientHandshakeState) doTLS13Handshake() error {
 
 	// Handshake done, set application traffic secret
 	// TODO store initial traffic secret key for KeyUpdate GH #85
+	c.out.exportKey(hs.keySchedule.suite, clientAppTrafficSecret)
 	c.out.setKey(c.vers, hs.keySchedule.suite, clientAppTrafficSecret)
 	if c.hand.Len() > 0 {
 		c.sendAlert(alertUnexpectedMessage)
 		return errors.New("tls: unexpected data after handshake")
 	}
+	c.in.exportKey(hs.keySchedule.suite, serverAppTrafficSecret)
 	c.in.setKey(c.vers, hs.keySchedule.suite, serverAppTrafficSecret)
 	return nil
 }

+ 8 - 3
vendor/github.com/marten-seemann/qtls/conn.go

@@ -234,15 +234,20 @@ func (hc *halfConn) changeCipherSpec() error {
 	return nil
 }
 
-func (hc *halfConn) setKey(version uint16, suite *cipherSuite, trafficSecret []byte) {
+func (hc *halfConn) exportKey(suite *cipherSuite, trafficSecret []byte) {
 	if hc.setKeyCallback != nil {
 		hc.setKeyCallback(&CipherSuite{*suite}, trafficSecret)
+	}
+}
+
+func (hc *halfConn) setKey(version uint16, suite *cipherSuite, trafficSecret []byte) {
+	if hc.setKeyCallback != nil {
 		return
 	}
 	hc.version = version
 	hash := hashForSuite(suite)
-	key := HkdfExpandLabel(hash, trafficSecret, nil, "key", suite.keyLen)
-	iv := HkdfExpandLabel(hash, trafficSecret, nil, "iv", suite.ivLen)
+	key := hkdfExpandLabel(hash, trafficSecret, nil, "key", suite.keyLen)
+	iv := hkdfExpandLabel(hash, trafficSecret, nil, "iv", suite.ivLen)
 	hc.cipher = suite.aead(key, iv)
 	for i := range hc.seq {
 		hc.seq[i] = 0

+ 4 - 0
vendor/github.com/marten-seemann/qtls/hkdf.go

@@ -47,6 +47,10 @@ func hkdfExpand(hash crypto.Hash, prk, info []byte, l int) []byte {
 
 // HkdfExtract generates a pseudorandom key for use with Expand from an input secret and an optional independent salt.
 func HkdfExtract(hash crypto.Hash, secret, salt []byte) []byte {
+	return hkdfExtract(hash, secret, salt)
+}
+
+func hkdfExtract(hash crypto.Hash, secret, salt []byte) []byte {
 	if salt == nil {
 		salt = make([]byte, hash.Size())
 	}

+ 3 - 3
vendor/vendor.json

@@ -45,10 +45,10 @@
 			"revisionTime": "2018-11-11T22:04:28Z"
 		},
 		{
-			"checksumSHA1": "dpjM/eonkDPExO4QWg4+R0wZPCs=",
+			"checksumSHA1": "9D0GoLWn+P00plU66qfLxFB1kNg=",
 			"path": "github.com/marten-seemann/qtls",
-			"revision": "26b223ad36d4436ed3eeb843041b66ac21dcee34",
-			"revisionTime": "2019-01-06T03:45:47Z"
+			"revision": "646330209b76bfdcdc054a863468f473e9d0a7af",
+			"revisionTime": "2019-01-10T16:28:36Z"
 		},
 		{
 			"checksumSHA1": "9TPZ7plxFmlYtMEv2LLXRCEQg7c=",