Skip to content

crypto/tls: support server side Encrypted Client Hello #68500

Closed
@rolandshoemaker

Description

@rolandshoemaker

With client support for ECH landing in 1.23, we should now move on to server side support, hopefully for 1.24.

After implementing client-side ECH, server-side seems a little more complicated, mainly from the configuration standpoint.

The more I think about it, the less I think we'll want to reuse the EncryptedClientHelloConfigList field we added for server-side support, since we need a mapping between configs and keys, and we'll likely want to provide some more options.

Perhaps introducing a new struct EncryptedClientHelloKey with ID and private key fields, and a new EncryptedClientHelloKeys field in tls.Config would be sufficient, hiding the rest of the HPKE cipher suite configuration etc as an internal implementation detail.

Additionally we should probably include func MarshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloConfig) ([]byte, error) to allow servers to generate the config list necessary for clients and for retry configs.

Concretely the new API would be:

type EncryptedClientHelloKey struct {
	ID         int
	PrivateKey ecdh.PrivateKey
}

type Config struct {
    ...

	// EncryptedClientHelloConfigs are the ECH configs to use when a client
	// attempts ECH. The public name is expected to be specified by ServerName.
	//
	// If EncryptedClientHelloConfigs is set, MinVersion, if set, must be
	// VersionTLS13.
	//
	// If a client attempts ECH, but it is rejected by the server, the server
	// will send the retry configs specified in
	// EncryptedClientHelloRetryConfigList, if it is initialized.
	EncryptedClientHelloKeys []EncryptedClientHelloKey

	// EncryptedClientHelloRetryConfigList specifies the ECH retry configs to be
	// sent when a client attempts ECH but it is rejected by the server.
	EncryptedClientHelloRetryConfigList []byte
}

type EncryptedClientHelloConfig struct {
	ID                uint8
	PublicKey         ecdh.PublicKey
	PublicName        string
	MaximumNameLength uint8
}

// MarshalEncryptedClientHelloConfigList serializes a list of
// EncryptedClientHelloConfigs into a ECHConfigList, suitable for usage in
// Config.EncryptedClientHelloConfigList.
func MarshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloConfig) ([]byte, error)

An open question for me is how we handle SNI requests for names not included in the EncryptedClientHelloConfigs list. I think I'm erring on the side of saying we reject the handshake, and document that if the user wishes to support both ECH and non-ECH handshakes, they should use not configure EncryptedClientHelloConfigs in their top-level Config and instead use GetConfigForClient to only set EncryptedClientHelloConfigs for names they explicitly want ECH for. See Discussion below for more context, in short we want to continue supporting both ECH and non-ECH connections in a single config.

See #63369 for the client side proposal we implemented.

Metadata

Metadata

Type

No type

Projects

Status

Accepted

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions