[go] crypto/tls: omit PSK in ECH outer client hello

8 views
Skip to first unread message

Roland Shoemaker (Gerrit)

unread,
May 8, 2026, 12:43:16 PMMay 8
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Roland Shoemaker has uploaded the change for review

Commit message

crypto/tls: omit PSK in ECH outer client hello

When using ECH, do not include the PSK extension in the outer hello.
Including the PSK extension allows for a degradation in privacy, as an
on-path attacker can harvest outer client hellos, and then construct new
hellos using the PSK extension and arbitrary guessed SNI values,
replaying them to the target server. If the server rejects the PSK, the
handshake will continue, but if the PSK is accepted, the binder check
will fail.

Thanks to Coia Prant (github.com/rbqvq) for
reporting this issue.

Fixes CVE-2026-42505
Fixes #79282
Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18

Change diff

diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go
index aa0b7db..511e073 100644
--- a/src/crypto/tls/handshake_messages.go
+++ b/src/crypto/tls/handshake_messages.go
@@ -5,6 +5,7 @@
package tls

import (
+ "bytes"
"errors"
"fmt"
"slices"
@@ -317,7 +318,8 @@
})
})
}
- if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension
+ // pre_shared_key must be the last extension
+ if len(m.pskIdentities) > 0 && (echInner || len(m.encryptedClientHello) == 0 || bytes.Equal(m.encryptedClientHello, []byte{byte(innerECHExt)})) {
// RFC 8446, Section 4.2.11
exts.AddUint16(extensionPreSharedKey)
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
diff --git a/src/crypto/tls/handshake_messages_test.go b/src/crypto/tls/handshake_messages_test.go
index fa81a72..6e6f503 100644
--- a/src/crypto/tls/handshake_messages_test.go
+++ b/src/crypto/tls/handshake_messages_test.go
@@ -215,7 +215,16 @@
case 2:
m.pskModes = []uint8{pskModeDHE, pskModePlain}
}
- for i := 0; i < rand.Intn(5); i++ {
+ // clientHelloMsg.marshal uses echInner == false. If m.encryptedClientHello > 0 and
+ // does not equal []byte{innerECHExt}, then the psk extension will be omitted,
+ // so either only emit empty encryptedClientHello and psk, encryptedClientHello with
+ // []byte{innerECHExt} and psk, or random encryptedClientHello and no psk.
+ if rand.Intn(10) > 5 {
+ m.encryptedClientHello = randomBytes(rand.Intn(50)+1, rand)
+ } else {
+ if rand.Intn(10) > 5 {
+ m.encryptedClientHello = []byte{byte(innerECHExt)}
+ }
var psk pskIdentity
psk.obfuscatedTicketAge = uint32(rand.Intn(500000))
psk.label = randomBytes(rand.Intn(500)+1, rand)
@@ -228,9 +237,6 @@
if rand.Intn(10) > 5 {
m.earlyData = true
}
- if rand.Intn(10) > 5 {
- m.encryptedClientHello = randomBytes(rand.Intn(50)+1, rand)
- }

return reflect.ValueOf(m)
}
diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go
index e2b1aa0..d076d31 100644
--- a/src/crypto/tls/tls_test.go
+++ b/src/crypto/tls/tls_test.go
@@ -2379,6 +2379,7 @@
clientConfig.RootCAs.AddCert(secretCert)
clientConfig.RootCAs.AddCert(publicCert)
clientConfig.EncryptedClientHelloConfigList = echConfigList
+ clientConfig.ClientSessionCache = NewLRUClientSessionCache(2)
serverConfig.InsecureSkipVerify = false
serverConfig.Rand = rand.Reader
serverConfig.Time = nil

Change information

Files:
  • M src/crypto/tls/handshake_messages.go
  • M src/crypto/tls/handshake_messages_test.go
  • M src/crypto/tls/tls_test.go
Change size: S
Delta: 3 files changed, 14 insertions(+), 5 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
Gerrit-Change-Number: 775960
Gerrit-PatchSet: 1
Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
unsatisfied_requirement
satisfied_requirement
open
diffy

Roland Shoemaker (Gerrit)

unread,
May 8, 2026, 12:43:31 PMMay 8
to goph...@pubsubhelper.golang.org, Daniel McCarney, golang-co...@googlegroups.com
Attention needed from Daniel McCarney

Roland Shoemaker voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention is currently required from:
  • Daniel McCarney
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: go
Gerrit-Branch: master
Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
Gerrit-Change-Number: 775960
Gerrit-PatchSet: 1
Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
Gerrit-Attention: Daniel McCarney <dan...@binaryparadox.net>
Gerrit-Comment-Date: Fri, 08 May 2026 16:43:27 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Daniel McCarney (Gerrit)

unread,
May 10, 2026, 10:50:52 AMMay 10
to Roland Shoemaker, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
Attention needed from Roland Shoemaker

Daniel McCarney added 1 comment

Patchset-level comments
File-level comment, Patchset 1 (Latest):
Daniel McCarney . resolved

Did you consider using a GREASE'd PSK in the outer hello instead of removing it outright? E.g. the approach described in [RFC 9849 §6.1.2](https://www.rfc-editor.org/rfc/rfc9849.html#name-grease-psk). That's what we did in [rustls](https://github.com/rustls/rustls/blob/b44c09fbca5172b3f5e5ed6ba2ffe6fcd934e07a/rustls/src/client/ech.rs#L730-L760).

§6.1.1 also says "This is possible because the ClientHelloOuter's "pre_shared_key" extension is either omitted or uses a random binder" so I think omitting it entirely isn't _wrong_, but I also wonder if we should favor the GREASE'd approach instead (or at least mention why it wasn't used) since it does seem to blend in better at face value.

Separately, AFAICT there's no test coverage for the specific fix. Reverting `handshake_messages.go` and then re-running the `/crypto/tls/...` test suite still shows everything passing. I think it would be nice to take a crack at implementing a test that would fail without the relevant fix. Do you think that's workable?

The meat of the `handshake_messages.go` diff seems reasonable to me with the two topics above put to the side 👍

Open in Gerrit

Related details

Attention is currently required from:
  • Roland Shoemaker
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: go
    Gerrit-Branch: master
    Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
    Gerrit-Change-Number: 775960
    Gerrit-PatchSet: 1
    Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
    Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
    Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
    Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
    Gerrit-Comment-Date: Sun, 10 May 2026 14:50:45 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    satisfied_requirement
    open
    diffy

    Roland Shoemaker (Gerrit)

    unread,
    Jun 25, 2026, 1:47:59 PM (2 days ago) Jun 25
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
    Attention needed from Daniel McCarney

    Roland Shoemaker uploaded new patchset

    Roland Shoemaker uploaded patch set #2 to this change.
    Following approvals got outdated and were removed:
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Daniel McCarney
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: newpatchset
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
      Gerrit-Change-Number: 775960
      Gerrit-PatchSet: 2
      Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
      Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
      Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
      Gerrit-Attention: Daniel McCarney <dan...@binaryparadox.net>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Roland Shoemaker (Gerrit)

      unread,
      Jun 25, 2026, 1:47:59 PM (2 days ago) Jun 25
      to goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, Daniel McCarney, golang-co...@googlegroups.com
      Attention needed from Daniel McCarney

      Roland Shoemaker added 1 comment

      Patchset-level comments
      Daniel McCarney . resolved

      Did you consider using a GREASE'd PSK in the outer hello instead of removing it outright? E.g. the approach described in [RFC 9849 §6.1.2](https://www.rfc-editor.org/rfc/rfc9849.html#name-grease-psk). That's what we did in [rustls](https://github.com/rustls/rustls/blob/b44c09fbca5172b3f5e5ed6ba2ffe6fcd934e07a/rustls/src/client/ech.rs#L730-L760).

      §6.1.1 also says "This is possible because the ClientHelloOuter's "pre_shared_key" extension is either omitted or uses a random binder" so I think omitting it entirely isn't _wrong_, but I also wonder if we should favor the GREASE'd approach instead (or at least mention why it wasn't used) since it does seem to blend in better at face value.

      Separately, AFAICT there's no test coverage for the specific fix. Reverting `handshake_messages.go` and then re-running the `/crypto/tls/...` test suite still shows everything passing. I think it would be nice to take a crack at implementing a test that would fail without the relevant fix. Do you think that's workable?

      The meat of the `handshake_messages.go` diff seems reasonable to me with the two topics above put to the side 👍

      Roland Shoemaker

      Discussed out of band last week (I think?). In short, we don't GREASE PSK because it doesn't seem particularly valuable, and is mainly another "make middlebox happy" measure. BoringSSL matches our behavior here as far as I know.

      We could revisit that decision, but if so it should be a separate change.

      Added a test to make sure the outer PSKs are removed.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Daniel McCarney
      Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
      Gerrit-Change-Number: 775960
      Gerrit-PatchSet: 2
      Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
      Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
      Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
      Gerrit-Attention: Daniel McCarney <dan...@binaryparadox.net>
      Gerrit-Comment-Date: Thu, 25 Jun 2026 17:47:52 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: Daniel McCarney <dan...@binaryparadox.net>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Daniel McCarney (Gerrit)

      unread,
      Jun 25, 2026, 2:33:43 PM (2 days ago) Jun 25
      to Roland Shoemaker, goph...@pubsubhelper.golang.org, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
      Attention needed from Roland Shoemaker

      Daniel McCarney voted and added 1 comment

      Votes added by Daniel McCarney

      Code-Review+2

      1 comment

      Patchset-level comments
      Daniel McCarney . resolved

      Did you consider using a GREASE'd PSK in the outer hello instead of removing it outright? E.g. the approach described in [RFC 9849 §6.1.2](https://www.rfc-editor.org/rfc/rfc9849.html#name-grease-psk). That's what we did in [rustls](https://github.com/rustls/rustls/blob/b44c09fbca5172b3f5e5ed6ba2ffe6fcd934e07a/rustls/src/client/ech.rs#L730-L760).

      §6.1.1 also says "This is possible because the ClientHelloOuter's "pre_shared_key" extension is either omitted or uses a random binder" so I think omitting it entirely isn't _wrong_, but I also wonder if we should favor the GREASE'd approach instead (or at least mention why it wasn't used) since it does seem to blend in better at face value.

      Separately, AFAICT there's no test coverage for the specific fix. Reverting `handshake_messages.go` and then re-running the `/crypto/tls/...` test suite still shows everything passing. I think it would be nice to take a crack at implementing a test that would fail without the relevant fix. Do you think that's workable?

      The meat of the `handshake_messages.go` diff seems reasonable to me with the two topics above put to the side 👍

      Roland Shoemaker

      Discussed out of band last week (I think?). In short, we don't GREASE PSK because it doesn't seem particularly valuable, and is mainly another "make middlebox happy" measure. BoringSSL matches our behavior here as far as I know.

      We could revisit that decision, but if so it should be a separate change.

      Added a test to make sure the outer PSKs are removed.

      Daniel McCarney

      Discussed out of band last week (I think?).

      Yah, we did 👍

      SGTM.

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Roland Shoemaker
      Submit Requirements:
      • requirement satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: go
      Gerrit-Branch: master
      Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
      Gerrit-Change-Number: 775960
      Gerrit-PatchSet: 2
      Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
      Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
      Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
      Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
      Gerrit-Comment-Date: Thu, 25 Jun 2026 18:33:38 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      Comment-In-Reply-To: Roland Shoemaker <rol...@golang.org>
      Comment-In-Reply-To: Daniel McCarney <dan...@binaryparadox.net>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Roland Shoemaker (Gerrit)

      unread,
      Jun 25, 2026, 2:36:18 PM (2 days ago) Jun 25
      to goph...@pubsubhelper.golang.org, Daniel McCarney, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com

      Roland Shoemaker voted and added 1 comment

      Votes added by Roland Shoemaker

      Auto-Submit+1
      TryBot-Bypass+1

      1 comment

      Patchset-level comments
      File-level comment, Patchset 2 (Latest):
      Roland Shoemaker . resolved

      Unrelated LUCI failures.

      Open in Gerrit

      Related details

      Attention set is empty
      Submit Requirements:
        • requirement satisfiedCode-Review
        • requirement satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: go
        Gerrit-Branch: master
        Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
        Gerrit-Change-Number: 775960
        Gerrit-PatchSet: 2
        Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
        Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
        Gerrit-Reviewer: Roland Shoemaker <rol...@golang.org>
        Gerrit-Comment-Date: Thu, 25 Jun 2026 18:36:12 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Carlos Amedee (Gerrit)

        unread,
        Jun 26, 2026, 12:06:46 PM (19 hours ago) Jun 26
        to Roland Shoemaker, goph...@pubsubhelper.golang.org, Daniel McCarney, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com
        Attention needed from Roland Shoemaker

        Carlos Amedee voted Code-Review+1

        Code-Review+1
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Roland Shoemaker
        Submit Requirements:
          • requirement satisfiedCode-Review
          • requirement satisfiedNo-Unresolved-Comments
          • requirement satisfiedReview-Enforcement
          • requirement satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: comment
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
          Gerrit-Change-Number: 775960
          Gerrit-PatchSet: 2
          Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
          Gerrit-Reviewer: Carlos Amedee <car...@golang.org>
          Gerrit-Attention: Roland Shoemaker <rol...@golang.org>
          Gerrit-Comment-Date: Fri, 26 Jun 2026 16:06:41 +0000
          Gerrit-HasComments: No
          Gerrit-Has-Labels: Yes
          satisfied_requirement
          open
          diffy

          Gopher Robot (Gerrit)

          unread,
          Jun 26, 2026, 12:07:07 PM (19 hours ago) Jun 26
          to Roland Shoemaker, goph...@pubsubhelper.golang.org, golang-...@googlegroups.com, Carlos Amedee, Daniel McCarney, golang...@luci-project-accounts.iam.gserviceaccount.com, golang-co...@googlegroups.com

          Gopher Robot submitted the change

          Change information

          Commit message:
          crypto/tls: omit PSK in ECH outer client hello

          When using ECH, do not include the PSK extension in the outer hello.
          Including the PSK extension allows for a degradation in privacy, as an
          on-path attacker can harvest outer client hellos, and then construct new
          hellos using the PSK extension and arbitrary guessed SNI values,
          replaying them to the target server. If the server rejects the PSK, the
          handshake will continue, but if the PSK is accepted, the binder check
          will fail.

          Thanks to Coia Prant (github.com/rbqvq) for
          reporting this issue.

          Fixes CVE-2026-42505
          Fixes #79282
          Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
          Auto-Submit: Roland Shoemaker <rol...@golang.org>
          TryBot-Bypass: Roland Shoemaker <rol...@golang.org>
          Reviewed-by: Daniel McCarney <dan...@binaryparadox.net>
          Reviewed-by: Carlos Amedee <car...@golang.org>
          Files:
          • M src/crypto/tls/handshake_messages.go
          • M src/crypto/tls/handshake_messages_test.go
          • M src/crypto/tls/tls_test.go
          Change size: M
          Delta: 3 files changed, 99 insertions(+), 5 deletions(-)
          Branch: refs/heads/master
          Submit Requirements:
          Open in Gerrit
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: merged
          Gerrit-Project: go
          Gerrit-Branch: master
          Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
          Gerrit-Change-Number: 775960
          Gerrit-PatchSet: 3
          Gerrit-Owner: Roland Shoemaker <rol...@golang.org>
          Gerrit-Reviewer: Carlos Amedee <car...@golang.org>
          Gerrit-Reviewer: Daniel McCarney <dan...@binaryparadox.net>
          Gerrit-Reviewer: Gopher Robot <go...@golang.org>
          open
          diffy
          satisfied_requirement

          Roland Shoemaker (Gerrit)

          unread,
          Jun 26, 2026, 4:28:44 PM (14 hours ago) Jun 26
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

          Roland Shoemaker has uploaded the change for review

          Commit message

          [release-branch.go1.25] crypto/tls: omit PSK in ECH outer client hello


          When using ECH, do not include the PSK extension in the outer hello.
          Including the PSK extension allows for a degradation in privacy, as an
          on-path attacker can harvest outer client hellos, and then construct new
          hellos using the PSK extension and arbitrary guessed SNI values,
          replaying them to the target server. If the server rejects the PSK, the
          handshake will continue, but if the PSK is accepted, the binder check
          will fail.

          Thanks to Coia Prant (github.com/rbqvq) for
          reporting this issue.

          Fixes CVE-2026-42505
          Updates #79282
          Fixes #80174


          Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
          Reviewed-on: https://go-review.googlesource.com/c/go/+/775960
          Auto-Submit: Roland Shoemaker <rol...@golang.org>
          TryBot-Bypass: Roland Shoemaker <rol...@golang.org>
          Reviewed-by: Daniel McCarney <dan...@binaryparadox.net>
          Reviewed-by: Carlos Amedee <car...@golang.org>
          (cherry picked from commit 137b8065ab5b485bbde0ed430dd89841c0602bb2)

          Change diff

          diff --git a/src/crypto/tls/handshake_messages.go b/src/crypto/tls/handshake_messages.go
          index aa0b7db..511e073 100644
          --- a/src/crypto/tls/handshake_messages.go
          +++ b/src/crypto/tls/handshake_messages.go
          @@ -5,6 +5,7 @@
          package tls

          import (
          + "bytes"
          "errors"
          "fmt"
          "slices"
          @@ -317,7 +318,8 @@
          })
          })
          }
          - if len(m.pskIdentities) > 0 { // pre_shared_key must be the last extension
          + // pre_shared_key must be the last extension
          + if len(m.pskIdentities) > 0 && (echInner || len(m.encryptedClientHello) == 0 || bytes.Equal(m.encryptedClientHello, []byte{byte(innerECHExt)})) {
          // RFC 8446, Section 4.2.11
          exts.AddUint16(extensionPreSharedKey)
          exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
          diff --git a/src/crypto/tls/handshake_messages_test.go b/src/crypto/tls/handshake_messages_test.go
          index fa81a72..80a7e76 100644
          @@ -587,3 +593,88 @@
          t.Fatal("Unmarshaled ServerHello with duplicate extensions")
          }
          }
          +
          +func TestECHRemoveOuterPSK(t *testing.T) {
          + r := rand.New(rand.NewSource(0))
          +
          + for _, tc := range []struct {
          + name string
          + echInner bool
          + echExt []byte
          + expectRemoved bool
          + }{
          + {
          + name: "echInner true",
          + echInner: true,
          + expectRemoved: false,
          + },
          + {
          + name: "echInner true, no ech ext",
          + echInner: true,
          + expectRemoved: false,
          + },
          + {
          + name: "echInner true, ech ext present",
          + echInner: true,
          + echExt: []byte{254},
          + expectRemoved: false,
          + },
          + {
          + name: "echInner true, ech ext present, inner ech sentinel",
          + echInner: true,
          + echExt: []byte{byte(innerECHExt)},
          + expectRemoved: false,
          + },
          + {
          + name: "echInner false, no ech ext",
          + echInner: false,
          + expectRemoved: false,
          + },
          + {
          + name: "echInner false, ech ext present",
          + echInner: false,
          + echExt: []byte{254},
          + expectRemoved: true,
          + },
          + {
          + name: "echInner false, ech ext present, inner ech sentinel",
          + echInner: false,
          + echExt: []byte{byte(innerECHExt)},
          + expectRemoved: false,
          + },
          + } {
          + t.Run(tc.name, func(t *testing.T) {
          + ch := (&clientHelloMsg{}).Generate(r, 0).Interface().(*clientHelloMsg)
          +
          + ch.pskBinders = [][]byte{[]byte("test")}
          + ch.pskIdentities = []pskIdentity{{label: []byte("test")}}
          + ch.encryptedClientHello = tc.echExt
          +
          + b, err := ch.marshalMsg(tc.echInner)
          + if err != nil {
          + t.Fatal(err)
          + }
          + var rch clientHelloMsg
          + if !rch.unmarshal(b) {
          + t.Fatal("Failed to unmarshal ClientHello")
          + }
          +
          + if tc.expectRemoved {
          + if rch.pskIdentities != nil {
          + t.Error("expected PSK identities to be removed")
          + }
          + if rch.pskBinders != nil {
          + t.Error("expected PSK binders to be removed")
          + }
          + } else {
          + if rch.pskIdentities == nil {
          + t.Error("expected PSK identities to be present")
          + }
          + if rch.pskBinders == nil {
          + t.Error("expected PSK binders to be present")
          + }
          + }
          + })
          + }
          +
          +}
          diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go
          index 40817a2..7ee34a2 100644
          --- a/src/crypto/tls/tls_test.go
          +++ b/src/crypto/tls/tls_test.go
          @@ -2328,6 +2328,7 @@

          clientConfig.RootCAs.AddCert(secretCert)
          clientConfig.RootCAs.AddCert(publicCert)
          clientConfig.EncryptedClientHelloConfigList = echConfigList
          + clientConfig.ClientSessionCache = NewLRUClientSessionCache(2)
          serverConfig.InsecureSkipVerify = false
          serverConfig.Rand = rand.Reader
          serverConfig.Time = nil

          Change information

          Files:
          • M src/crypto/tls/handshake_messages.go
          • M src/crypto/tls/handshake_messages_test.go
          • M src/crypto/tls/tls_test.go
          Change size: M
          Delta: 3 files changed, 99 insertions(+), 5 deletions(-)
          Open in Gerrit

          Related details

          Attention set is empty
          Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newchange
          Gerrit-Project: go
          Gerrit-Branch: release-branch.go1.25
          Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
          Gerrit-Change-Number: 794920
          unsatisfied_requirement
          satisfied_requirement
          open
          diffy

          Roland Shoemaker (Gerrit)

          unread,
          Jun 26, 2026, 4:29:21 PM (14 hours ago) Jun 26
          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

          Roland Shoemaker has uploaded the change for review

          Commit message

          [release-branch.go1.26] crypto/tls: omit PSK in ECH outer client hello


          When using ECH, do not include the PSK extension in the outer hello.
          Including the PSK extension allows for a degradation in privacy, as an
          on-path attacker can harvest outer client hellos, and then construct new
          hellos using the PSK extension and arbitrary guessed SNI values,
          replaying them to the target server. If the server rejects the PSK, the
          handshake will continue, but if the PSK is accepted, the binder check
          will fail.

          Thanks to Coia Prant (github.com/rbqvq) for
          reporting this issue.

          Fixes CVE-2026-42505
          Updates #79282
          Fixes #80175

          Change diff

          index 8632239..655f58b 100644
          --- a/src/crypto/tls/tls_test.go
          +++ b/src/crypto/tls/tls_test.go
          @@ -2379,6 +2379,7 @@
          Gerrit-Branch: release-branch.go1.26
          Gerrit-Change-Id: Ib3a3c948106a57c1b07b9e61a58cbf757848be18
          Gerrit-Change-Number: 794921
          unsatisfied_requirement
          satisfied_requirement
          open
          diffy
          Reply all
          Reply to author
          Forward
          0 new messages