mirror of
https://github.com/XTLS/REALITY.git
synced 2026-10-12 09:18:14 +03:00
crypto/tls: add server-side ECH
Adds support for server-side ECH. We make a couple of implementation decisions that are not completely in-line with the spec. In particular, we don't enforce that the SNI matches the ECHConfig public_name, and we implement a hybrid shared/backend mode (rather than shared or split mode, as described in Section 7). Both of these match the behavior of BoringSSL. The hybrid server mode will either act as a shared mode server, where-in the server accepts "outer" client hellos and unwraps them before processing the "inner" hello, or accepts bare "inner" hellos initially. This lets the server operate either transparently as a shared mode server, or a backend server, in Section 7 terminology. This seems like the best implementation choice for a TLS library. Fixes #68500 Change-Id: Ife69db7c1886610742e95e76b0ca92587e6d7ed4 Reviewed-on: https://go-review.googlesource.com/c/go/+/623576 Reviewed-by: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Daniel McCarney <daniel@binaryparadox.net> Auto-Submit: Roland Shoemaker <roland@golang.org> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
This commit is contained in:
+30
-7
@@ -40,7 +40,7 @@ type serverHandshakeState struct {
|
||||
|
||||
// serverHandshake performs a TLS handshake as a server.
|
||||
func (c *Conn) serverHandshake(ctx context.Context) error {
|
||||
clientHello, err := c.readClientHello(ctx)
|
||||
clientHello, ech, err := c.readClientHello(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -50,6 +50,7 @@ func (c *Conn) serverHandshake(ctx context.Context) error {
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
clientHello: clientHello,
|
||||
echContext: ech,
|
||||
}
|
||||
return hs.handshake()
|
||||
}
|
||||
@@ -130,17 +131,27 @@ func (hs *serverHandshakeState) handshake() error {
|
||||
}
|
||||
|
||||
// readClientHello reads a ClientHello message and selects the protocol version.
|
||||
func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
|
||||
func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServerContext, error) {
|
||||
// clientHelloMsg is included in the transcript, but we haven't initialized
|
||||
// it yet. The respective handshake functions will record it themselves.
|
||||
msg, err := c.readHandshake(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
clientHello, ok := msg.(*clientHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return nil, unexpectedMessageError(clientHello, msg)
|
||||
return nil, nil, unexpectedMessageError(clientHello, msg)
|
||||
}
|
||||
|
||||
// ECH processing has to be done before we do any other negotiation based on
|
||||
// the contents of the client hello, since we may swap it out completely.
|
||||
var ech *echServerContext
|
||||
if len(clientHello.encryptedClientHello) != 0 {
|
||||
clientHello, ech, err = c.processECHClientHello(clientHello)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var configForClient *Config
|
||||
@@ -149,7 +160,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
|
||||
chi := clientHelloInfo(ctx, c, clientHello)
|
||||
if configForClient, err = c.config.GetConfigForClient(chi); err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
} else if configForClient != nil {
|
||||
c.config = configForClient
|
||||
}
|
||||
@@ -163,13 +174,25 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
|
||||
c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
|
||||
if !ok {
|
||||
c.sendAlert(alertProtocolVersion)
|
||||
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
|
||||
return nil, nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
|
||||
}
|
||||
c.haveVers = true
|
||||
c.in.version = c.vers
|
||||
c.out.version = c.vers
|
||||
|
||||
return clientHello, nil
|
||||
// This check reflects some odd specification implied behavior. Client-facing servers
|
||||
// are supposed to reject hellos with outer ECH and inner ECH that offers 1.2, but
|
||||
// backend servers are allowed to accept hellos with inner ECH that offer 1.2, since
|
||||
// they cannot expect client-facing servers to behave properly. Since we act as both
|
||||
// a client-facing and backend server, we only enforce 1.3 being negotiated if we
|
||||
// saw a hello with outer ECH first. The spec probably should've made this an error,
|
||||
// but it didn't, and this matches the boringssl behavior.
|
||||
if c.vers != VersionTLS13 && (ech != nil && !ech.inner) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return nil, nil, errors.New("tls: Encrypted Client Hello cannot be used pre-TLS 1.3")
|
||||
}
|
||||
|
||||
return clientHello, ech, nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) processClientHello() error {
|
||||
|
||||
Reference in New Issue
Block a user