0
0
mirror of https://github.com/XTLS/REALITY.git synced 2025-08-22 14:38:35 +00:00

crypto/tls: add GetEncryptedClientHelloKeys

This allows servers to rotate their ECH keys without needing to restart
the server.

Fixes #71920

Change-Id: I55591ab3303d5fde639038541c50edcf1fafc9aa
Reviewed-on: https://go-review.googlesource.com/c/go/+/670655
TryBot-Bypass: Roland Shoemaker <roland@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Roland Shoemaker <roland@golang.org>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
This commit is contained in:
yuhan6665 2025-05-25 15:32:29 -04:00
parent 3682e614bc
commit 1b86b6ea09
4 changed files with 40 additions and 6 deletions

View File

@ -851,6 +851,20 @@ type Config struct {
// when ECH is rejected, even if set, and InsecureSkipVerify is ignored. // when ECH is rejected, even if set, and InsecureSkipVerify is ignored.
EncryptedClientHelloRejectionVerify func(ConnectionState) error EncryptedClientHelloRejectionVerify func(ConnectionState) error
// GetEncryptedClientHelloKeys, if not nil, is called when by a server when
// a client attempts ECH.
//
// If GetEncryptedClientHelloKeys is not nil, [EncryptedClientHelloKeys] is
// ignored.
//
// If GetEncryptedClientHelloKeys returns an error, the handshake will be
// aborted and the error will be returned. Otherwise,
// GetEncryptedClientHelloKeys must return a non-nil slice of
// [EncryptedClientHelloKey] that represents the acceptable ECH keys.
//
// For further details, see [EncryptedClientHelloKeys].
GetEncryptedClientHelloKeys func(*ClientHelloInfo) ([]EncryptedClientHelloKey, error)
// EncryptedClientHelloKeys are the ECH keys to use when a client // EncryptedClientHelloKeys are the ECH keys to use when a client
// attempts ECH. // attempts ECH.
// //
@ -861,6 +875,9 @@ type Config struct {
// will send a list of configs to retry based on the set of // will send a list of configs to retry based on the set of
// EncryptedClientHelloKeys which have the SendAsRetry field set. // EncryptedClientHelloKeys which have the SendAsRetry field set.
// //
// If GetEncryptedClientHelloKeys is non-nil, EncryptedClientHelloKeys is
// ignored.
//
// On the client side, this field is ignored. In order to configure ECH for // On the client side, this field is ignored. In order to configure ECH for
// clients, see the EncryptedClientHelloConfigList field. // clients, see the EncryptedClientHelloConfigList field.
EncryptedClientHelloKeys []EncryptedClientHelloKey EncryptedClientHelloKeys []EncryptedClientHelloKey
@ -961,6 +978,7 @@ func (c *Config) Clone() *Config {
GetCertificate: c.GetCertificate, GetCertificate: c.GetCertificate,
GetClientCertificate: c.GetClientCertificate, GetClientCertificate: c.GetClientCertificate,
GetConfigForClient: c.GetConfigForClient, GetConfigForClient: c.GetConfigForClient,
GetEncryptedClientHelloKeys: c.GetEncryptedClientHelloKeys,
VerifyPeerCertificate: c.VerifyPeerCertificate, VerifyPeerCertificate: c.VerifyPeerCertificate,
VerifyConnection: c.VerifyConnection, VerifyConnection: c.VerifyConnection,
RootCAs: c.RootCAs, RootCAs: c.RootCAs,

6
ech.go
View File

@ -579,7 +579,7 @@ func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([
return builder.Bytes() return builder.Bytes()
} }
func (c *Conn) processECHClientHello(outer *clientHelloMsg) (*clientHelloMsg, *echServerContext, error) { func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello) echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
if err != nil { if err != nil {
if errors.Is(err, errInvalidECHExt) { if errors.Is(err, errInvalidECHExt) {
@ -595,11 +595,11 @@ func (c *Conn) processECHClientHello(outer *clientHelloMsg) (*clientHelloMsg, *e
return outer, &echServerContext{inner: true}, nil return outer, &echServerContext{inner: true}, nil
} }
if len(c.config.EncryptedClientHelloKeys) == 0 { if len(echKeys) == 0 {
return outer, nil, nil return outer, nil, nil
} }
for _, echKey := range c.config.EncryptedClientHelloKeys { for _, echKey := range echKeys {
skip, config, err := parseECHConfig(echKey.Config) skip, config, err := parseECHConfig(echKey.Config)
if err != nil || skip { if err != nil || skip {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)

View File

@ -150,7 +150,15 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServer
// the contents of the client hello, since we may swap it out completely. // the contents of the client hello, since we may swap it out completely.
var ech *echServerContext var ech *echServerContext
if len(clientHello.encryptedClientHello) != 0 { if len(clientHello.encryptedClientHello) != 0 {
clientHello, ech, err = c.processECHClientHello(clientHello) echKeys := c.config.EncryptedClientHelloKeys
if c.config.GetEncryptedClientHelloKeys != nil {
echKeys, err = c.config.GetEncryptedClientHelloKeys(clientHelloInfo(ctx, c, clientHello))
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, err
}
}
clientHello, ech, err = c.processECHClientHello(clientHello, echKeys)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@ -877,8 +877,16 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
// If client sent ECH extension, but we didn't accept it, // If client sent ECH extension, but we didn't accept it,
// send retry configs, if available. // send retry configs, if available.
if len(hs.c.config.EncryptedClientHelloKeys) > 0 && len(hs.clientHello.encryptedClientHello) > 0 && hs.echContext == nil { echKeys := hs.c.config.EncryptedClientHelloKeys
encryptedExtensions.echRetryConfigs, err = buildRetryConfigList(hs.c.config.EncryptedClientHelloKeys) if hs.c.config.GetEncryptedClientHelloKeys != nil {
echKeys, err = hs.c.config.GetEncryptedClientHelloKeys(clientHelloInfo(hs.ctx, c, hs.clientHello))
if err != nil {
c.sendAlert(alertInternalError)
return err
}
}
if len(echKeys) > 0 && len(hs.clientHello.encryptedClientHello) > 0 && hs.echContext == nil {
encryptedExtensions.echRetryConfigs, err = buildRetryConfigList(echKeys)
if err != nil { if err != nil {
c.sendAlert(alertInternalError) c.sendAlert(alertInternalError)
return err return err