1
2
3
4
5 package tls
6
7 import (
8 "bytes"
9 "crypto/hpke"
10 "errors"
11 "fmt"
12 "net/netip"
13 "slices"
14 "strings"
15
16 "golang.org/x/crypto/cryptobyte"
17 )
18
19 type echCipher struct {
20 KDFID uint16
21 AEADID uint16
22 }
23
24 type echExtension struct {
25 Type uint16
26 Data []byte
27 }
28
29 type echConfig struct {
30 raw []byte
31
32 Version uint16
33 Length uint16
34
35 ConfigID uint8
36 KemID uint16
37 PublicKey []byte
38 SymmetricCipherSuite []echCipher
39
40 MaxNameLength uint8
41 PublicName []byte
42 Extensions []echExtension
43 }
44
45 var errMalformedECHConfigList = errors.New("tls: malformed ECHConfigList")
46
47 type echConfigErr struct {
48 field string
49 }
50
51 func (e *echConfigErr) Error() string {
52 if e.field == "" {
53 return "tls: malformed ECHConfig"
54 }
55 return fmt.Sprintf("tls: malformed ECHConfig, invalid %s field", e.field)
56 }
57
58 func parseECHConfig(enc []byte) (skip bool, ec echConfig, err error) {
59 s := cryptobyte.String(enc)
60 ec.raw = enc
61 if !s.ReadUint16(&ec.Version) {
62 return false, echConfig{}, &echConfigErr{"version"}
63 }
64 if !s.ReadUint16(&ec.Length) {
65 return false, echConfig{}, &echConfigErr{"length"}
66 }
67 if len(ec.raw) < int(ec.Length)+4 {
68 return false, echConfig{}, &echConfigErr{"length"}
69 }
70 ec.raw = ec.raw[:int(ec.Length)+4]
71 if ec.Version != extensionEncryptedClientHello {
72 s.Skip(int(ec.Length))
73 return true, echConfig{}, nil
74 }
75 if !s.ReadUint8(&ec.ConfigID) {
76 return false, echConfig{}, &echConfigErr{"config_id"}
77 }
78 if !s.ReadUint16(&ec.KemID) {
79 return false, echConfig{}, &echConfigErr{"kem_id"}
80 }
81 if !readUint16LengthPrefixed(&s, &ec.PublicKey) {
82 return false, echConfig{}, &echConfigErr{"public_key"}
83 }
84 var cipherSuites cryptobyte.String
85 if !s.ReadUint16LengthPrefixed(&cipherSuites) {
86 return false, echConfig{}, &echConfigErr{"cipher_suites"}
87 }
88 for !cipherSuites.Empty() {
89 var c echCipher
90 if !cipherSuites.ReadUint16(&c.KDFID) {
91 return false, echConfig{}, &echConfigErr{"cipher_suites kdf_id"}
92 }
93 if !cipherSuites.ReadUint16(&c.AEADID) {
94 return false, echConfig{}, &echConfigErr{"cipher_suites aead_id"}
95 }
96 ec.SymmetricCipherSuite = append(ec.SymmetricCipherSuite, c)
97 }
98 if !s.ReadUint8(&ec.MaxNameLength) {
99 return false, echConfig{}, &echConfigErr{"maximum_name_length"}
100 }
101 var publicName cryptobyte.String
102 if !s.ReadUint8LengthPrefixed(&publicName) {
103 return false, echConfig{}, &echConfigErr{"public_name"}
104 }
105 ec.PublicName = publicName
106 var extensions cryptobyte.String
107 if !s.ReadUint16LengthPrefixed(&extensions) {
108 return false, echConfig{}, &echConfigErr{"extensions"}
109 }
110 for !extensions.Empty() {
111 var e echExtension
112 if !extensions.ReadUint16(&e.Type) {
113 return false, echConfig{}, &echConfigErr{"extensions type"}
114 }
115 if !extensions.ReadUint16LengthPrefixed((*cryptobyte.String)(&e.Data)) {
116 return false, echConfig{}, &echConfigErr{"extensions data"}
117 }
118 ec.Extensions = append(ec.Extensions, e)
119 }
120
121 return false, ec, nil
122 }
123
124
125
126
127 func parseECHConfigList(data []byte) ([]echConfig, error) {
128 s := cryptobyte.String(data)
129 var length uint16
130 if !s.ReadUint16(&length) {
131 return nil, errMalformedECHConfigList
132 }
133 if int(length) != len(data)-2 {
134 return nil, errMalformedECHConfigList
135 }
136 var configs []echConfig
137 for len(s) > 0 {
138 if len(s) < 4 {
139 return nil, errors.New("tls: malformed ECHConfig")
140 }
141 configLen := int(s[2])<<8 | int(s[3])
142 skip, ec, err := parseECHConfig(s)
143 if err != nil {
144 return nil, err
145 }
146 s = s[configLen+4:]
147 if !skip {
148 configs = append(configs, ec)
149 }
150 }
151 return configs, nil
152 }
153
154 func pickECHConfig(list []echConfig) (*echConfig, hpke.PublicKey, hpke.KDF, hpke.AEAD) {
155 for _, ec := range list {
156 if !validDNSName(string(ec.PublicName)) {
157 continue
158 }
159 var unsupportedExt bool
160 for _, ext := range ec.Extensions {
161
162
163
164 if ext.Type&uint16(1<<15) != 0 {
165 unsupportedExt = true
166 }
167 }
168 if unsupportedExt {
169 continue
170 }
171 kem, err := hpke.NewKEM(ec.KemID)
172 if err != nil {
173 continue
174 }
175 pub, err := kem.NewPublicKey(ec.PublicKey)
176 if err != nil {
177
178
179 continue
180 }
181 for _, cs := range ec.SymmetricCipherSuite {
182
183
184
185 kdf, err := hpke.NewKDF(cs.KDFID)
186 if err != nil {
187 continue
188 }
189
190
191 if cs.AEADID == 0xFFFF {
192 continue
193 }
194 aead, err := hpke.NewAEAD(cs.AEADID)
195 if err != nil {
196 continue
197 }
198 return &ec, pub, kdf, aead
199 }
200 }
201 return nil, nil, nil, nil
202 }
203
204 func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
205 h, err := inner.marshalMsg(true)
206 if err != nil {
207 return nil, err
208 }
209 h = h[4:]
210
211 var paddingLen int
212 if inner.serverName != "" {
213 paddingLen = max(0, maxNameLength-len(inner.serverName))
214 } else {
215 paddingLen = maxNameLength + 9
216 }
217 paddingLen += 31 - ((len(h) + paddingLen - 1) % 32)
218
219 return append(h, make([]byte, paddingLen)...), nil
220 }
221
222 func skipUint8LengthPrefixed(s *cryptobyte.String) bool {
223 var skip uint8
224 if !s.ReadUint8(&skip) {
225 return false
226 }
227 return s.Skip(int(skip))
228 }
229
230 func skipUint16LengthPrefixed(s *cryptobyte.String) bool {
231 var skip uint16
232 if !s.ReadUint16(&skip) {
233 return false
234 }
235 return s.Skip(int(skip))
236 }
237
238 type rawExtension struct {
239 extType uint16
240 data []byte
241 }
242
243 func extractRawExtensions(hello *clientHelloMsg) ([]rawExtension, error) {
244 s := cryptobyte.String(hello.original)
245 if !s.Skip(4+2+32) ||
246 !skipUint8LengthPrefixed(&s) ||
247 !skipUint16LengthPrefixed(&s) ||
248 !skipUint8LengthPrefixed(&s) {
249 return nil, errors.New("tls: malformed outer client hello")
250 }
251 var rawExtensions []rawExtension
252 var extensions cryptobyte.String
253 if !s.ReadUint16LengthPrefixed(&extensions) {
254 return nil, errors.New("tls: malformed outer client hello")
255 }
256
257 for !extensions.Empty() {
258 var extension uint16
259 var extData cryptobyte.String
260 if !extensions.ReadUint16(&extension) ||
261 !extensions.ReadUint16LengthPrefixed(&extData) {
262 return nil, errors.New("tls: invalid inner client hello")
263 }
264 rawExtensions = append(rawExtensions, rawExtension{extension, extData})
265 }
266 return rawExtensions, nil
267 }
268
269 func decodeInnerClientHello(outer *clientHelloMsg, encoded []byte) (*clientHelloMsg, error) {
270
271
272
273
274
275
276
277
278 innerReader := cryptobyte.String(encoded)
279 var versionAndRandom, sessionID, cipherSuites, compressionMethods []byte
280 var extensions cryptobyte.String
281 if !innerReader.ReadBytes(&versionAndRandom, 2+32) ||
282 !readUint8LengthPrefixed(&innerReader, &sessionID) ||
283 len(sessionID) != 0 ||
284 !readUint16LengthPrefixed(&innerReader, &cipherSuites) ||
285 !readUint8LengthPrefixed(&innerReader, &compressionMethods) ||
286 !innerReader.ReadUint16LengthPrefixed(&extensions) {
287 return nil, errors.New("tls: invalid inner client hello")
288 }
289
290
291
292
293 for _, p := range innerReader {
294 if p != 0 {
295 return nil, errors.New("tls: invalid inner client hello")
296 }
297 }
298
299 rawOuterExts, err := extractRawExtensions(outer)
300 if err != nil {
301 return nil, err
302 }
303
304 recon := cryptobyte.NewBuilder(nil)
305 recon.AddUint8(typeClientHello)
306 recon.AddUint24LengthPrefixed(func(recon *cryptobyte.Builder) {
307 recon.AddBytes(versionAndRandom)
308 recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) {
309 recon.AddBytes(outer.sessionId)
310 })
311 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
312 recon.AddBytes(cipherSuites)
313 })
314 recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) {
315 recon.AddBytes(compressionMethods)
316 })
317 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
318 var outerExtensionsSeen bool
319 for !extensions.Empty() {
320 var extension uint16
321 var extData cryptobyte.String
322 if !extensions.ReadUint16(&extension) ||
323 !extensions.ReadUint16LengthPrefixed(&extData) {
324 recon.SetError(errors.New("tls: invalid inner client hello"))
325 return
326 }
327 if extension == extensionECHOuterExtensions {
328 if outerExtensionsSeen {
329 recon.SetError(errors.New("tls: invalid outer extensions"))
330 return
331 }
332 outerExtensionsSeen = true
333 var outerExtensions cryptobyte.String
334 if !extData.ReadUint8LengthPrefixed(&outerExtensions) || !extData.Empty() ||
335 outerExtensions.Empty() {
336 recon.SetError(errors.New("tls: invalid inner client hello"))
337 return
338 }
339
340
341
342
343 var i int
344 for !outerExtensions.Empty() {
345 var extType uint16
346 if !outerExtensions.ReadUint16(&extType) {
347 recon.SetError(errors.New("tls: invalid inner client hello"))
348 return
349 }
350 if extType == extensionEncryptedClientHello ||
351 extType == extensionECHOuterExtensions {
352 recon.SetError(errors.New("tls: invalid outer extensions"))
353 return
354 }
355 for ; i <= len(rawOuterExts); i++ {
356 if i == len(rawOuterExts) {
357 recon.SetError(errors.New("tls: invalid outer extensions"))
358 return
359 }
360 if rawOuterExts[i].extType == extType {
361 break
362 }
363 }
364 recon.AddUint16(rawOuterExts[i].extType)
365 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
366 recon.AddBytes(rawOuterExts[i].data)
367 })
368 i++
369 }
370 } else {
371 recon.AddUint16(extension)
372 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
373 recon.AddBytes(extData)
374 })
375 }
376 }
377 })
378 })
379
380 reconBytes, err := recon.Bytes()
381 if err != nil {
382 return nil, err
383 }
384 inner := &clientHelloMsg{}
385 if !inner.unmarshal(reconBytes) {
386 return nil, errors.New("tls: invalid reconstructed inner client hello")
387 }
388
389 if !bytes.Equal(inner.encryptedClientHello, []byte{uint8(innerECHExt)}) {
390 return nil, errInvalidECHExt
391 }
392
393 hasTLS13 := false
394 for _, v := range inner.supportedVersions {
395
396
397
398
399
400 if v&0x0F0F == 0x0A0A && v&0xff == v>>8 {
401 continue
402 }
403
404
405 if v == VersionTLS13 {
406 hasTLS13 = true
407 } else if v < VersionTLS13 {
408
409 return nil, errors.New("tls: client sent encrypted_client_hello extension with unsupported versions")
410 }
411 }
412
413 if !hasTLS13 {
414 return nil, errors.New("tls: client sent encrypted_client_hello extension but did not offer TLS 1.3")
415 }
416
417 return inner, nil
418 }
419
420 func decryptECHPayload(context *hpke.Recipient, hello, payload []byte) ([]byte, error) {
421 outerAAD := bytes.Replace(hello[4:], payload, make([]byte, len(payload)), 1)
422 return context.Open(outerAAD, payload)
423 }
424
425 func generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payload []byte) ([]byte, error) {
426 var b cryptobyte.Builder
427 b.AddUint8(0)
428 b.AddUint16(kdfID)
429 b.AddUint16(aeadID)
430 b.AddUint8(id)
431 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(encodedKey) })
432 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(payload) })
433 return b.Bytes()
434 }
435
436 func computeAndUpdateOuterECHExtension(outer, inner *clientHelloMsg, ech *echClientContext, useKey bool) error {
437 var encapKey []byte
438 if useKey {
439 encapKey = ech.encapsulatedKey
440 }
441 encodedInner, err := encodeInnerClientHello(inner, int(ech.config.MaxNameLength))
442 if err != nil {
443 return err
444 }
445
446
447
448 encryptedLen := len(encodedInner) + 16
449 outer.encryptedClientHello, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, make([]byte, encryptedLen))
450 if err != nil {
451 return err
452 }
453 serializedOuter, err := outer.marshal()
454 if err != nil {
455 return err
456 }
457 serializedOuter = serializedOuter[4:]
458 encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
459 if err != nil {
460 return err
461 }
462 outer.encryptedClientHello, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
463 if err != nil {
464 return err
465 }
466 return nil
467 }
468
469
470
471
472
473 func validDNSName(name string) bool {
474
475
476
477 if _, err := netip.ParseAddr(name); err == nil {
478 return false
479 }
480 if len(name) > 253 {
481 return false
482 }
483 labels := strings.Split(name, ".")
484 if len(labels) <= 1 {
485 return false
486 }
487 for _, l := range labels {
488 labelLen := len(l)
489 if labelLen == 0 {
490 return false
491 }
492 for i, r := range l {
493 if r == '-' && (i == 0 || i == labelLen-1) {
494 return false
495 }
496 if (r < '0' || r > '9') && (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && r != '-' {
497 return false
498 }
499 }
500 }
501 return true
502 }
503
504
505
506
507
508
509
510 type ECHRejectionError struct {
511 RetryConfigList []byte
512 }
513
514 func (e *ECHRejectionError) Error() string {
515 return "tls: server rejected ECH"
516 }
517
518 var errMalformedECHExt = errors.New("tls: malformed encrypted_client_hello extension")
519 var errInvalidECHExt = errors.New("tls: client sent invalid encrypted_client_hello extension")
520
521 type echExtType uint8
522
523 const (
524 innerECHExt echExtType = 1
525 outerECHExt echExtType = 0
526 )
527
528 func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8, encap []byte, payload []byte, err error) {
529 data := make([]byte, len(ext))
530 copy(data, ext)
531 s := cryptobyte.String(data)
532 var echInt uint8
533 if !s.ReadUint8(&echInt) {
534 err = errMalformedECHExt
535 return
536 }
537 echType = echExtType(echInt)
538 if echType == innerECHExt {
539 if !s.Empty() {
540 err = errMalformedECHExt
541 return
542 }
543 return echType, cs, 0, nil, nil, nil
544 }
545 if echType != outerECHExt {
546 err = errInvalidECHExt
547 return
548 }
549 if !s.ReadUint16(&cs.KDFID) {
550 err = errMalformedECHExt
551 return
552 }
553 if !s.ReadUint16(&cs.AEADID) {
554 err = errMalformedECHExt
555 return
556 }
557 if !s.ReadUint8(&configID) {
558 err = errMalformedECHExt
559 return
560 }
561 if !readUint16LengthPrefixed(&s, &encap) {
562 err = errMalformedECHExt
563 return
564 }
565 if !readUint16LengthPrefixed(&s, &payload) {
566 err = errMalformedECHExt
567 return
568 }
569
570
571
572 return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
573 }
574
575 func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
576 echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
577 if err != nil {
578 if errors.Is(err, errInvalidECHExt) {
579 c.sendAlert(alertIllegalParameter)
580 } else {
581 c.sendAlert(alertDecodeError)
582 }
583
584 return nil, nil, errInvalidECHExt
585 }
586
587 if echType == innerECHExt {
588 return outer, &echServerContext{inner: true}, nil
589 }
590
591 if len(echKeys) == 0 {
592 return outer, nil, nil
593 }
594
595 for _, echKey := range echKeys {
596 skip, config, err := parseECHConfig(echKey.Config)
597 if err != nil {
598 c.sendAlert(alertInternalError)
599 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config: %s", err)
600 }
601 if skip {
602 continue
603 }
604
605
606 if !slices.Contains(config.SymmetricCipherSuite, echCiphersuite) {
607 continue
608 }
609 kem, err := hpke.NewKEM(config.KemID)
610 if err != nil {
611 c.sendAlert(alertInternalError)
612 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KEM: %s", err)
613 }
614 echPriv, err := kem.NewPrivateKey(echKey.PrivateKey)
615 if err != nil {
616 c.sendAlert(alertInternalError)
617 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey PrivateKey: %s", err)
618 }
619 kdf, err := hpke.NewKDF(echCiphersuite.KDFID)
620 if err != nil {
621 c.sendAlert(alertInternalError)
622 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KDF: %s", err)
623 }
624 aead, err := hpke.NewAEAD(echCiphersuite.AEADID)
625 if err != nil {
626 c.sendAlert(alertInternalError)
627 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config AEAD: %s", err)
628 }
629 info := append([]byte("tls ech\x00"), echKey.Config...)
630 hpkeContext, err := hpke.NewRecipient(encap, echPriv, kdf, aead, info)
631 if err != nil {
632
633 continue
634 }
635
636 encodedInner, err := decryptECHPayload(hpkeContext, outer.original, payload)
637 if err != nil {
638
639 continue
640 }
641
642
643
644
645
646
647
648 echInner, err := decodeInnerClientHello(outer, encodedInner)
649 if err != nil {
650 c.sendAlert(alertIllegalParameter)
651 return nil, nil, errInvalidECHExt
652 }
653
654 c.echAccepted = true
655
656 return echInner, &echServerContext{
657 hpkeContext: hpkeContext,
658 configID: configID,
659 ciphersuite: echCiphersuite,
660 }, nil
661 }
662
663 return outer, nil, nil
664 }
665
666 func buildRetryConfigList(keys []EncryptedClientHelloKey) ([]byte, error) {
667 var atLeastOneRetryConfig bool
668 var retryBuilder cryptobyte.Builder
669 retryBuilder.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
670 for _, c := range keys {
671 if !c.SendAsRetry {
672 continue
673 }
674 atLeastOneRetryConfig = true
675 b.AddBytes(c.Config)
676 }
677 })
678 if !atLeastOneRetryConfig {
679 return nil, nil
680 }
681 return retryBuilder.Bytes()
682 }
683
View as plain text