Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit f156fea

Browse filesBrowse files
committed
feat: add support for ECH when using custom clienthello specs
1 parent e430876 commit f156fea
Copy full SHA for f156fea

6 files changed

+156
-24
lines changed

‎ech.go

Copy file name to clipboardExpand all lines: ech.go
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,16 @@ func pickECHCipherSuite(suites []echCipher) (echCipher, error) {
207207
return echCipher{}, errors.New("tls: no supported symmetric ciphersuites for ECH")
208208
}
209209

210+
// [uTLS SECTION BEGIN]
210211
func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
211-
h, err := inner.marshalMsg(true)
212+
return encodeInnerClientHelloReorderOuterExts(inner, maxNameLength, nil)
213+
}
214+
215+
// [uTLS SECTION END]
216+
217+
// func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
218+
func encodeInnerClientHelloReorderOuterExts(inner *clientHelloMsg, maxNameLength int, outerExts []uint16) ([]byte, error) { // uTLS
219+
h, err := inner.marshalMsgReorderOuterExts(true, outerExts)
212220
if err != nil {
213221
return nil, err
214222
}

‎handshake_client.go

Copy file name to clipboardExpand all lines: handshake_client.go
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCli
214214

215215
var ech *echClientContext
216216
if c.config.EncryptedClientHelloConfigList != nil {
217-
if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {
218-
return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
219-
}
217+
// if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {
218+
// return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
219+
// }
220220
if c.config.MaxVersion != 0 && c.config.MaxVersion <= VersionTLS12 {
221221
return nil, nil, nil, errors.New("tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
222222
}

‎handshake_client_tls13.go

Copy file name to clipboardExpand all lines: handshake_client_tls13.go
+13-1Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,21 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
7575

7676
if hs.echContext != nil {
7777
hs.echContext.innerTranscript = hs.suite.hash.New()
78-
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
78+
// [uTLS SECTION BEGIN]
79+
encodedInner, err := encodeInnerClientHelloReorderOuterExts(hs.echContext.innerHello, int(hs.echContext.config.MaxNameLength), hs.uconn.extensionsList())
80+
if err != nil {
81+
return err
82+
}
83+
84+
decodedInner, err := decodeInnerClientHello(hs.hello, encodedInner)
85+
if err != nil {
86+
return err
87+
}
88+
89+
if err := transcriptMsg(decodedInner, hs.echContext.innerTranscript); err != nil {
7990
return err
8091
}
92+
// [uTLS SECTION END]
8193
}
8294

8395
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {

‎handshake_messages.go

Copy file name to clipboardExpand all lines: handshake_messages.go
+29-11Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,15 @@ type clientHelloMsg struct {
105105
nextProtoNeg bool
106106
}
107107

108+
// [uTLS SECTION BEGIN]
108109
func (m *clientHelloMsg) marshalMsg(echInner bool) ([]byte, error) {
110+
return m.marshalMsgReorderOuterExts(echInner, nil)
111+
}
112+
113+
// [uTLS SECTION END]
114+
115+
// func (m *clientHelloMsg) marshalMsg(echInner bool) ([]byte, error) {
116+
func (m *clientHelloMsg) marshalMsgReorderOuterExts(echInner bool, outerExts []uint16) ([]byte, error) { // uTLS
109117
var exts cryptobyte.Builder
110118
if len(m.serverName) > 0 {
111119
// RFC 6066, Section 3
@@ -254,18 +262,14 @@ func (m *clientHelloMsg) marshalMsg(echInner bool) ([]byte, error) {
254262
}
255263
if len(m.supportedVersions) > 0 {
256264
// RFC 8446, Section 4.2.1
257-
if echInner {
258-
echOuterExts = append(echOuterExts, extensionSupportedVersions)
259-
} else {
260-
exts.AddUint16(extensionSupportedVersions)
261-
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
262-
exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) {
263-
for _, vers := range m.supportedVersions {
264-
exts.AddUint16(vers)
265-
}
266-
})
265+
exts.AddUint16(extensionSupportedVersions)
266+
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {
267+
exts.AddUint8LengthPrefixed(func(exts *cryptobyte.Builder) {
268+
for _, vers := range m.supportedVersions {
269+
exts.AddUint16(vers)
270+
}
267271
})
268-
}
272+
})
269273
}
270274
if len(m.cookie) > 0 {
271275
// RFC 8446, Section 4.2.2
@@ -311,6 +315,20 @@ func (m *clientHelloMsg) marshalMsg(echInner bool) ([]byte, error) {
311315
})
312316
}
313317
}
318+
// [uTLS SECTION BEGIN]
319+
if echInner && outerExts != nil {
320+
echOuterExtsReordered := slices.Collect(func(yield func(uint16) bool) {
321+
for _, ext := range outerExts {
322+
if slices.Contains(echOuterExts, ext) {
323+
if !yield(ext) {
324+
return
325+
}
326+
}
327+
}
328+
})
329+
echOuterExts = echOuterExtsReordered
330+
}
331+
// [uTLS SECTION END]
314332
if len(echOuterExts) > 0 && echInner {
315333
exts.AddUint16(extensionECHOuterExtensions)
316334
exts.AddUint16LengthPrefixed(func(exts *cryptobyte.Builder) {

‎u_conn.go

Copy file name to clipboardExpand all lines: u_conn.go
+98-4Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import (
1414
"fmt"
1515
"hash"
1616
"net"
17+
"slices"
1718
"strconv"
19+
20+
"golang.org/x/crypto/cryptobyte"
1821
)
1922

2023
type ClientHelloBuildStatus int
@@ -487,15 +490,106 @@ func (uconn *UConn) ApplyConfig() error {
487490
return nil
488491
}
489492

493+
func (uconn *UConn) extensionsList() []uint16 {
494+
495+
outerExts := []uint16{}
496+
for _, ext := range uconn.Extensions {
497+
buffer := cryptobyte.String(make([]byte, 2000))
498+
ext.Read(buffer)
499+
var extension uint16
500+
buffer.ReadUint16(&extension)
501+
outerExts = append(outerExts, extension)
502+
}
503+
return outerExts
504+
}
505+
490506
func (uconn *UConn) MarshalClientHello() error {
491-
if len(uconn.config.ECHConfigs) > 0 && uconn.ech != nil {
492-
if err := uconn.ech.Configure(uconn.config.ECHConfigs); err != nil {
507+
if len(uconn.config.EncryptedClientHelloConfigList) > 0 {
508+
inner, _, ech, err := uconn.makeClientHello()
509+
if err != nil {
493510
return err
494511
}
495-
return uconn.ech.MarshalClientHello(uconn)
512+
513+
inner.keyShares = KeyShares(uconn.HandshakeState.Hello.KeyShares).ToPrivate()
514+
inner.supportedSignatureAlgorithms = uconn.HandshakeState.Hello.SupportedSignatureAlgorithms
515+
inner.sessionId = uconn.HandshakeState.Hello.SessionId
516+
inner.supportedCurves = uconn.HandshakeState.Hello.SupportedCurves
517+
inner.supportedVersions = []uint16{VersionTLS13}
518+
519+
ech.innerHello = inner
520+
521+
encapKey := ech.encapsulatedKey
522+
523+
encodedInner, err := encodeInnerClientHelloReorderOuterExts(inner, int(ech.config.MaxNameLength), uconn.extensionsList())
524+
if err != nil {
525+
return err
526+
}
527+
528+
// NOTE: the tag lengths for all of the supported AEADs are the same (16
529+
// bytes), so we have hardcoded it here. If we add support for another AEAD
530+
// with a different tag length, we will need to change this.
531+
encryptedLen := len(encodedInner) + 16 // AEAD tag length
532+
outerECHExt, err := generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, make([]byte, encryptedLen))
533+
if err != nil {
534+
return err
535+
}
536+
537+
sniExtIdex := slices.IndexFunc(uconn.Extensions, func(ext TLSExtension) bool {
538+
_, ok := ext.(*SNIExtension)
539+
return ok
540+
})
541+
uconn.Extensions[sniExtIdex] = &SNIExtension{
542+
ServerName: string(ech.config.PublicName),
543+
}
544+
545+
echExtIdx := slices.IndexFunc(uconn.Extensions, func(ext TLSExtension) bool {
546+
_, ok := ext.(EncryptedClientHelloExtension)
547+
return ok
548+
})
549+
uconn.Extensions[echExtIdx] = &GenericExtension{
550+
Id: extensionEncryptedClientHello,
551+
Data: outerECHExt,
552+
}
553+
554+
// uconn.HandshakeState.Hello.Random = make([]byte, 32)
555+
// _, err = io.ReadFull(uconn.config.rand(), uconn.HandshakeState.Hello.Random)
556+
// if err != nil {
557+
// return errors.New("tls: short read from Rand: " + err.Error())
558+
// }
559+
560+
if err := uconn.MarshalClientHelloNoECH(); err != nil {
561+
return err
562+
}
563+
564+
serializedOuter := uconn.HandshakeState.Hello.Raw
565+
serializedOuter = serializedOuter[4:] // strip the four byte prefix
566+
encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
567+
if err != nil {
568+
return err
569+
}
570+
outerECHExt, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
571+
if err != nil {
572+
return err
573+
}
574+
uconn.Extensions[echExtIdx] = &GenericExtension{
575+
Id: extensionEncryptedClientHello,
576+
Data: outerECHExt,
577+
}
578+
579+
if err := uconn.MarshalClientHelloNoECH(); err != nil {
580+
return err
581+
}
582+
583+
uconn.echCtx = ech
584+
return nil
496585
}
497586

498-
return uconn.MarshalClientHelloNoECH() // if no ECH pointer, just marshal normally
587+
if err := uconn.MarshalClientHelloNoECH(); err != nil {
588+
return err
589+
}
590+
591+
return nil
592+
499593
}
500594

501595
// MarshalClientHelloNoECH marshals ClientHello as if there was no

‎u_handshake_client.go

Copy file name to clipboardExpand all lines: u_handshake_client.go
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivat
353353

354354
var ech *echClientContext
355355
if c.config.EncryptedClientHelloConfigList != nil {
356-
if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {
357-
return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
358-
}
356+
// if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {
357+
// return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
358+
// }
359359
if c.config.MaxVersion != 0 && c.config.MaxVersion <= VersionTLS12 {
360360
return nil, nil, nil, errors.New("tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
361361
}
@@ -483,7 +483,7 @@ func (c *UConn) clientHandshake(ctx context.Context) (err error) {
483483
}()
484484
}
485485

486-
if ech != nil {
486+
if ech != nil && c.clientHelloBuildStatus == BuildByGoTLS {
487487
// Split hello into inner and outer
488488
ech.innerHello = hello.clone()
489489

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.