Source file src/crypto/tls/ech.go

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

View as plain text