package tls
import (
)
var sortedSupportedAEADs []uint16
func () {
for := range hpke.SupportedAEADs {
sortedSupportedAEADs = append(sortedSupportedAEADs, )
}
slices.Sort(sortedSupportedAEADs)
}
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 ( []byte) ( bool, echConfig, error) {
:= cryptobyte.String()
.raw = []byte()
if !.ReadUint16(&.Version) {
return false, echConfig{}, errMalformedECHConfig
}
if !.ReadUint16(&.Length) {
return false, echConfig{}, errMalformedECHConfig
}
if len(.raw) < int(.Length)+4 {
return false, echConfig{}, errMalformedECHConfig
}
.raw = .raw[:.Length+4]
if .Version != extensionEncryptedClientHello {
.Skip(int(.Length))
return true, echConfig{}, nil
}
if !.ReadUint8(&.ConfigID) {
return false, echConfig{}, errMalformedECHConfig
}
if !.ReadUint16(&.KemID) {
return false, echConfig{}, errMalformedECHConfig
}
if !readUint16LengthPrefixed(&, &.PublicKey) {
return false, echConfig{}, errMalformedECHConfig
}
var cryptobyte.String
if !.ReadUint16LengthPrefixed(&) {
return false, echConfig{}, errMalformedECHConfig
}
for !.Empty() {
var echCipher
if !.ReadUint16(&.KDFID) {
return false, echConfig{}, errMalformedECHConfig
}
if !.ReadUint16(&.AEADID) {
return false, echConfig{}, errMalformedECHConfig
}
.SymmetricCipherSuite = append(.SymmetricCipherSuite, )
}
if !.ReadUint8(&.MaxNameLength) {
return false, echConfig{}, errMalformedECHConfig
}
var cryptobyte.String
if !.ReadUint8LengthPrefixed(&) {
return false, echConfig{}, errMalformedECHConfig
}
.PublicName =
var cryptobyte.String
if !.ReadUint16LengthPrefixed(&) {
return false, echConfig{}, errMalformedECHConfig
}
for !.Empty() {
var echExtension
if !.ReadUint16(&.Type) {
return false, echConfig{}, errMalformedECHConfig
}
if !.ReadUint16LengthPrefixed((*cryptobyte.String)(&.Data)) {
return false, echConfig{}, errMalformedECHConfig
}
.Extensions = append(.Extensions, )
}
return false, , nil
}
func ( []byte) ([]echConfig, error) {
:= cryptobyte.String()
var uint16
if !.ReadUint16(&) {
return nil, errMalformedECHConfig
}
if != uint16(len()-2) {
return nil, errMalformedECHConfig
}
var []echConfig
for len() > 0 {
if len() < 4 {
return nil, errors.New("tls: malformed ECHConfig")
}
:= uint16([2])<<8 | uint16([3])
, , := parseECHConfig()
if != nil {
return nil,
}
= [+4:]
if ! {
= append(, )
}
}
return , nil
}
func ( []echConfig) *echConfig {
for , := range {
if , := hpke.SupportedKEMs[.KemID]; ! {
continue
}
var bool
for , := range .SymmetricCipherSuite {
if , := hpke.SupportedAEADs[.AEADID]; ! {
continue
}
if , := hpke.SupportedKDFs[.KDFID]; ! {
continue
}
= true
break
}
if ! {
continue
}
if !validDNSName(string(.PublicName)) {
continue
}
var bool
for , := range .Extensions {
if .Type&uint16(1<<15) != 0 {
= true
}
}
if {
continue
}
return &
}
return nil
}
func ( []echCipher) (echCipher, error) {
for , := range {
if , := hpke.SupportedAEADs[.AEADID]; ! {
continue
}
if , := hpke.SupportedKDFs[.KDFID]; ! {
continue
}
return , nil
}
return echCipher{}, errors.New("tls: no supported symmetric ciphersuites for ECH")
}
func ( *clientHelloMsg, int) ([]byte, error) {
, := .marshalMsg(true)
if != nil {
return nil,
}
= [4:]
var int
if .serverName != "" {
= max(0, -len(.serverName))
} else {
= + 9
}
= 31 - ((len() + - 1) % 32)
return append(, make([]byte, )...), nil
}
func ( *cryptobyte.String) bool {
var uint8
if !.ReadUint8(&) {
return false
}
return .Skip(int())
}
func ( *cryptobyte.String) bool {
var uint16
if !.ReadUint16(&) {
return false
}
return .Skip(int())
}
type rawExtension struct {
extType uint16
data []byte
}
func ( *clientHelloMsg) ([]rawExtension, error) {
:= cryptobyte.String(.original)
if !.Skip(4+2+32) ||
!skipUint8LengthPrefixed(&) ||
!skipUint16LengthPrefixed(&) ||
!skipUint8LengthPrefixed(&) {
return nil, errors.New("tls: malformed outer client hello")
}
var []rawExtension
var cryptobyte.String
if !.ReadUint16LengthPrefixed(&) {
return nil, errors.New("tls: malformed outer client hello")
}
for !.Empty() {
var uint16
var cryptobyte.String
if !.ReadUint16(&) ||
!.ReadUint16LengthPrefixed(&) {
return nil, errors.New("tls: invalid inner client hello")
}
= append(, rawExtension{, })
}
return , nil
}
func ( *clientHelloMsg, []byte) (*clientHelloMsg, error) {
:= cryptobyte.String()
var , , , []byte
var cryptobyte.String
if !.ReadBytes(&, 2+32) ||
!readUint8LengthPrefixed(&, &) ||
len() != 0 ||
!readUint16LengthPrefixed(&, &) ||
!readUint8LengthPrefixed(&, &) ||
!.ReadUint16LengthPrefixed(&) {
return nil, errors.New("tls: invalid inner client hello")
}
for , := range {
if != 0 {
return nil, errors.New("tls: invalid inner client hello")
}
}
, := extractRawExtensions()
if != nil {
return nil,
}
:= cryptobyte.NewBuilder(nil)
.AddUint8(typeClientHello)
.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes()
.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes(.sessionId)
})
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes()
})
.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes()
})
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
for !.Empty() {
var uint16
var cryptobyte.String
if !.ReadUint16(&) ||
!.ReadUint16LengthPrefixed(&) {
.SetError(errors.New("tls: invalid inner client hello"))
return
}
if == extensionECHOuterExtensions {
if !.ReadUint8LengthPrefixed(&) {
.SetError(errors.New("tls: invalid inner client hello"))
return
}
var int
for !.Empty() {
var uint16
if !.ReadUint16(&) {
.SetError(errors.New("tls: invalid inner client hello"))
return
}
if == extensionEncryptedClientHello {
.SetError(errors.New("tls: invalid outer extensions"))
return
}
for ; <= len(); ++ {
if == len() {
.SetError(errors.New("tls: invalid outer extensions"))
return
}
if [].extType == {
break
}
}
.AddUint16([].extType)
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes([].data)
})
}
} else {
.AddUint16()
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
.AddBytes()
})
}
}
})
})
, := .Bytes()
if != nil {
return nil,
}
:= &clientHelloMsg{}
if !.unmarshal() {
return nil, errors.New("tls: invalid reconstructed inner client hello")
}
if !bytes.Equal(.encryptedClientHello, []byte{uint8(innerECHExt)}) {
return nil, errInvalidECHExt
}
:= false
for , := range .supportedVersions {
if &0x0F0F == 0x0A0A && &0xff == >>8 {
continue
}
if == VersionTLS13 {
= true
} else if < VersionTLS13 {
return nil, errors.New("tls: client sent encrypted_client_hello extension with unsupported versions")
}
}
if ! {
return nil, errors.New("tls: client sent encrypted_client_hello extension but did not offer TLS 1.3")
}
return , nil
}
func ( *hpke.Receipient, , []byte) ([]byte, error) {
:= bytes.Replace([4:], , make([]byte, len()), 1)
return .Open(, )
}
func ( uint8, , uint16, []byte, []byte) ([]byte, error) {
var cryptobyte.Builder
.AddUint8(0)
.AddUint16()
.AddUint16()
.AddUint8()
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) { .AddBytes() })
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) { .AddBytes() })
return .Bytes()
}
func (, *clientHelloMsg, *echClientContext, bool) error {
var []byte
if {
= .encapsulatedKey
}
, := encodeInnerClientHello(, int(.config.MaxNameLength))
if != nil {
return
}
:= len() + 16
.encryptedClientHello, = generateOuterECHExt(.config.ConfigID, .kdfID, .aeadID, , make([]byte, ))
if != nil {
return
}
, := .marshal()
if != nil {
return
}
= [4:]
, := .hpkeContext.Seal(, )
if != nil {
return
}
.encryptedClientHello, = generateOuterECHExt(.config.ConfigID, .kdfID, .aeadID, , )
if != nil {
return
}
return nil
}
func ( string) bool {
if len() > 253 {
return false
}
:= strings.Split(, ".")
if len() <= 1 {
return false
}
for , := range {
:= len()
if == 0 {
return false
}
for , := range {
if == '-' && ( == 0 || == -1) {
return false
}
if ( < '0' || > '9') && ( < 'a' || > 'z') && ( < 'A' || > 'Z') && != '-' {
return false
}
}
}
return true
}
type ECHRejectionError struct {
RetryConfigList []byte
}
func ( *ECHRejectionError) () string {
return "tls: server rejected ECH"
}
var errMalformedECHExt = errors.New("tls: malformed encrypted_client_hello extension")
var errInvalidECHExt = errors.New("tls: client sent invalid encrypted_client_hello extension")
type echExtType uint8
const (
innerECHExt echExtType = 1
outerECHExt echExtType = 0
)
func ( []byte) ( echExtType, echCipher, uint8, []byte, []byte, error) {
:= make([]byte, len())
copy(, )
:= cryptobyte.String()
var uint8
if !.ReadUint8(&) {
= errMalformedECHExt
return
}
= echExtType()
if == innerECHExt {
if !.Empty() {
= errMalformedECHExt
return
}
return , , 0, nil, nil, nil
}
if != outerECHExt {
= errInvalidECHExt
return
}
if !.ReadUint16(&.KDFID) {
= errMalformedECHExt
return
}
if !.ReadUint16(&.AEADID) {
= errMalformedECHExt
return
}
if !.ReadUint8(&) {
= errMalformedECHExt
return
}
if !readUint16LengthPrefixed(&, &) {
= errMalformedECHExt
return
}
if !readUint16LengthPrefixed(&, &) {
= errMalformedECHExt
return
}
return , , , bytes.Clone(), bytes.Clone(), nil
}
func ( []EncryptedClientHelloKey) ([]byte, error) {
:= cryptobyte.NewBuilder(nil)
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
for , := range {
.AddBytes(.Config)
}
})
return .Bytes()
}
func ( *Conn) ( *clientHelloMsg) (*clientHelloMsg, *echServerContext, error) {
, , , , , := parseECHExt(.encryptedClientHello)
if != nil {
if errors.Is(, errInvalidECHExt) {
.sendAlert(alertIllegalParameter)
} else {
.sendAlert(alertDecodeError)
}
return nil, nil, errInvalidECHExt
}
if == innerECHExt {
return , &echServerContext{inner: true}, nil
}
if len(.config.EncryptedClientHelloKeys) == 0 {
return , nil, nil
}
for , := range .config.EncryptedClientHelloKeys {
, , := parseECHConfig(.Config)
if != nil || {
.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys Config: %s", )
}
if {
continue
}
, := hpke.ParseHPKEPrivateKey(.KemID, .PrivateKey)
if != nil {
.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys PrivateKey: %s", )
}
:= append([]byte("tls ech\x00"), .Config...)
, := hpke.SetupReceipient(hpke.DHKEM_X25519_HKDF_SHA256, .KDFID, .AEADID, , , )
if != nil {
continue
}
, := decryptECHPayload(, .original, )
if != nil {
continue
}
, := decodeInnerClientHello(, )
if != nil {
.sendAlert(alertIllegalParameter)
return nil, nil, errInvalidECHExt
}
.echAccepted = true
return , &echServerContext{
hpkeContext: ,
configID: ,
ciphersuite: ,
}, nil
}
return , nil, nil
}
func ( []EncryptedClientHelloKey) ([]byte, error) {
var bool
var cryptobyte.Builder
.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
for , := range {
if !.SendAsRetry {
continue
}
= true
.AddBytes(.Config)
}
})
if ! {
return nil, nil
}
return .Bytes()
}