package tls

import (
	
	
	
	
	
	

	
	
	
)

// Unstable API: This is a work in progress and may change in the future. Using
// it in your application may cause your application to break when updating to
// a new version of uTLS.

const (
	OuterClientHello byte = 0x00
	InnerClientHello byte = 0x01
)

type EncryptedClientHelloExtension interface {
	// TLSExtension must be implemented by all EncryptedClientHelloExtension implementations.
	TLSExtension

	// MarshalClientHello is called by (*UConn).MarshalClientHello() when an ECH extension
	// is present to allow the ECH extension to take control of the generation of the
	// entire ClientHello message.
	MarshalClientHello(*UConn) error

	mustEmbedUnimplementedECHExtension()
}

type ECHExtension = EncryptedClientHelloExtension // alias

// type guard: GREASEEncryptedClientHelloExtension must implement EncryptedClientHelloExtension
var (
	_ EncryptedClientHelloExtension = (*GREASEEncryptedClientHelloExtension)(nil)

	_ EncryptedClientHelloExtension = (*UnimplementedECHExtension)(nil)
)

type GREASEEncryptedClientHelloExtension struct {
	CandidateCipherSuites []HPKESymmetricCipherSuite
	cipherSuite           HPKESymmetricCipherSuite // randomly picked from CandidateCipherSuites or generated if empty
	CandidateConfigIds    []uint8
	configId              uint8    // randomly picked from CandidateConfigIds or generated if empty
	EncapsulatedKey       []byte   // if empty, will generate random bytes
	CandidatePayloadLens  []uint16 // Pre-encryption. If 0, will pick 128(+16=144)
	payload               []byte   // payload should be calculated ONCE and stored here, HRR will reuse this

	initOnce sync.Once

	UnimplementedECHExtension
}

type GREASEECHExtension = GREASEEncryptedClientHelloExtension // alias

// init initializes the GREASEEncryptedClientHelloExtension with random values if they are not set.
//
// Based on cloudflare/go's echGenerateGreaseExt()
func ( *GREASEEncryptedClientHelloExtension) () error {
	var  error
	.initOnce.Do(func() {
		// Set the config_id field to a random byte.
		//
		// Note: must not reuse this extension unless for HRR. It is required
		// to generate new random bytes for config_id for each new ClientHello,
		// but reuse the same config_id for HRR.
		if len(.CandidateConfigIds) == 0 {
			var  []byte = make([]byte, 1)
			,  := rand.Read([:])
			if  != nil {
				 = fmt.Errorf("error generating random byte for config_id: %w", )
				return
			}
			.configId = [0]
		} else {
			// randomly pick one from the list
			,  := rand.Int(rand.Reader, big.NewInt(int64(len(.CandidateConfigIds))))
			if  != nil {
				 = fmt.Errorf("error generating random index for config_id: %w", )
				return
			}
			.configId = .CandidateConfigIds[.Int64()]
		}

		// Set the cipher_suite field to a supported HpkeSymmetricCipherSuite.
		// The selection SHOULD vary to exercise all supported configurations,
		// but MAY be held constant for successive connections to the same server
		// in the same session.
		if len(.CandidateCipherSuites) == 0 {
			.cipherSuite = HPKESymmetricCipherSuite{uint16(defaultHpkeKdf), uint16(defaultHpkeAead)}
		} else {
			// randomly pick one from the list
			,  := rand.Int(rand.Reader, big.NewInt(int64(len(.CandidateCipherSuites))))
			if  != nil {
				 = fmt.Errorf("error generating random index for cipher_suite: %w", )
				return
			}
			.cipherSuite = HPKESymmetricCipherSuite{
				.CandidateCipherSuites[.Int64()].KdfId,
				.CandidateCipherSuites[.Int64()].AeadId,
			}
			// aead = hpke.AEAD(g.cipherSuite.AeadId)
		}

		if len(.EncapsulatedKey) == 0 {
			 := uint16(defaultHpkeKem)

			,  := hpke.ParseHPKEPublicKey(uint16(), dummyX25519PublicKey)
			if  != nil {
				 = fmt.Errorf("tls: grease ech: failed to parse dummy public key: %w", )
				return
			}
			 := echCipher{
				KDFID:  defaultHpkeKdf,
				AEADID: defaultHpkeAead,
			}
			.EncapsulatedKey, _,  = hpke.SetupSender(, .KDFID, .AEADID, , []byte{})
			if  != nil {
				 = fmt.Errorf("tls: grease ech: failed to setup encapsulated key: %w", )
				return
			}
		}

		if len(.payload) == 0 {
			if len(.CandidatePayloadLens) == 0 {
				.CandidatePayloadLens = []uint16{128}
			}

			// randomly pick one from the list
			,  := rand.Int(rand.Reader, big.NewInt(int64(len(.CandidatePayloadLens))))
			if  != nil {
				 = fmt.Errorf("error generating random index for payload length: %w", )
				return
			}

			 = .randomizePayload(.CandidatePayloadLens[.Int64()])
		}
	})

	return 
}

func ( *GREASEEncryptedClientHelloExtension) ( uint16) error {
	if len(.payload) != 0 {
		return errors.New("tls: grease ech: regenerating payload is forbidden")
	}

	.payload = make([]byte, cipherLen(.cipherSuite.AeadId, int()))
	,  := rand.Read(.payload)
	if  != nil {
		return fmt.Errorf("tls: generating grease ech payload: %w", )
	}
	return nil
}

// writeToUConn implements TLSExtension.
//
// For ECH extensions, writeToUConn simply points the ech field in UConn to the extension.
func ( *GREASEEncryptedClientHelloExtension) ( *UConn) error {
	.ech = 
	return .MarshalClientHelloNoECH()
}

// Len implements TLSExtension.
func ( *GREASEEncryptedClientHelloExtension) () int {
	.init()
	return 2 + 2 + 1 /* ClientHello Type */ + 4 /* CipherSuite */ + 1 /* Config ID */ + 2 + len(.EncapsulatedKey) + 2 + len(.payload)
}

// Read implements TLSExtension.
func ( *GREASEEncryptedClientHelloExtension) ( []byte) (int, error) {
	if len() < .Len() {
		return 0, io.ErrShortBuffer
	}

	[0] = byte(utlsExtensionECH >> 8)
	[1] = byte(utlsExtensionECH & 0xFF)
	[2] = byte((.Len() - 4) >> 8)
	[3] = byte((.Len() - 4) & 0xFF)
	[4] = OuterClientHello
	[5] = byte(.cipherSuite.KdfId >> 8)
	[6] = byte(.cipherSuite.KdfId & 0xFF)
	[7] = byte(.cipherSuite.AeadId >> 8)
	[8] = byte(.cipherSuite.AeadId & 0xFF)
	[9] = .configId
	[10] = byte(len(.EncapsulatedKey) >> 8)
	[11] = byte(len(.EncapsulatedKey) & 0xFF)
	copy([12:], .EncapsulatedKey)
	[12+len(.EncapsulatedKey)] = byte(len(.payload) >> 8)
	[12+len(.EncapsulatedKey)+1] = byte(len(.payload) & 0xFF)
	copy([12+len(.EncapsulatedKey)+2:], .payload)

	return .Len(), io.EOF
}

// MarshalClientHello implements EncryptedClientHelloExtension.
func (*GREASEEncryptedClientHelloExtension) (*UConn) error {
	return errors.New("tls: grease ech: MarshalClientHello() is not implemented, use (*UConn).MarshalClientHello() instead")
}

// Write implements TLSExtensionWriter.
func ( *GREASEEncryptedClientHelloExtension) ( []byte) (int, error) {
	 := len()
	 := cryptobyte.String()

	// Check the extension type, it must be OuterClientHello otherwise we are not
	// parsing the correct extension
	var  uint8 // 0: outer, 1: inner
	var  cryptobyte.String
	if !.ReadUint8(&) ||  != 0 {
		return , errors.New("bad Client Hello type, expected 0, got " + fmt.Sprintf("%d", ))
	}

	// Parse the cipher suite
	if !.ReadUint16(&.cipherSuite.KdfId) || !.ReadUint16(&.cipherSuite.AeadId) {
		return , errors.New("bad cipher suite")
	}
	if .cipherSuite.KdfId != dicttls.HKDF_SHA256 &&
		.cipherSuite.KdfId != dicttls.HKDF_SHA384 &&
		.cipherSuite.KdfId != dicttls.HKDF_SHA512 {
		return , errors.New("bad KDF ID: " + fmt.Sprintf("%d", .cipherSuite.KdfId))
	}
	if .cipherSuite.AeadId != dicttls.AEAD_AES_128_GCM &&
		.cipherSuite.AeadId != dicttls.AEAD_AES_256_GCM &&
		.cipherSuite.AeadId != dicttls.AEAD_CHACHA20_POLY1305 {
		return , errors.New("bad AEAD ID: " + fmt.Sprintf("%d", .cipherSuite.AeadId))
	}
	.CandidateCipherSuites = []HPKESymmetricCipherSuite{.cipherSuite}

	// GREASE the ConfigId
	if !.ReadUint8(&.configId) {
		return , errors.New("bad config ID")
	}
	// we don't write to CandidateConfigIds because we don't really want to reuse the same config_id

	// GREASE the EncapsulatedKey
	if !.ReadUint16LengthPrefixed(&) {
		return , errors.New("bad encapsulated key")
	}
	.EncapsulatedKey = make([]byte, len())
	,  := rand.Read(.EncapsulatedKey)
	if  != nil {
		return , fmt.Errorf("tls: generating grease ech encapsulated key: %w", )
	}
	if  != len(.EncapsulatedKey) {
		return , fmt.Errorf("tls: generating grease ech encapsulated key: short read for %d bytes", len()-)
	}

	// GREASE the payload
	if !.ReadUint16LengthPrefixed(&) {
		return , errors.New("bad payload")
	}
	.CandidatePayloadLens = []uint16{uint16(len() - cipherLen(.cipherSuite.AeadId, 0))}

	return , nil
}

// UnimplementedECHExtension is a placeholder for an ECH extension that is not implemented.
// All implementations of EncryptedClientHelloExtension should embed this struct to ensure
// forward compatibility.
type UnimplementedECHExtension struct{}

// writeToUConn implements TLSExtension.
func (*UnimplementedECHExtension) ( *UConn) error {
	return errors.New("tls: unimplemented ECHExtension")
}

// Len implements TLSExtension.
func (*UnimplementedECHExtension) () int {
	return 0
}

// Read implements TLSExtension.
func (*UnimplementedECHExtension) ( []byte) (int, error) {
	return 0, errors.New("tls: unimplemented ECHExtension")
}

// MarshalClientHello implements EncryptedClientHelloExtension.
func (*UnimplementedECHExtension) (*UConn) error {
	return errors.New("tls: unimplemented ECHExtension")
}

// mustEmbedUnimplementedECHExtension is a noop function but is required to
// ensure forward compatibility.
func (*UnimplementedECHExtension) () {
	panic("mustEmbedUnimplementedECHExtension() is not implemented")
}

// BoringGREASEECH returns a GREASE scheme BoringSSL uses by default.
func () *GREASEEncryptedClientHelloExtension {
	return &GREASEEncryptedClientHelloExtension{
		CandidateCipherSuites: []HPKESymmetricCipherSuite{
			{
				KdfId:  dicttls.HKDF_SHA256,
				AeadId: dicttls.AEAD_AES_128_GCM,
			},
		},
		CandidatePayloadLens: []uint16{128, 160, 192, 224}, // +16: 144, 176, 208, 240
	}
}