ech.mx raw
1 // Copyright 2024 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package tls
6
7 import (
8 "bytes"
9 "crypto/hpke"
10 "errors"
11 "fmt"
12
13 "golang.org/x/crypto/cryptobyte"
14 )
15
16 type echCipher struct {
17 KDFID uint16
18 AEADID uint16
19 }
20
21 type echExtension struct {
22 Type uint16
23 Data []byte
24 }
25
26 type echConfig struct {
27 raw []byte
28
29 Version uint16
30 Length uint16
31
32 ConfigID uint8
33 KemID uint16
34 PublicKey []byte
35 SymmetricCipherSuite []echCipher
36
37 MaxNameLength uint8
38 PublicName []byte
39 Extensions []echExtension
40 }
41
42 var errMalformedECHConfigList = errors.New("tls: malformed ECHConfigList")
43
44 type echConfigErr struct {
45 field []byte
46 }
47
48 func (e *echConfigErr) Error() string {
49 if e.field == "" {
50 return "tls: malformed ECHConfig"
51 }
52 return fmt.Sprintf("tls: malformed ECHConfig, invalid %s field", e.field)
53 }
54
55 func parseECHConfig(enc []byte) (skip bool, ec echConfig, err error) {
56 s := cryptobyte.String(enc)
57 ec.raw = []byte(enc)
58 if !s.ReadUint16(&ec.Version) {
59 return false, echConfig{}, &echConfigErr{"version"}
60 }
61 if !s.ReadUint16(&ec.Length) {
62 return false, echConfig{}, &echConfigErr{"length"}
63 }
64 if len(ec.raw) < int(ec.Length)+4 {
65 return false, echConfig{}, &echConfigErr{"length"}
66 }
67 ec.raw = ec.raw[:ec.Length+4]
68 if ec.Version != extensionEncryptedClientHello {
69 s.Skip(int(ec.Length))
70 return true, echConfig{}, nil
71 }
72 if !s.ReadUint8(&ec.ConfigID) {
73 return false, echConfig{}, &echConfigErr{"config_id"}
74 }
75 if !s.ReadUint16(&ec.KemID) {
76 return false, echConfig{}, &echConfigErr{"kem_id"}
77 }
78 if !readUint16LengthPrefixed(&s, &ec.PublicKey) {
79 return false, echConfig{}, &echConfigErr{"public_key"}
80 }
81 var cipherSuites cryptobyte.String
82 if !s.ReadUint16LengthPrefixed(&cipherSuites) {
83 return false, echConfig{}, &echConfigErr{"cipher_suites"}
84 }
85 for !cipherSuites.Empty() {
86 var c echCipher
87 if !cipherSuites.ReadUint16(&c.KDFID) {
88 return false, echConfig{}, &echConfigErr{"cipher_suites kdf_id"}
89 }
90 if !cipherSuites.ReadUint16(&c.AEADID) {
91 return false, echConfig{}, &echConfigErr{"cipher_suites aead_id"}
92 }
93 ec.SymmetricCipherSuite = append(ec.SymmetricCipherSuite, c)
94 }
95 if !s.ReadUint8(&ec.MaxNameLength) {
96 return false, echConfig{}, &echConfigErr{"maximum_name_length"}
97 }
98 var publicName cryptobyte.String
99 if !s.ReadUint8LengthPrefixed(&publicName) {
100 return false, echConfig{}, &echConfigErr{"public_name"}
101 }
102 ec.PublicName = publicName
103 var extensions cryptobyte.String
104 if !s.ReadUint16LengthPrefixed(&extensions) {
105 return false, echConfig{}, &echConfigErr{"extensions"}
106 }
107 for !extensions.Empty() {
108 var e echExtension
109 if !extensions.ReadUint16(&e.Type) {
110 return false, echConfig{}, &echConfigErr{"extensions type"}
111 }
112 if !extensions.ReadUint16LengthPrefixed((*cryptobyte.String)(&e.Data)) {
113 return false, echConfig{}, &echConfigErr{"extensions data"}
114 }
115 ec.Extensions = append(ec.Extensions, e)
116 }
117
118 return false, ec, nil
119 }
120
121 // parseECHConfigList parses a draft-ietf-tls-esni-18 ECHConfigList, returning a
122 // slice of parsed ECHConfigs, in the same order they were parsed, or an error
123 // if the list is malformed.
124 func parseECHConfigList(data []byte) ([]echConfig, error) {
125 s := cryptobyte.String(data)
126 var length uint16
127 if !s.ReadUint16(&length) {
128 return nil, errMalformedECHConfigList
129 }
130 if length != uint16(len(data)-2) {
131 return nil, errMalformedECHConfigList
132 }
133 var configs []echConfig
134 for len(s) > 0 {
135 if len(s) < 4 {
136 return nil, errors.New("tls: malformed ECHConfig")
137 }
138 configLen := uint16(s[2])<<8 | uint16(s[3])
139 skip, ec, err := parseECHConfig(s)
140 if err != nil {
141 return nil, err
142 }
143 s = s[configLen+4:]
144 if !skip {
145 configs = append(configs, ec)
146 }
147 }
148 return configs, nil
149 }
150
151 func pickECHConfig(list []echConfig) (*echConfig, hpke.PublicKey, hpke.KDF, hpke.AEAD) {
152 for _, ec := range list {
153 if !validDNSName([]byte(ec.PublicName)) {
154 continue
155 }
156 var unsupportedExt bool
157 for _, ext := range ec.Extensions {
158 // If high order bit is set to 1 the extension is mandatory.
159 // Since we don't support any extensions, if we see a mandatory
160 // bit, we skip the config.
161 if ext.Type&uint16(1<<15) != 0 {
162 unsupportedExt = true
163 }
164 }
165 if unsupportedExt {
166 continue
167 }
168 kem, err := hpke.NewKEM(ec.KemID)
169 if err != nil {
170 continue
171 }
172 pub, err := kem.NewPublicKey(ec.PublicKey)
173 if err != nil {
174 // This is an error in the config, but killing the connection feels
175 // excessive.
176 continue
177 }
178 for _, cs := range ec.SymmetricCipherSuite {
179 // All of the supported AEADs and KDFs are fine, rather than
180 // imposing some sort of preference here, we just pick the first
181 // valid suite.
182 kdf, err := hpke.NewKDF(cs.KDFID)
183 if err != nil {
184 continue
185 }
186 aead, err := hpke.NewAEAD(cs.AEADID)
187 if err != nil {
188 continue
189 }
190 return &ec, pub, kdf, aead
191 }
192 }
193 return nil, nil, nil, nil
194 }
195
196 func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
197 h, err := inner.marshalMsg(true)
198 if err != nil {
199 return nil, err
200 }
201 h = h[4:] // strip four byte prefix
202
203 var paddingLen int
204 if inner.serverName != "" {
205 paddingLen = max(0, maxNameLength-len(inner.serverName))
206 } else {
207 paddingLen = maxNameLength + 9
208 }
209 paddingLen = 31 - ((len(h) + paddingLen - 1) % 32)
210
211 return append(h, []byte{:paddingLen}...), nil
212 }
213
214 func skipUint8LengthPrefixed(s *cryptobyte.String) bool {
215 var skip uint8
216 if !s.ReadUint8(&skip) {
217 return false
218 }
219 return s.Skip(int(skip))
220 }
221
222 func skipUint16LengthPrefixed(s *cryptobyte.String) bool {
223 var skip uint16
224 if !s.ReadUint16(&skip) {
225 return false
226 }
227 return s.Skip(int(skip))
228 }
229
230 type rawExtension struct {
231 extType uint16
232 data []byte
233 }
234
235 func extractRawExtensions(hello *clientHelloMsg) ([]rawExtension, error) {
236 s := cryptobyte.String(hello.original)
237 if !s.Skip(4+2+32) || // header, version, random
238 !skipUint8LengthPrefixed(&s) || // session ID
239 !skipUint16LengthPrefixed(&s) || // cipher suites
240 !skipUint8LengthPrefixed(&s) { // compression methods
241 return nil, errors.New("tls: malformed outer client hello")
242 }
243 var rawExtensions []rawExtension
244 var extensions cryptobyte.String
245 if !s.ReadUint16LengthPrefixed(&extensions) {
246 return nil, errors.New("tls: malformed outer client hello")
247 }
248
249 for !extensions.Empty() {
250 var extension uint16
251 var extData cryptobyte.String
252 if !extensions.ReadUint16(&extension) ||
253 !extensions.ReadUint16LengthPrefixed(&extData) {
254 return nil, errors.New("tls: invalid inner client hello")
255 }
256 rawExtensions = append(rawExtensions, rawExtension{extension, extData})
257 }
258 return rawExtensions, nil
259 }
260
261 func decodeInnerClientHello(outer *clientHelloMsg, encoded []byte) (*clientHelloMsg, error) {
262 // Reconstructing the inner client hello from its encoded form is somewhat
263 // complicated. It is missing its header (message type and length), session
264 // ID, and the extensions may be compressed. Since we need to put the
265 // extensions back in the same order as they were in the raw outer hello,
266 // and since we don't store the raw extensions, or the order we parsed them
267 // in, we need to reparse the raw extensions from the outer hello in order
268 // to properly insert them into the inner hello. This _should_ result in raw
269 // bytes which match the hello as it was generated by the client.
270 innerReader := cryptobyte.String(encoded)
271 var versionAndRandom, sessionID, cipherSuites, compressionMethods []byte
272 var extensions cryptobyte.String
273 if !innerReader.ReadBytes(&versionAndRandom, 2+32) ||
274 !readUint8LengthPrefixed(&innerReader, &sessionID) ||
275 len(sessionID) != 0 ||
276 !readUint16LengthPrefixed(&innerReader, &cipherSuites) ||
277 !readUint8LengthPrefixed(&innerReader, &compressionMethods) ||
278 !innerReader.ReadUint16LengthPrefixed(&extensions) {
279 return nil, errors.New("tls: invalid inner client hello")
280 }
281
282 // The specification says we must verify that the trailing padding is all
283 // zeros. This is kind of weird for TLS messages, where we generally just
284 // throw away any trailing garbage.
285 for _, p := range innerReader {
286 if p != 0 {
287 return nil, errors.New("tls: invalid inner client hello")
288 }
289 }
290
291 rawOuterExts, err := extractRawExtensions(outer)
292 if err != nil {
293 return nil, err
294 }
295
296 recon := cryptobyte.NewBuilder(nil)
297 recon.AddUint8(typeClientHello)
298 recon.AddUint24LengthPrefixed(func(recon *cryptobyte.Builder) {
299 recon.AddBytes(versionAndRandom)
300 recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) {
301 recon.AddBytes(outer.sessionId)
302 })
303 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
304 recon.AddBytes(cipherSuites)
305 })
306 recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) {
307 recon.AddBytes(compressionMethods)
308 })
309 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
310 for !extensions.Empty() {
311 var extension uint16
312 var extData cryptobyte.String
313 if !extensions.ReadUint16(&extension) ||
314 !extensions.ReadUint16LengthPrefixed(&extData) {
315 recon.SetError(errors.New("tls: invalid inner client hello"))
316 return
317 }
318 if extension == extensionECHOuterExtensions {
319 if !extData.ReadUint8LengthPrefixed(&extData) {
320 recon.SetError(errors.New("tls: invalid inner client hello"))
321 return
322 }
323 var i int
324 for !extData.Empty() {
325 var extType uint16
326 if !extData.ReadUint16(&extType) {
327 recon.SetError(errors.New("tls: invalid inner client hello"))
328 return
329 }
330 if extType == extensionEncryptedClientHello {
331 recon.SetError(errors.New("tls: invalid outer extensions"))
332 return
333 }
334 for ; i <= len(rawOuterExts); i++ {
335 if i == len(rawOuterExts) {
336 recon.SetError(errors.New("tls: invalid outer extensions"))
337 return
338 }
339 if rawOuterExts[i].extType == extType {
340 break
341 }
342 }
343 recon.AddUint16(rawOuterExts[i].extType)
344 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
345 recon.AddBytes(rawOuterExts[i].data)
346 })
347 }
348 } else {
349 recon.AddUint16(extension)
350 recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
351 recon.AddBytes(extData)
352 })
353 }
354 }
355 })
356 })
357
358 reconBytes, err := recon.Bytes()
359 if err != nil {
360 return nil, err
361 }
362 inner := &clientHelloMsg{}
363 if !inner.unmarshal(reconBytes) {
364 return nil, errors.New("tls: invalid reconstructed inner client hello")
365 }
366
367 if !bytes.Equal(inner.encryptedClientHello, []byte{uint8(innerECHExt)}) {
368 return nil, errInvalidECHExt
369 }
370
371 hasTLS13 := false
372 for _, v := range inner.supportedVersions {
373 // Skip GREASE values (values of the form 0x?A0A).
374 // GREASE (Generate Random Extensions And Sustain Extensibility) is a mechanism used by
375 // browsers like Chrome to ensure TLS implementations correctly ignore unknown values.
376 // GREASE values follow a specific pattern: 0x?A0A, where ? can be any hex digit.
377 // These values should be ignored when processing supported TLS versions.
378 if v&0x0F0F == 0x0A0A && v&0xff == v>>8 {
379 continue
380 }
381
382 // Ensure at least TLS 1.3 is offered.
383 if v == VersionTLS13 {
384 hasTLS13 = true
385 } else if v < VersionTLS13 {
386 // Reject if any non-GREASE value is below TLS 1.3, as ECH requires TLS 1.3+.
387 return nil, errors.New("tls: client sent encrypted_client_hello extension with unsupported versions")
388 }
389 }
390
391 if !hasTLS13 {
392 return nil, errors.New("tls: client sent encrypted_client_hello extension but did not offer TLS 1.3")
393 }
394
395 return inner, nil
396 }
397
398 func decryptECHPayload(context *hpke.Recipient, hello, payload []byte) ([]byte, error) {
399 outerAAD := bytes.Replace(hello[4:], payload, []byte{:len(payload)}, 1)
400 return context.Open(outerAAD, payload)
401 }
402
403 func generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payload []byte) ([]byte, error) {
404 var b cryptobyte.Builder
405 b.AddUint8(0) // outer
406 b.AddUint16(kdfID)
407 b.AddUint16(aeadID)
408 b.AddUint8(id)
409 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(encodedKey) })
410 b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(payload) })
411 return b.Bytes()
412 }
413
414 func computeAndUpdateOuterECHExtension(outer, inner *clientHelloMsg, ech *echClientContext, useKey bool) error {
415 var encapKey []byte
416 if useKey {
417 encapKey = ech.encapsulatedKey
418 }
419 encodedInner, err := encodeInnerClientHello(inner, int(ech.config.MaxNameLength))
420 if err != nil {
421 return err
422 }
423 // NOTE: the tag lengths for all of the supported AEADs are the same (16
424 // bytes), so we have hardcoded it here. If we add support for another AEAD
425 // with a different tag length, we will need to change this.
426 encryptedLen := len(encodedInner) + 16 // AEAD tag length
427 outer.encryptedClientHello, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, []byte{:encryptedLen})
428 if err != nil {
429 return err
430 }
431 serializedOuter, err := outer.marshal()
432 if err != nil {
433 return err
434 }
435 serializedOuter = serializedOuter[4:] // strip the four byte prefix
436 encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
437 if err != nil {
438 return err
439 }
440 outer.encryptedClientHello, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
441 if err != nil {
442 return err
443 }
444 return nil
445 }
446
447 // validDNSName is a rather rudimentary check for the validity of a DNS name.
448 // This is used to check if the public_name in a ECHConfig is valid when we are
449 // picking a config. This can be somewhat lax because even if we pick a
450 // valid-looking name, the DNS layer will later reject it anyway.
451 func validDNSName(name []byte) bool {
452 if len(name) > 253 {
453 return false
454 }
455 labels := bytes.Split(name, ".")
456 if len(labels) <= 1 {
457 return false
458 }
459 for _, l := range labels {
460 labelLen := len(l)
461 if labelLen == 0 {
462 return false
463 }
464 for i, r := range l {
465 if r == '-' && (i == 0 || i == labelLen-1) {
466 return false
467 }
468 if (r < '0' || r > '9') && (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && r != '-' {
469 return false
470 }
471 }
472 }
473 return true
474 }
475
476 // ECHRejectionError is the error type returned when ECH is rejected by a remote
477 // server. If the server offered a ECHConfigList to use for retries, the
478 // RetryConfigList field will contain this list.
479 //
480 // The client may treat an ECHRejectionError with an empty set of RetryConfigs
481 // as a secure signal from the server.
482 type ECHRejectionError struct {
483 RetryConfigList []byte
484 }
485
486 func (e *ECHRejectionError) Error() string {
487 return "tls: server rejected ECH"
488 }
489
490 var errMalformedECHExt = errors.New("tls: malformed encrypted_client_hello extension")
491 var errInvalidECHExt = errors.New("tls: client sent invalid encrypted_client_hello extension")
492
493 type echExtType uint8
494
495 const (
496 innerECHExt echExtType = 1
497 outerECHExt echExtType = 0
498 )
499
500 func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8, encap []byte, payload []byte, err error) {
501 data := []byte{:len(ext)}
502 copy(data, ext)
503 s := cryptobyte.String(data)
504 var echInt uint8
505 if !s.ReadUint8(&echInt) {
506 err = errMalformedECHExt
507 return
508 }
509 echType = echExtType(echInt)
510 if echType == innerECHExt {
511 if !s.Empty() {
512 err = errMalformedECHExt
513 return
514 }
515 return echType, cs, 0, nil, nil, nil
516 }
517 if echType != outerECHExt {
518 err = errInvalidECHExt
519 return
520 }
521 if !s.ReadUint16(&cs.KDFID) {
522 err = errMalformedECHExt
523 return
524 }
525 if !s.ReadUint16(&cs.AEADID) {
526 err = errMalformedECHExt
527 return
528 }
529 if !s.ReadUint8(&configID) {
530 err = errMalformedECHExt
531 return
532 }
533 if !readUint16LengthPrefixed(&s, &encap) {
534 err = errMalformedECHExt
535 return
536 }
537 if !readUint16LengthPrefixed(&s, &payload) {
538 err = errMalformedECHExt
539 return
540 }
541
542 // NOTE: clone encap and payload so that mutating them does not mutate the
543 // raw extension bytes.
544 return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
545 }
546
547 func (c *Conn) processECHClientHello(outer *clientHelloMsg, echKeys []EncryptedClientHelloKey) (*clientHelloMsg, *echServerContext, error) {
548 echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
549 if err != nil {
550 if errors.Is(err, errInvalidECHExt) {
551 c.sendAlert(alertIllegalParameter)
552 } else {
553 c.sendAlert(alertDecodeError)
554 }
555
556 return nil, nil, errInvalidECHExt
557 }
558
559 if echType == innerECHExt {
560 return outer, &echServerContext{inner: true}, nil
561 }
562
563 if len(echKeys) == 0 {
564 return outer, nil, nil
565 }
566
567 for _, echKey := range echKeys {
568 skip, config, err := parseECHConfig(echKey.Config)
569 if err != nil || skip {
570 c.sendAlert(alertInternalError)
571 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config: %s", err)
572 }
573 if skip {
574 continue
575 }
576 kem, err := hpke.NewKEM(config.KemID)
577 if err != nil {
578 c.sendAlert(alertInternalError)
579 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KEM: %s", err)
580 }
581 echPriv, err := kem.NewPrivateKey(echKey.PrivateKey)
582 if err != nil {
583 c.sendAlert(alertInternalError)
584 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey PrivateKey: %s", err)
585 }
586 kdf, err := hpke.NewKDF(echCiphersuite.KDFID)
587 if err != nil {
588 c.sendAlert(alertInternalError)
589 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config KDF: %s", err)
590 }
591 aead, err := hpke.NewAEAD(echCiphersuite.AEADID)
592 if err != nil {
593 c.sendAlert(alertInternalError)
594 return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKey Config AEAD: %s", err)
595 }
596 info := append([]byte("tls ech\x00"), echKey.Config...)
597 hpkeContext, err := hpke.NewRecipient(encap, echPriv, kdf, aead, info)
598 if err != nil {
599 // attempt next trial decryption
600 continue
601 }
602
603 encodedInner, err := decryptECHPayload(hpkeContext, outer.original, payload)
604 if err != nil {
605 // attempt next trial decryption
606 continue
607 }
608
609 // NOTE: we do not enforce that the sent server_name matches the ECH
610 // configs PublicName, since this is not particularly important, and
611 // the client already had to know what it was in order to properly
612 // encrypt the payload. This is only a MAY in the spec, so we're not
613 // doing anything revolutionary.
614
615 echInner, err := decodeInnerClientHello(outer, encodedInner)
616 if err != nil {
617 c.sendAlert(alertIllegalParameter)
618 return nil, nil, errInvalidECHExt
619 }
620
621 c.echAccepted = true
622
623 return echInner, &echServerContext{
624 hpkeContext: hpkeContext,
625 configID: configID,
626 ciphersuite: echCiphersuite,
627 }, nil
628 }
629
630 return outer, nil, nil
631 }
632
633 func buildRetryConfigList(keys []EncryptedClientHelloKey) ([]byte, error) {
634 var atLeastOneRetryConfig bool
635 var retryBuilder cryptobyte.Builder
636 retryBuilder.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
637 for _, c := range keys {
638 if !c.SendAsRetry {
639 continue
640 }
641 atLeastOneRetryConfig = true
642 b.AddBytes(c.Config)
643 }
644 })
645 if !atLeastOneRetryConfig {
646 return nil, nil
647 }
648 return retryBuilder.Bytes()
649 }
650