From d100be5ad5063d722bdb7e14d18aa7f55991be5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Thu, 12 Feb 2026 12:08:59 +0800 Subject: [PATCH] Chore: Migrate to Go 1.26 (#5680) --- go.mod | 2 +- main/commands/all/tls/ech.go | 94 ++++++++++++++++++++++++++++++-- transport/internet/tls/config.go | 12 ++-- transport/internet/tls/ech.go | 70 ------------------------ 4 files changed, 98 insertions(+), 80 deletions(-) diff --git a/go.mod b/go.mod index 39d10943..0ed1e6dd 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/xtls/xray-core -go 1.25.7 +go 1.26 require ( github.com/apernet/quic-go v0.57.2-0.20260111184307-eec823306178 diff --git a/main/commands/all/tls/ech.go b/main/commands/all/tls/ech.go index 18ecb602..b2b532ff 100644 --- a/main/commands/all/tls/ech.go +++ b/main/commands/all/tls/ech.go @@ -1,11 +1,14 @@ package tls import ( + "crypto/ecdh" + "crypto/hpke" + "crypto/rand" "encoding/base64" "encoding/pem" + "io" "os" - "github.com/xtls/reality/hpke" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/main/commands/base" "github.com/xtls/xray-core/transport/internet/tls" @@ -40,15 +43,15 @@ func executeECH(cmd *base.Command, args []string) { // if *input_pqSignatureSchemesEnabled { // kem = 0x30 // hpke.KEM_X25519_KYBER768_DRAFT00 // } else { - kem = hpke.DHKEM_X25519_HKDF_SHA256 + kem = hpke.DHKEM(ecdh.X25519()).ID() // } - echConfig, priv, err := tls.GenerateECHKeySet(0, *input_serverName, kem) + echConfig, priv, err := generateECHKeySet(0, *input_serverName, kem) common.Must(err) var configBuffer, keyBuffer []byte if *input_echServerKeys == "" { - configBytes, _ := tls.MarshalBinary(echConfig) + configBytes, _ := marshalBinary(echConfig) var b cryptobyte.Builder b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { child.AddBytes(configBytes) @@ -91,3 +94,86 @@ func executeECH(cmd *base.Command, args []string) { os.Stdout.WriteString("ECH server keys: \n" + base64.StdEncoding.EncodeToString(keyBuffer) + "\n") } } + +type EchConfig struct { + Version uint16 + ConfigID uint8 + KemID uint16 + PublicKey []byte + SymmetricCipherSuite []EchCipher + MaxNameLength uint8 + PublicName []byte + Extensions []Extension +} + +type EchCipher struct { + KDFID uint16 + AEADID uint16 +} + +type Extension struct { + Type uint16 + Data []byte +} + +// reference github.com/OmarTariq612/goech +func marshalBinary(ech EchConfig) ([]byte, error) { + var b cryptobyte.Builder + b.AddUint16(ech.Version) + b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { + child.AddUint8(ech.ConfigID) + child.AddUint16(ech.KemID) + child.AddUint16(uint16(len(ech.PublicKey))) + child.AddBytes(ech.PublicKey) + child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { + for _, cipherSuite := range ech.SymmetricCipherSuite { + child.AddUint16(cipherSuite.KDFID) + child.AddUint16(cipherSuite.AEADID) + } + }) + child.AddUint8(ech.MaxNameLength) + child.AddUint8(uint8(len(ech.PublicName))) + child.AddBytes(ech.PublicName) + child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { + for _, extention := range ech.Extensions { + child.AddUint16(extention.Type) + child.AddBytes(extention.Data) + } + }) + }) + return b.Bytes() +} + +const ExtensionEncryptedClientHello = 0xfe0d + +func generateECHKeySet(configID uint8, domain string, kem uint16) (EchConfig, []byte, error) { + config := EchConfig{ + Version: ExtensionEncryptedClientHello, + ConfigID: configID, + PublicName: []byte(domain), + KemID: kem, + SymmetricCipherSuite: []EchCipher{ + {KDFID: hpke.HKDFSHA256().ID(), AEADID: hpke.AES128GCM().ID()}, + {KDFID: hpke.HKDFSHA256().ID(), AEADID: hpke.AES256GCM().ID()}, + {KDFID: hpke.HKDFSHA256().ID(), AEADID: hpke.ChaCha20Poly1305().ID()}, + {KDFID: hpke.HKDFSHA384().ID(), AEADID: hpke.AES128GCM().ID()}, + {KDFID: hpke.HKDFSHA384().ID(), AEADID: hpke.AES256GCM().ID()}, + {KDFID: hpke.HKDFSHA384().ID(), AEADID: hpke.ChaCha20Poly1305().ID()}, + {KDFID: hpke.HKDFSHA512().ID(), AEADID: hpke.AES128GCM().ID()}, + {KDFID: hpke.HKDFSHA512().ID(), AEADID: hpke.AES256GCM().ID()}, + {KDFID: hpke.HKDFSHA512().ID(), AEADID: hpke.ChaCha20Poly1305().ID()}, + }, + MaxNameLength: 0, + Extensions: nil, + } + // if kem == hpke.DHKEM_X25519_HKDF_SHA256 { + curve := ecdh.X25519() + priv := make([]byte, 32) + _, err := io.ReadFull(rand.Reader, priv) + if err != nil { + return config, nil, err + } + privKey, _ := curve.NewPrivateKey(priv) + config.PublicKey = privKey.PublicKey().Bytes() + return config, priv, nil +} diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index 4ecc9728..9a41f379 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -524,11 +524,13 @@ func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config { func ParseCurveName(curveNames []string) []tls.CurveID { curveMap := map[string]tls.CurveID{ - "curvep256": tls.CurveP256, - "curvep384": tls.CurveP384, - "curvep521": tls.CurveP521, - "x25519": tls.X25519, - "x25519mlkem768": tls.X25519MLKEM768, + "curvep256": tls.CurveP256, + "curvep384": tls.CurveP384, + "curvep521": tls.CurveP521, + "x25519": tls.X25519, + "x25519mlkem768": tls.X25519MLKEM768, + "secp256r1mlkem768": tls.SecP256r1MLKEM768, + "secp384r1mlkem1024": tls.SecP384r1MLKEM1024, } var curveIDs []tls.CurveID diff --git a/transport/internet/tls/ech.go b/transport/internet/tls/ech.go index 6ba38aea..ba175fd8 100644 --- a/transport/internet/tls/ech.go +++ b/transport/internet/tls/ech.go @@ -3,8 +3,6 @@ package tls import ( "bytes" "context" - "crypto/ecdh" - "crypto/rand" "crypto/tls" "encoding/base64" "encoding/binary" @@ -23,8 +21,6 @@ import ( "golang.org/x/net/http2" "github.com/miekg/dns" - "github.com/xtls/reality" - "github.com/xtls/reality/hpke" "github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/net" "github.com/xtls/xray-core/common/utils" @@ -330,34 +326,6 @@ func dnsQuery(server string, domain string, sockopt *internet.SocketConfig) ([]b return nil, dns2.DefaultTTL, nil } -// reference github.com/OmarTariq612/goech -func MarshalBinary(ech reality.EchConfig) ([]byte, error) { - var b cryptobyte.Builder - b.AddUint16(ech.Version) - b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { - child.AddUint8(ech.ConfigID) - child.AddUint16(ech.KemID) - child.AddUint16(uint16(len(ech.PublicKey))) - child.AddBytes(ech.PublicKey) - child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { - for _, cipherSuite := range ech.SymmetricCipherSuite { - child.AddUint16(cipherSuite.KDFID) - child.AddUint16(cipherSuite.AEADID) - } - }) - child.AddUint8(ech.MaxNameLength) - child.AddUint8(uint8(len(ech.PublicName))) - child.AddBytes(ech.PublicName) - child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { - for _, extention := range ech.Extensions { - child.AddUint16(extention.Type) - child.AddBytes(extention.Data) - } - }) - }) - return b.Bytes() -} - var ErrInvalidLen = errors.New("goech: invalid length") func ConvertToGoECHKeys(data []byte) ([]tls.EncryptedClientHelloKey, error) { @@ -392,41 +360,3 @@ func ConvertToGoECHKeys(data []byte) ([]tls.EncryptedClientHelloKey, error) { } return keys, nil } - -const ExtensionEncryptedClientHello = 0xfe0d -const KDF_HKDF_SHA384 = 0x0002 -const KDF_HKDF_SHA512 = 0x0003 - -func GenerateECHKeySet(configID uint8, domain string, kem uint16) (reality.EchConfig, []byte, error) { - config := reality.EchConfig{ - Version: ExtensionEncryptedClientHello, - ConfigID: configID, - PublicName: []byte(domain), - KemID: kem, - SymmetricCipherSuite: []reality.EchCipher{ - {KDFID: hpke.KDF_HKDF_SHA256, AEADID: hpke.AEAD_AES_128_GCM}, - {KDFID: hpke.KDF_HKDF_SHA256, AEADID: hpke.AEAD_AES_256_GCM}, - {KDFID: hpke.KDF_HKDF_SHA256, AEADID: hpke.AEAD_ChaCha20Poly1305}, - {KDFID: KDF_HKDF_SHA384, AEADID: hpke.AEAD_AES_128_GCM}, - {KDFID: KDF_HKDF_SHA384, AEADID: hpke.AEAD_AES_256_GCM}, - {KDFID: KDF_HKDF_SHA384, AEADID: hpke.AEAD_ChaCha20Poly1305}, - {KDFID: KDF_HKDF_SHA512, AEADID: hpke.AEAD_AES_128_GCM}, - {KDFID: KDF_HKDF_SHA512, AEADID: hpke.AEAD_AES_256_GCM}, - {KDFID: KDF_HKDF_SHA512, AEADID: hpke.AEAD_ChaCha20Poly1305}, - }, - MaxNameLength: 0, - Extensions: nil, - } - // if kem == hpke.DHKEM_X25519_HKDF_SHA256 { - curve := ecdh.X25519() - priv := make([]byte, 32) //x25519 - _, err := io.ReadFull(rand.Reader, priv) - if err != nil { - return config, nil, err - } - privKey, _ := curve.NewPrivateKey(priv) - config.PublicKey = privKey.PublicKey().Bytes() - return config, priv, nil - // } - // TODO: add mlkem768 (former kyber768 draft00). The golang mlkem private key is 64 bytes seed? -}