From: Deepak Rathore <[email protected]> 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 <[email protected]> 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 <[email protected]> +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 <[email protected]> +Reviewed-by: Michael Knyszek <[email protected]> +Reviewed-by: Daniel McCarney <[email protected]> +Reviewed-by: Coia Prant <[email protected]> +(cherry picked from commit 5046bdf8a612b35a2c1a9e168054c1d5c65e7dd7) +Reviewed-on: https://go-review.googlesource.com/c/go/+/731961 +Reviewed-by: Damien Neil <[email protected]> +(cherry picked from commit ad2cd043db66cd36e1f55359638729d2c8ff3d99) +Signed-off-by: Deepak Rathore <[email protected]> +--- + 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 -- 2.35.6
-=-=-=-=-=-=-=-=-=-=-=- Links: You receive all messages sent to this group. View/Reply Online (#231005): https://lists.openembedded.org/g/openembedded-core/message/231005 Mute This Topic: https://lists.openembedded.org/mt/117770158/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
