package  tlsimport  (	"crypto/internal/hpke" 	"errors" 	"strings" 	"golang.org/x/crypto/cryptobyte" )type  echCipher  struct  {	KDFID   uint16 	AEADID  uint16 }type  echExtension  struct  {	Type  uint16 	Data  []byte }type  echConfig  struct  {	raw  []byte 	Version  uint16 	Length   uint16 	ConfigID              uint8 	KemID                 uint16 	PublicKey             []byte 	SymmetricCipherSuite  []echCipher 	MaxNameLength  uint8 	PublicName     []byte 	Extensions     []echExtension }var  errMalformedECHConfig  = errors .New ("tls: malformed ECHConfigList" )func  parseECHConfigList data  []byte ) ([]echConfig , error ) {	s  := cryptobyte .String (data )		var  length  uint16 	if  !s .ReadUint16 (&length ) {		return  nil , errMalformedECHConfig 	}	if  length  != uint16 (len (data )-2 ) {		return  nil , errMalformedECHConfig 	}	var  configs  []echConfig 	for  len (s ) > 0  {		var  ec  echConfig 		ec .raw  = []byte (s )		if  !s .ReadUint16 (&ec .Version ) {			return  nil , errMalformedECHConfig 		}		if  !s .ReadUint16 (&ec .Length ) {			return  nil , errMalformedECHConfig 		}		if  len (ec .raw ) < int (ec .Length )+4  {			return  nil , errMalformedECHConfig 		}		ec .raw  = ec .raw [:ec .Length +4 ]		if  ec .Version  != extensionEncryptedClientHello  {			s .Skip (int (ec .Length ))			continue 		}		if  !s .ReadUint8 (&ec .ConfigID ) {			return  nil , errMalformedECHConfig 		}		if  !s .ReadUint16 (&ec .KemID ) {			return  nil , errMalformedECHConfig 		}		if  !s .ReadUint16LengthPrefixed ((*cryptobyte .String )(&ec .PublicKey )) {			return  nil , errMalformedECHConfig 		}		var  cipherSuites  cryptobyte .String 		if  !s .ReadUint16LengthPrefixed (&cipherSuites ) {			return  nil , errMalformedECHConfig 		}		for  !cipherSuites .Empty () {			var  c  echCipher 			if  !cipherSuites .ReadUint16 (&c .KDFID ) {				return  nil , errMalformedECHConfig 			}			if  !cipherSuites .ReadUint16 (&c .AEADID ) {				return  nil , errMalformedECHConfig 			}			ec .SymmetricCipherSuite  = append (ec .SymmetricCipherSuite , c )		}		if  !s .ReadUint8 (&ec .MaxNameLength ) {			return  nil , errMalformedECHConfig 		}		var  publicName  cryptobyte .String 		if  !s .ReadUint8LengthPrefixed (&publicName ) {			return  nil , errMalformedECHConfig 		}		ec .PublicName  = publicName 		var  extensions  cryptobyte .String 		if  !s .ReadUint16LengthPrefixed (&extensions ) {			return  nil , errMalformedECHConfig 		}		for  !extensions .Empty () {			var  e  echExtension 			if  !extensions .ReadUint16 (&e .Type ) {				return  nil , errMalformedECHConfig 			}			if  !extensions .ReadUint16LengthPrefixed ((*cryptobyte .String )(&e .Data )) {				return  nil , errMalformedECHConfig 			}			ec .Extensions  = append (ec .Extensions , e )		}		configs  = append (configs , ec )	}	return  configs , nil }func  pickECHConfig list  []echConfig ) *echConfig  {	for  _ , ec  := range  list  {		if  _ , ok  := hpke .SupportedKEMs [ec .KemID ]; !ok  {			continue 		}		var  validSCS  bool 		for  _ , cs  := range  ec .SymmetricCipherSuite  {			if  _ , ok  := hpke .SupportedAEADs [cs .AEADID ]; !ok  {				continue 			}			if  _ , ok  := hpke .SupportedKDFs [cs .KDFID ]; !ok  {				continue 			}			validSCS  = true 			break 		}		if  !validSCS  {			continue 		}		if  !validDNSName (string (ec .PublicName )) {			continue 		}		var  unsupportedExt  bool 		for  _ , ext  := range  ec .Extensions  {						if  ext .Type &uint16 (1 <<15 ) != 0  {				unsupportedExt  = true 			}		}		if  unsupportedExt  {			continue 		}		return  &ec 	}	return  nil }func  pickECHCipherSuite suites  []echCipher ) (echCipher , error ) {	for  _ , s  := range  suites  {				if  _ , ok  := hpke .SupportedAEADs [s .AEADID ]; !ok  {			continue 		}		if  _ , ok  := hpke .SupportedKDFs [s .KDFID ]; !ok  {			continue 		}		return  s , nil 	}	return  echCipher {}, errors .New ("tls: no supported symmetric ciphersuites for ECH" )}func  encodeInnerClientHello inner  *clientHelloMsg , maxNameLength  int ) ([]byte , error ) {	h , err  := inner .marshalMsg (true )	if  err  != nil  {		return  nil , err 	}	h  = h [4 :] 	var  paddingLen  int 	if  inner .serverName  != ""  {		paddingLen  = max (0 , maxNameLength -len (inner .serverName ))	} else  {		paddingLen  = maxNameLength  + 9 	}	paddingLen  = 31  - ((len (h ) + paddingLen  - 1 ) % 32 )	return  append (h , make ([]byte , paddingLen )...), nil }func  generateOuterECHExt id  uint8 , kdfID , aeadID  uint16 , encodedKey  []byte , payload  []byte ) ([]byte , error ) {	var  b  cryptobyte .Builder 	b .AddUint8 (0 ) 	b .AddUint16 (kdfID )	b .AddUint16 (aeadID )	b .AddUint8 (id )	b .AddUint16LengthPrefixed (func (b  *cryptobyte .Builder ) { b .AddBytes (encodedKey ) })	b .AddUint16LengthPrefixed (func (b  *cryptobyte .Builder ) { b .AddBytes (payload ) })	return  b .Bytes ()}func  computeAndUpdateOuterECHExtension outer , inner  *clientHelloMsg , ech  *echContext , useKey  bool ) error  {	var  encapKey  []byte 	if  useKey  {		encapKey  = ech .encapsulatedKey 	}	encodedInner , err  := encodeInnerClientHello (inner , int (ech .config .MaxNameLength ))	if  err  != nil  {		return  err 	}		encryptedLen  := len (encodedInner ) + 16  	outer .encryptedClientHello , err  = generateOuterECHExt (ech .config .ConfigID , ech .kdfID , ech .aeadID , encapKey , make ([]byte , encryptedLen ))	if  err  != nil  {		return  err 	}	serializedOuter , err  := outer .marshal ()	if  err  != nil  {		return  err 	}	serializedOuter  = serializedOuter [4 :] 	encryptedInner , err  := ech .hpkeContext .Seal (serializedOuter , encodedInner )	if  err  != nil  {		return  err 	}	outer .encryptedClientHello , err  = generateOuterECHExt (ech .config .ConfigID , ech .kdfID , ech .aeadID , encapKey , encryptedInner )	if  err  != nil  {		return  err 	}	return  nil }func  validDNSName name  string ) bool  {	if  len (name ) > 253  {		return  false 	}	labels  := strings .Split (name , "." )	if  len (labels ) <= 1  {		return  false 	}	for  _ , l  := range  labels  {		labelLen  := len (l )		if  labelLen  == 0  {			return  false 		}		for  i , r  := range  l  {			if  r  == '-'  && (i  == 0  || i  == labelLen -1 ) {				return  false 			}			if  (r  < '0'  || r  > '9' ) && (r  < 'a'  || r  > 'z' ) && (r  < 'A'  || r  > 'Z' ) && r  != '-'  {				return  false 			}		}	}	return  true }type  ECHRejectionError  struct  {	RetryConfigList  []byte }func  (e  *ECHRejectionError ) Error string  {	return  "tls: server rejected ECH" } The pages are generated with Golds v0.7.6 . (GOOS=linux GOARCH=amd64)
Golds  is a Go 101  project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101  (reachable from the left QR code) to get the latest news of Golds .