Skip to content

ECH: Support TLS Encrypted Client Hello #3813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ module github.com/xtls/xray-core
go 1.24

require (
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
github.com/OmarTariq612/goech v0.0.1
github.com/cloudflare/circl v1.6.0
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
github.com/golang/mock v1.7.0-rc.1
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
github.com/OmarTariq612/goech v0.0.1 h1:/0c918Bk1ik65GXDj2k7SOK78DyZr30Jmq9euy1/HXg=
github.com/OmarTariq612/goech v0.0.1/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
12 changes: 12 additions & 0 deletions infra/conf/transport_internet.go
Original file line number Diff line number Diff line change
@@ -412,6 +412,8 @@ type TLSConfig struct {
MasterKeyLog string `json:"masterKeyLog"`
ServerNameToVerify string `json:"serverNameToVerify"`
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
ECHConfigList string `json:"echConfigList"`
EchKeySets string `json:"echKeySets"`
}

// Build implements Buildable.
@@ -483,6 +485,16 @@ func (c *TLSConfig) Build() (proto.Message, error) {
}
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames

config.EchConfigList = c.ECHConfigList

if c.EchKeySets != "" {
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.EchKeySets)
if err != nil {
return nil, errors.New("invalid ECH Config", c.EchKeySets)
}
config.EchKeySets = EchPrivateKey
}

return config, nil
}

40 changes: 17 additions & 23 deletions main/commands/all/tls/ech.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package tls

import (
"encoding/json"
"encoding/pem"
"os"
"strings"

"github.com/OmarTariq612/goech"
"github.com/cloudflare/circl/hpke"
@@ -13,13 +11,13 @@ import (
)

var cmdECH = &base.Command{
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--json]`,
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--pem]`,
Short: `Generate TLS-ECH certificates`,
Long: `
Generate TLS-ECH certificates.

Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
Generate into json format: {{.Exec}} tls ech --json
Generate into pem format: {{.Exec}} tls ech --pem
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
}

@@ -29,7 +27,7 @@ func init() {

var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
var input_json = cmdECH.Flag.Bool("json", false, "True == turn on json output")
var input_pem = cmdECH.Flag.Bool("pem", false, "True == turn on pem output")

func executeECH(cmd *base.Command, args []string) {
var kem hpke.KEM
@@ -40,30 +38,26 @@ func executeECH(cmd *base.Command, args []string) {
kem = hpke.KEM_X25519_HKDF_SHA256
}

echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem)
echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem, nil)
common.Must(err)

configBuffer, _ := echKeySet.ECHConfig.MarshalBinary()
keyBuffer, _ := echKeySet.MarshalBinary()
// Make single key set to a list with only one element
ECHConfigList := make(goech.ECHConfigList, 1)
ECHConfigList[0] = echKeySet.ECHConfig
ECHKeySetList := make(goech.ECHKeySetList, 1)
ECHKeySetList[0] = echKeySet
configBuffer, _ := ECHConfigList.MarshalBinary()
keyBuffer, _ := ECHKeySetList.MarshalBinary()
configStr, _ := ECHConfigList.ToBase64()
keySetStr, _ := ECHKeySetList.ToBase64()

configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
if *input_json {
jECHConfigs := map[string]interface{}{
"configs": strings.Split(strings.TrimSpace(string(configPEM)), "\n"),
}
jECHKey := map[string]interface{}{
"key": strings.Split(strings.TrimSpace(string(keyPEM)), "\n"),
}

for _, i := range []map[string]interface{}{jECHConfigs, jECHKey} {
content, err := json.MarshalIndent(i, "", " ")
common.Must(err)
os.Stdout.Write(content)
os.Stdout.WriteString("\n")
}
} else {
if *input_pem {
os.Stdout.WriteString(configPEM)
os.Stdout.WriteString(keyPEM)
} else {
os.Stdout.WriteString("ECH config list: \n" + configStr + "\n")
os.Stdout.WriteString("ECH Key sets: \n" + keySetStr + "\n")
}
}
6 changes: 6 additions & 0 deletions transport/internet/tls/config.go
Original file line number Diff line number Diff line change
@@ -444,6 +444,12 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
config.KeyLogWriter = writer
}
}
if len(c.EchConfigList) > 0 || len(c.EchKeySets) > 0 {
err := ApplyECH(c, config)
if err != nil {
errors.LogError(context.Background(), err)
}
}

return config
}
40 changes: 30 additions & 10 deletions transport/internet/tls/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions transport/internet/tls/config.proto
Original file line number Diff line number Diff line change
@@ -91,4 +91,8 @@ message Config {
@Critical
*/
repeated string verify_peer_cert_in_names = 17;

string ech_config_list = 18;

bytes ech_key_sets = 19;
}
Loading