--- /dev/null
+pkg crypto/tls, type ConnectionState struct, HelloRetryRequest bool #74425
+pkg crypto/tls, type ClientHelloInfo struct, HelloRetryRequest bool #74425
--- /dev/null
+The new [ClientHelloInfo.HelloRetryRequest] field indicates if the ClientHello
+was sent in response to a HelloRetryRequest message. The new
+[ConnectionState.HelloRetryRequest] field indicates if the server
+sent a HelloRetryRequest, or if the client received a HelloRetryRequest,
+depending on connection role.
log.Fatal("did not expect ECH, but it was accepted")
}
- if *expectHRR && !cs.testingOnlyDidHRR {
+ if *expectHRR && !cs.HelloRetryRequest {
log.Fatal("expected HRR but did not do it")
}
- if *expectNoHRR && cs.testingOnlyDidHRR {
+ if *expectNoHRR && cs.HelloRetryRequest {
log.Fatal("expected no HRR but did do it")
}
// client side.
ECHAccepted bool
+ // HelloRetryRequest indicates whether we sent a HelloRetryRequest if we
+ // are a server, or if we received a HelloRetryRequest if we are a client.
+ HelloRetryRequest bool
+
// ekm is a closure exposed via ExportKeyingMaterial.
ekm func(label string, context []byte, length int) ([]byte, error)
- // testingOnlyDidHRR is true if a HelloRetryRequest was sent/received.
- testingOnlyDidHRR bool
-
// testingOnlyPeerSignatureAlgorithm is the signature algorithm used by the
// peer to sign the handshake. It is not set for resumed connections.
testingOnlyPeerSignatureAlgorithm SignatureScheme
// connection to fail.
Conn net.Conn
+ // HelloRetryRequest indicates whether the ClientHello was sent in response
+ // to a HelloRetryRequest message.
+ HelloRetryRequest bool
+
// config is embedded by the GetCertificate or GetConfigForClient caller,
// for use with SupportsCertificate.
config *Config
state.Version = c.vers
state.NegotiatedProtocol = c.clientProtocol
state.DidResume = c.didResume
- state.testingOnlyDidHRR = c.didHRR
+ state.HelloRetryRequest = c.didHRR
state.testingOnlyPeerSignatureAlgorithm = c.peerSigAlg
state.CurveID = c.curveID
state.NegotiatedProtocolIsMutual = true
args: []string{"-cipher", "ECDHE-RSA-AES128-GCM-SHA256", "-curves", "P-256"},
config: config,
validate: func(cs ConnectionState) error {
- if !cs.testingOnlyDidHRR {
+ if !cs.HelloRetryRequest {
return errors.New("expected HelloRetryRequest")
}
return nil
SupportedVersions: supportedVersions,
Extensions: clientHello.extensions,
Conn: c.conn,
+ HelloRetryRequest: c.didHRR,
config: c.config,
ctx: ctx,
}
config := testConfig.Clone()
config.CurvePreferences = []CurveID{CurveP256}
+ var clientHelloInfoHRR bool
+ var getCertificateCalled bool
+ eeCert := config.Certificates[0]
+ config.Certificates = nil
+ config.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
+ getCertificateCalled = true
+ clientHelloInfoHRR = clientHello.HelloRetryRequest
+ return &eeCert, nil
+ }
+
test := &serverTest{
name: "HelloRetryRequest",
command: []string{"openssl", "s_client", "-no_ticket", "-ciphersuites", "TLS_CHACHA20_POLY1305_SHA256", "-curves", "X25519:P-256"},
config: config,
validate: func(cs ConnectionState) error {
- if !cs.testingOnlyDidHRR {
+ if !cs.HelloRetryRequest {
return errors.New("expected HelloRetryRequest")
}
+ if !getCertificateCalled {
+ return errors.New("expected GetCertificate to be called")
+ }
+ if !clientHelloInfoHRR {
+ return errors.New("expected ClientHelloInfo.HelloRetryRequest to be true")
+ }
return nil
},
}
}
// TestHandshakeServerKeySharePreference checks that we prefer a key share even
-// if it's later in the CurvePreferences order.
+// if it's later in the CurvePreferences order, and that the client hello HRR
+// field is correctly represented.
func TestHandshakeServerKeySharePreference(t *testing.T) {
config := testConfig.Clone()
config.CurvePreferences = []CurveID{X25519, CurveP256}
+ // We also use this test as a convenient place to assert the ClientHelloInfo
+ // HelloRetryRequest field is _not_ set for a non-HRR hello.
+ var clientHelloInfoHRR bool
+ var getCertificateCalled bool
+ eeCert := config.Certificates[0]
+ config.Certificates = nil
+ config.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) {
+ getCertificateCalled = true
+ clientHelloInfoHRR = clientHello.HelloRetryRequest
+ return &eeCert, nil
+ }
+
test := &serverTest{
name: "KeySharePreference",
command: []string{"openssl", "s_client", "-no_ticket", "-ciphersuites", "TLS_CHACHA20_POLY1305_SHA256", "-curves", "P-256:X25519"},
config: config,
validate: func(cs ConnectionState) error {
- if cs.testingOnlyDidHRR {
+ if cs.HelloRetryRequest {
return errors.New("unexpected HelloRetryRequest")
}
+ if !getCertificateCalled {
+ return errors.New("expected GetCertificate to be called")
+ }
+ if clientHelloInfoHRR {
+ return errors.New("expected ClientHelloInfo.HelloRetryRequest to be false")
+ }
return nil
},
}
}
}
if test.expectHRR {
- if !ss.testingOnlyDidHRR {
+ if !ss.HelloRetryRequest {
t.Error("server did not use HRR")
}
- if !cs.testingOnlyDidHRR {
+ if !cs.HelloRetryRequest {
t.Error("client did not use HRR")
}
} else {
- if ss.testingOnlyDidHRR {
+ if ss.HelloRetryRequest {
t.Error("server used HRR")
}
- if cs.testingOnlyDidHRR {
+ if cs.HelloRetryRequest {
t.Error("client used HRR")
}
}