|
1 | 1 | package tls |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "crypto/ecdh" |
| 5 | + "crypto/hpke" |
| 6 | + "crypto/rand" |
4 | 7 | "encoding/base64" |
5 | 8 | "encoding/pem" |
| 9 | + "io" |
6 | 10 | "os" |
7 | 11 |
|
8 | | - "github.com/xtls/reality/hpke" |
9 | 12 | "github.com/xtls/xray-core/common" |
10 | 13 | "github.com/xtls/xray-core/main/commands/base" |
11 | 14 | "github.com/xtls/xray-core/transport/internet/tls" |
@@ -40,15 +43,15 @@ func executeECH(cmd *base.Command, args []string) { |
40 | 43 | // if *input_pqSignatureSchemesEnabled { |
41 | 44 | // kem = 0x30 // hpke.KEM_X25519_KYBER768_DRAFT00 |
42 | 45 | // } else { |
43 | | - kem = hpke.DHKEM_X25519_HKDF_SHA256 |
| 46 | + kem = hpke.DHKEM(ecdh.X25519()).ID() |
44 | 47 | // } |
45 | 48 |
|
46 | | - echConfig, priv, err := tls.GenerateECHKeySet(0, *input_serverName, kem) |
| 49 | + echConfig, priv, err := generateECHKeySet(0, *input_serverName, kem) |
47 | 50 | common.Must(err) |
48 | 51 |
|
49 | 52 | var configBuffer, keyBuffer []byte |
50 | 53 | if *input_echServerKeys == "" { |
51 | | - configBytes, _ := tls.MarshalBinary(echConfig) |
| 54 | + configBytes, _ := marshalBinary(echConfig) |
52 | 55 | var b cryptobyte.Builder |
53 | 56 | b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { |
54 | 57 | child.AddBytes(configBytes) |
@@ -91,3 +94,86 @@ func executeECH(cmd *base.Command, args []string) { |
91 | 94 | os.Stdout.WriteString("ECH server keys: \n" + base64.StdEncoding.EncodeToString(keyBuffer) + "\n") |
92 | 95 | } |
93 | 96 | } |
| 97 | + |
| 98 | +type EchConfig struct { |
| 99 | + Version uint16 |
| 100 | + ConfigID uint8 |
| 101 | + KemID uint16 |
| 102 | + PublicKey []byte |
| 103 | + SymmetricCipherSuite []EchCipher |
| 104 | + MaxNameLength uint8 |
| 105 | + PublicName []byte |
| 106 | + Extensions []Extension |
| 107 | +} |
| 108 | + |
| 109 | +type EchCipher struct { |
| 110 | + KDFID uint16 |
| 111 | + AEADID uint16 |
| 112 | +} |
| 113 | + |
| 114 | +type Extension struct { |
| 115 | + Type uint16 |
| 116 | + Data []byte |
| 117 | +} |
| 118 | + |
| 119 | +// reference github.com/OmarTariq612/goech |
| 120 | +func marshalBinary(ech EchConfig) ([]byte, error) { |
| 121 | + var b cryptobyte.Builder |
| 122 | + b.AddUint16(ech.Version) |
| 123 | + b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { |
| 124 | + child.AddUint8(ech.ConfigID) |
| 125 | + child.AddUint16(ech.KemID) |
| 126 | + child.AddUint16(uint16(len(ech.PublicKey))) |
| 127 | + child.AddBytes(ech.PublicKey) |
| 128 | + child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { |
| 129 | + for _, cipherSuite := range ech.SymmetricCipherSuite { |
| 130 | + child.AddUint16(cipherSuite.KDFID) |
| 131 | + child.AddUint16(cipherSuite.AEADID) |
| 132 | + } |
| 133 | + }) |
| 134 | + child.AddUint8(ech.MaxNameLength) |
| 135 | + child.AddUint8(uint8(len(ech.PublicName))) |
| 136 | + child.AddBytes(ech.PublicName) |
| 137 | + child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) { |
| 138 | + for _, extention := range ech.Extensions { |
| 139 | + child.AddUint16(extention.Type) |
| 140 | + child.AddBytes(extention.Data) |
| 141 | + } |
| 142 | + }) |
| 143 | + }) |
| 144 | + return b.Bytes() |
| 145 | +} |
| 146 | + |
| 147 | +const ExtensionEncryptedClientHello = 0xfe0d |
| 148 | + |
| 149 | +func generateECHKeySet(configID uint8, domain string, kem uint16) (EchConfig, []byte, error) { |
| 150 | + config := EchConfig{ |
| 151 | + Version: ExtensionEncryptedClientHello, |
| 152 | + ConfigID: configID, |
| 153 | + PublicName: []byte(domain), |
| 154 | + KemID: kem, |
| 155 | + SymmetricCipherSuite: []EchCipher{ |
| 156 | + {KDFID: hpke.HKDFSHA256().ID(), AEADID: hpke.AES128GCM().ID()}, |
| 157 | + {KDFID: hpke.HKDFSHA256().ID(), AEADID: hpke.AES256GCM().ID()}, |
| 158 | + {KDFID: hpke.HKDFSHA256().ID(), AEADID: hpke.ChaCha20Poly1305().ID()}, |
| 159 | + {KDFID: hpke.HKDFSHA384().ID(), AEADID: hpke.AES128GCM().ID()}, |
| 160 | + {KDFID: hpke.HKDFSHA384().ID(), AEADID: hpke.AES256GCM().ID()}, |
| 161 | + {KDFID: hpke.HKDFSHA384().ID(), AEADID: hpke.ChaCha20Poly1305().ID()}, |
| 162 | + {KDFID: hpke.HKDFSHA512().ID(), AEADID: hpke.AES128GCM().ID()}, |
| 163 | + {KDFID: hpke.HKDFSHA512().ID(), AEADID: hpke.AES256GCM().ID()}, |
| 164 | + {KDFID: hpke.HKDFSHA512().ID(), AEADID: hpke.ChaCha20Poly1305().ID()}, |
| 165 | + }, |
| 166 | + MaxNameLength: 0, |
| 167 | + Extensions: nil, |
| 168 | + } |
| 169 | + // if kem == hpke.DHKEM_X25519_HKDF_SHA256 { |
| 170 | + curve := ecdh.X25519() |
| 171 | + priv := make([]byte, 32) |
| 172 | + _, err := io.ReadFull(rand.Reader, priv) |
| 173 | + if err != nil { |
| 174 | + return config, nil, err |
| 175 | + } |
| 176 | + privKey, _ := curve.NewPrivateKey(priv) |
| 177 | + config.PublicKey = privKey.PublicKey().Bytes() |
| 178 | + return config, priv, nil |
| 179 | +} |
0 commit comments