diff mbox series

[openembedded-core,scarthgap,1/5] go 1.22.12: Fix CVE-2025-61730

Message ID 20260212045843.4046769-1-deeratho@cisco.com
State New
Headers show
Series [openembedded-core,scarthgap,1/5] go 1.22.12: Fix CVE-2025-61730 | expand

Commit Message

From: Deepak Rathore <deeratho@cisco.com>

Upstream Repository: https://github.com/golang/go.git

Bug details: https://nvd.nist.gov/vuln/detail/CVE-2025-61730
Type: Security Fix
CVE: CVE-2025-61730
Score: 4.2
Patch: https://github.com/golang/go/commit/ad2cd043db66

Signed-off-by: Deepak Rathore <deeratho@cisco.com>
diff mbox series

Patch

diff --git a/meta/recipes-devtools/go/go-1.22.12.inc b/meta/recipes-devtools/go/go-1.22.12.inc
index ca5016c2f5..e9a1803252 100644
--- a/meta/recipes-devtools/go/go-1.22.12.inc
+++ b/meta/recipes-devtools/go/go-1.22.12.inc
@@ -31,6 +31,7 @@  SRC_URI += "\
     file://CVE-2025-61724.patch \
     file://CVE-2025-61727.patch \
     file://CVE-2025-61729.patch \
+    file://CVE-2025-61730.patch \
 "
 SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
 
diff --git a/meta/recipes-devtools/go/go/CVE-2025-61730.patch b/meta/recipes-devtools/go/go/CVE-2025-61730.patch
new file mode 100644
index 0000000000..b7234e6bf2
--- /dev/null
+++ b/meta/recipes-devtools/go/go/CVE-2025-61730.patch
@@ -0,0 +1,460 @@ 
+From 2cfa797798cc982973d194eca3be19fb1f092556 Mon Sep 17 00:00:00 2001
+From: Roland Shoemaker <roland@golang.org>
+Date: Mon, 24 Nov 2025 14:03:10 -0800
+Subject: [PATCH] [release-branch.go1.24] crypto/tls: reject trailing messages
+ after client/server hello
+
+For TLS 1.3, after procesesing the server/client hello, if there isn't a
+CCS message, reject the trailing messages which were appended to the
+hello messages. This prevents an on-path attacker from injecting
+plaintext messages into the handshake.
+
+Additionally, check that we don't have any buffered messages before we
+switch the read traffic secret regardless, since any buffered messages
+would have been under an old key which is no longer appropriate.
+
+We also invert the ordering of setting the read/write secrets so that if
+we fail when changing the read secret we send the alert using the
+correct write secret.
+
+Updates #76443
+Fixes #76854
+Fixes CVE-2025-61730
+
+CVE: CVE-2025-61730
+Upstream-Status: Backport [https://github.com/golang/go/commit/ad2cd043db66]
+
+Backport Changes:
+- In version 1.24, the doHelloRetryRequest function defined in handshake_server_tls13.go
+  returns keyshare and error, but in version 1.22 it only returns error. The backport
+  was adjusted accordingly and These changes were introduced by commit
+  https://github.com/golang/go/commit/d0edd9acc80a in version 1.24.
+- In file src/crypto/tls/handshake_server_tls13.go, Replaced the function call
+  hs.handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript) with
+  hs.suite.deriveSecret(hs.handshakeSecret, clientHandshakeTrafficLabel, hs.transcript).
+  This change is not present in version v1.22 and it was introduced by commit
+  https://github.com/golang/go/commit/743746a3a52d in version 1.24.
+
+Change-Id: If6ba8ad16f48d5cd5db5574824062ad4244a5b52
+Reviewed-on: https://go-review.googlesource.com/c/go/+/724120
+LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
+Reviewed-by: Michael Knyszek <mknyszek@google.com>
+Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
+Reviewed-by: Coia Prant <coiaprant@gmail.com>
+(cherry picked from commit 5046bdf8a612b35a2c1a9e168054c1d5c65e7dd7)
+Reviewed-on: https://go-review.googlesource.com/c/go/+/731961
+Reviewed-by: Damien Neil <dneil@google.com>
+(cherry picked from commit ad2cd043db66cd36e1f55359638729d2c8ff3d99)
+Signed-off-by: Deepak Rathore <deeratho@cisco.com>
+---
+ src/crypto/tls/conn.go                   |  39 ++++++-
+ src/crypto/tls/handshake_client_tls13.go |  22 ++--
+ src/crypto/tls/handshake_server_tls13.go |  39 ++++---
+ src/crypto/tls/handshake_test.go         | 140 +++++++++++++++++++++++
+ src/crypto/tls/quic.go                   |  11 +-
+ 5 files changed, 219 insertions(+), 32 deletions(-)
+
+diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
+index 0e4669866e..08609ce17b 100644
+--- a/src/crypto/tls/conn.go
++++ b/src/crypto/tls/conn.go
+@@ -225,6 +225,9 @@ func (hc *halfConn) changeCipherSpec() error {
+	return nil
+ }
+
++// setTrafficSecret sets the traffic secret for the given encryption level. setTrafficSecret
++// should not be called directly, but rather through the Conn setWriteTrafficSecret and
++// setReadTrafficSecret wrapper methods.
+ func (hc *halfConn) setTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) {
+	hc.trafficSecret = secret
+	hc.level = level
+@@ -1321,9 +1324,6 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
+		return c.in.setErrorLocked(c.sendAlert(alertInternalError))
+	}
+
+-	newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
+-	c.in.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
+-
+	if keyUpdate.updateRequested {
+		c.out.Lock()
+		defer c.out.Unlock()
+@@ -1341,7 +1341,12 @@ func (c *Conn) handleKeyUpdate(keyUpdate *keyUpdateMsg) error {
+		}
+
+		newSecret := cipherSuite.nextTrafficSecret(c.out.trafficSecret)
+-		c.out.setTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
++		c.setWriteTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret)
++	}
++
++	newSecret := cipherSuite.nextTrafficSecret(c.in.trafficSecret)
++	if err := c.setReadTrafficSecret(cipherSuite, QUICEncryptionLevelInitial, newSecret); err != nil {
++		return err
+	}
+
+	return nil
+@@ -1572,7 +1577,9 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
+			// Provide the 1-RTT read secret now that the handshake is complete.
+			// The QUIC layer MUST NOT decrypt 1-RTT packets prior to completing
+			// the handshake (RFC 9001, Section 5.7).
+-			c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret)
++			if err := c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret); err != nil {
++				return err
++			}
+		} else {
+			var a alert
+			c.out.Lock()
+@@ -1664,3 +1671,25 @@ func (c *Conn) VerifyHostname(host string) error {
+	}
+	return c.peerCertificates[0].VerifyHostname(host)
+ }
++
++// setReadTrafficSecret sets the read traffic secret for the given encryption level. If
++// being called at the same time as setWriteTrafficSecret, the caller must ensure the call
++// to setWriteTrafficSecret happens first so any alerts are sent at the write level.
++func (c *Conn) setReadTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) error {
++	// Ensure that there are no buffered handshake messages before changing the
++	// read keys, since that can cause messages to be parsed that were encrypted
++	// using old keys which are no longer appropriate.
++	if c.hand.Len() != 0 {
++		c.sendAlert(alertUnexpectedMessage)
++		return errors.New("tls: handshake buffer not empty before setting read traffic secret")
++	}
++	c.in.setTrafficSecret(suite, level, secret)
++	return nil
++}
++
++// setWriteTrafficSecret sets the write traffic secret for the given encryption level. If
++// being called at the same time as setReadTrafficSecret, the caller must ensure the call
++// to setWriteTrafficSecret happens first so any alerts are sent at the write level.
++func (c *Conn) setWriteTrafficSecret(suite *cipherSuiteTLS13, level QUICEncryptionLevel, secret []byte) {
++	c.out.setTrafficSecret(suite, level, secret)
++}
+diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go
+index 2f59f6888c..68ff92beda 100644
+--- a/src/crypto/tls/handshake_client_tls13.go
++++ b/src/crypto/tls/handshake_client_tls13.go
+@@ -393,17 +393,18 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
+
+	clientSecret := hs.suite.deriveSecret(handshakeSecret,
+		clientHandshakeTrafficLabel, hs.transcript)
+-	c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
++	c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
+	serverSecret := hs.suite.deriveSecret(handshakeSecret,
+		serverHandshakeTrafficLabel, hs.transcript)
+-	c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
++	if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret); err != nil {
++		return err
++	}
+
+	if c.quic != nil {
+-		if c.hand.Len() != 0 {
+-			c.sendAlert(alertUnexpectedMessage)
+-		}
+		c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
+-		c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
++		if err := c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret); err != nil {
++			return err
++		}
+	}
+
+	err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
+@@ -606,7 +607,9 @@ func (hs *clientHandshakeStateTLS13) readServerFinished() error {
+		clientApplicationTrafficLabel, hs.transcript)
+	serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+		serverApplicationTrafficLabel, hs.transcript)
+-	c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
++	if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret); err != nil {
++		return err
++	}
+
+	err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
+	if err != nil {
+@@ -702,7 +705,7 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
+		return err
+	}
+
+-	c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
++	c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
+
+	if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
+		c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
+@@ -710,9 +713,6 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
+	}
+
+	if c.quic != nil {
+-		if c.hand.Len() != 0 {
+-			c.sendAlert(alertUnexpectedMessage)
+-		}
+		c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, hs.trafficSecret)
+	}
+
+diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
+index 21d798de37..5aa69e9640 100644
+--- a/src/crypto/tls/handshake_server_tls13.go
++++ b/src/crypto/tls/handshake_server_tls13.go
+@@ -380,7 +380,9 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error {
+				return err
+			}
+			earlyTrafficSecret := hs.suite.deriveSecret(hs.earlySecret, clientEarlyTrafficLabel, transcript)
+-			c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret)
++			if err := c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret); err != nil {
++				return err
++			}
+		}
+
+		c.didResume = true
+@@ -477,6 +479,14 @@ func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
+ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
+	c := hs.c
+
++	// Make sure the client didn't send extra handshake messages alongside
++	// their initial client_hello. If they sent two client_hello messages,
++	// we will consume the second before they respond to the server_hello.
++	if c.hand.Len() != 0 {
++		c.sendAlert(alertUnexpectedMessage)
++		return errors.New("tls: handshake buffer not empty before HelloRetryRequest")
++	}
++
+	// The first ClientHello gets double-hashed into the transcript upon a
+	// HelloRetryRequest. See RFC 8446, Section 4.4.1.
+	if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
+@@ -615,19 +625,20 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
+	hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
+		hs.suite.deriveSecret(earlySecret, "derived", nil))
+
+-	clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+-		clientHandshakeTrafficLabel, hs.transcript)
+-	c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
+	serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
+		serverHandshakeTrafficLabel, hs.transcript)
+-	c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
++	c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
++	clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
++		clientHandshakeTrafficLabel, hs.transcript)
++	if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret); err != nil {
++		return err
++	}
+
+	if c.quic != nil {
+-		if c.hand.Len() != 0 {
+-			c.sendAlert(alertUnexpectedMessage)
+-		}
+		c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
+-		c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
++		if err := c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret); err != nil {
++			return err
++		}
+	}
+
+	err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
+@@ -751,13 +762,9 @@ func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
+		clientApplicationTrafficLabel, hs.transcript)
+	serverSecret := hs.suite.deriveSecret(hs.masterSecret,
+		serverApplicationTrafficLabel, hs.transcript)
+-	c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
++	c.setWriteTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
+
+	if c.quic != nil {
+-		if c.hand.Len() != 0 {
+-			// TODO: Handle this in setTrafficSecret?
+-			c.sendAlert(alertUnexpectedMessage)
+-		}
+		c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, serverSecret)
+	}
+
+@@ -992,7 +999,9 @@ func (hs *serverHandshakeStateTLS13) readClientFinished() error {
+		return errors.New("tls: invalid client finished hash")
+	}
+
+-	c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
++	if err := c.setReadTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret); err != nil {
++		return err
++	}
+
+	return nil
+ }
+diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go
+index 27ab19ef31..4991a0e69b 100644
+--- a/src/crypto/tls/handshake_test.go
++++ b/src/crypto/tls/handshake_test.go
+@@ -6,6 +6,7 @@ package tls
+
+ import (
+	"bufio"
++	"context"
+	"crypto/ed25519"
+	"crypto/x509"
+	"encoding/hex"
+@@ -533,3 +534,142 @@ var clientEd25519KeyPEM = testingKey(`
+ -----BEGIN TESTING KEY-----
+ MC4CAQAwBQYDK2VwBCIEINifzf07d9qx3d44e0FSbV4mC/xQxT644RRbpgNpin7I
+ -----END TESTING KEY-----`)
++
++func TestServerHelloTrailingMessage(t *testing.T) {
++	// In TLS 1.3 the change cipher spec message is optional. If a CCS message
++	// is not sent, after reading the ServerHello, the read traffic secret is
++	// set, and all following messages must be encrypted. If the server sends
++	// additional unencrypted messages in a record with the ServerHello, the
++	// client must either fail or ignore the additional messages.
++
++	c, s := localPipe(t)
++	go func() {
++		ctx := context.Background()
++		srv := Server(s, testConfig)
++		clientHello, _, err := srv.readClientHello(ctx)
++		if err != nil {
++			testFatal(t, err)
++		}
++
++		hs := serverHandshakeStateTLS13{
++			c:           srv,
++			ctx:         ctx,
++			clientHello: clientHello,
++		}
++		if err := hs.processClientHello(); err != nil {
++			testFatal(t, err)
++		}
++		if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil {
++			testFatal(t, err)
++		}
++
++		record, err := concatHandshakeMessages(hs.hello, &encryptedExtensionsMsg{alpnProtocol: "h2"})
++		if err != nil {
++			testFatal(t, err)
++		}
++
++		if _, err := s.Write(record); err != nil {
++			testFatal(t, err)
++		}
++		srv.Close()
++	}()
++
++	cli := Client(c, testConfig)
++	expectedErr := "tls: handshake buffer not empty before setting read traffic secret"
++	if err := cli.Handshake(); err == nil {
++		t.Fatal("expected error from incomplete handshake, got nil")
++	} else if err.Error() != expectedErr {
++		t.Fatalf("expected error %q, got %q", expectedErr, err.Error())
++	}
++}
++
++func TestClientHelloTrailingMessage(t *testing.T) {
++	// Same as TestServerHelloTrailingMessage but for the client side.
++
++	c, s := localPipe(t)
++	go func() {
++		cli := Client(c, testConfig)
++
++		hello, _, _, err := cli.makeClientHello()
++		if err != nil {
++			testFatal(t, err)
++		}
++
++		record, err := concatHandshakeMessages(hello, &certificateMsgTLS13{})
++		if err != nil {
++			testFatal(t, err)
++		}
++
++		if _, err := c.Write(record); err != nil {
++			testFatal(t, err)
++		}
++		cli.Close()
++	}()
++
++	srv := Server(s, testConfig)
++	expectedErr := "tls: handshake buffer not empty before setting read traffic secret"
++	if err := srv.Handshake(); err == nil {
++		t.Fatal("expected error from incomplete handshake, got nil")
++	} else if err.Error() != expectedErr {
++		t.Fatalf("expected error %q, got %q", expectedErr, err.Error())
++	}
++}
++
++func TestDoubleClientHelloHRR(t *testing.T) {
++	// If a client sends two ClientHello messages in a single record, and the
++	// server sends a HRR after reading the first ClientHello, the server must
++	// either fail or ignore the trailing ClientHello.
++
++	c, s := localPipe(t)
++
++	go func() {
++		cli := Client(c, testConfig)
++
++		hello, _, _, err := cli.makeClientHello()
++		if err != nil {
++			testFatal(t, err)
++		}
++		hello.keyShares = nil
++
++		record, err := concatHandshakeMessages(hello, hello)
++		if err != nil {
++			testFatal(t, err)
++		}
++
++		if _, err := c.Write(record); err != nil {
++			testFatal(t, err)
++		}
++		cli.Close()
++	}()
++
++	srv := Server(s, testConfig)
++	expectedErr := "tls: handshake buffer not empty before HelloRetryRequest"
++	if err := srv.Handshake(); err == nil {
++		t.Fatal("expected error from incomplete handshake, got nil")
++	} else if err.Error() != expectedErr {
++		t.Fatalf("expected error %q, got %q", expectedErr, err.Error())
++	}
++}
++
++// concatHandshakeMessages marshals and concatenates the given handshake
++// messages into a single record.
++func concatHandshakeMessages(msgs ...handshakeMessage) ([]byte, error) {
++	var marshalled []byte
++	for _, msg := range msgs {
++		data, err := msg.marshal()
++		if err != nil {
++			return nil, err
++		}
++		marshalled = append(marshalled, data...)
++	}
++	m := len(marshalled)
++	outBuf := make([]byte, recordHeaderLen)
++	outBuf[0] = byte(recordTypeHandshake)
++	vers := VersionTLS12
++	outBuf[1] = byte(vers >> 8)
++	outBuf[2] = byte(vers)
++	outBuf[3] = byte(m >> 8)
++	outBuf[4] = byte(m)
++	outBuf = append(outBuf, marshalled...)
++	return outBuf, nil
++}
+diff --git a/src/crypto/tls/quic.go b/src/crypto/tls/quic.go
+index 3518169bf7..aa14f1dadb 100644
+--- a/src/crypto/tls/quic.go
++++ b/src/crypto/tls/quic.go
+@@ -323,13 +323,22 @@ func (c *Conn) quicReadHandshakeBytes(n int) error {
+	return nil
+ }
+
+-func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
++func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) error {
++	// Ensure that there are no buffered handshake messages before changing the
++	// read keys, since that can cause messages to be parsed that were encrypted
++	// using old keys which are no longer appropriate.
++	// TODO(roland): we should merge this check with the similar one in setReadTrafficSecret.
++	if c.hand.Len() != 0 {
++		c.sendAlert(alertUnexpectedMessage)
++		return errors.New("tls: handshake buffer not empty before setting read traffic secret")
++	}
+	c.quic.events = append(c.quic.events, QUICEvent{
+		Kind:  QUICSetReadSecret,
+		Level: level,
+		Suite: suite,
+		Data:  secret,
+	})
++	return nil
+ }
+
+ func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
+--
+2.35.6