// Copyright 2022 uTLS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
	
	
	
	
	
	

	
	
	
	
	
)

// This function is called by (*clientHandshakeStateTLS13).readServerCertificate()
// to retrieve the certificate out of a message read by (*Conn).readHandshake()
func ( *clientHandshakeStateTLS13) ( any) ( any,  error) {
	for ,  := range .uconn.Extensions {
		switch .(type) {
		case *UtlsCompressCertExtension:
			// Included Compressed Certificate extension
			if len(.uconn.certCompressionAlgs) > 0 {
				,  := .(*utlsCompressedCertificateMsg)
				if  {
					if  = transcriptMsg(, .transcript);  != nil {
						return nil, 
					}
					,  = .decompressCert(*)
					if  != nil {
						return nil, fmt.Errorf("tls: failed to decompress certificate message: %w", )
					} else {
						return , nil
					}
				}
			}
		default:
			continue
		}
	}
	return nil, nil
}

// called by (*clientHandshakeStateTLS13).utlsReadServerCertificate() when UtlsCompressCertExtension is used
func ( *clientHandshakeStateTLS13) ( utlsCompressedCertificateMsg) (*certificateMsgTLS13, error) {
	var (
		 io.Reader
		   = bytes.NewReader(.compressedCertificateMessage)
		            = .c
	)

	// Check to see if the peer responded with an algorithm we advertised.
	 := false
	for ,  := range .uconn.certCompressionAlgs {
		if .algorithm == uint16() {
			 = true
		}
	}
	if ! {
		.sendAlert(alertBadCertificate)
		return nil, fmt.Errorf("unadvertised algorithm (%d)", .algorithm)
	}

	switch CertCompressionAlgo(.algorithm) {
	case CertCompressionBrotli:
		 = brotli.NewReader()

	case CertCompressionZlib:
		,  := zlib.NewReader()
		if  != nil {
			.sendAlert(alertBadCertificate)
			return nil, fmt.Errorf("failed to open zlib reader: %w", )
		}
		defer .Close()
		 = 

	case CertCompressionZstd:
		,  := zstd.NewReader()
		if  != nil {
			.sendAlert(alertBadCertificate)
			return nil, fmt.Errorf("failed to open zstd reader: %w", )
		}
		defer .Close()
		 = 

	default:
		.sendAlert(alertBadCertificate)
		return nil, fmt.Errorf("unsupported algorithm (%d)", .algorithm)
	}

	 := make([]byte, .uncompressedLength+4) // +4 for message type and uint24 length field
	[0] = typeCertificate
	[1] = uint8(.uncompressedLength >> 16)
	[2] = uint8(.uncompressedLength >> 8)
	[3] = uint8(.uncompressedLength)

	,  := .Read([4:])
	if  != nil && !errors.Is(, io.EOF) {
		.sendAlert(alertBadCertificate)
		return nil, 
	}
	if  < len()-4 {
		// If, after decompression, the specified length does not match the actual length, the party
		// receiving the invalid message MUST abort the connection with the "bad_certificate" alert.
		// https://datatracker.ietf.org/doc/html/rfc8879#section-4
		.sendAlert(alertBadCertificate)
		return nil, fmt.Errorf("decompressed len (%d) does not match specified len (%d)", , .uncompressedLength)
	}
	 := new(certificateMsgTLS13)
	if !.unmarshal() {
		return nil, .sendAlert(alertUnexpectedMessage)
	}
	return , nil
}

// to be called in (*clientHandshakeStateTLS13).handshake(),
// after hs.readServerFinished() and before hs.sendClientCertificate()
func ( *clientHandshakeStateTLS13) () error {
	if  := .sendClientEncryptedExtensions();  != nil {
		return 
	}
	return nil
}

func ( *clientHandshakeStateTLS13) () error {
	 := .c
	 := new(utlsClientEncryptedExtensionsMsg)
	if .utls.applicationSettingsCodepoint != 0 {
		.applicationSettingsCodepoint = .utls.applicationSettingsCodepoint
		.applicationSettings = .utls.localApplicationSettings
		if ,  := .writeHandshakeRecord(, .transcript);  != nil {
			return 
		}
	}

	return nil
}

func ( *clientHandshakeStateTLS13) ( *encryptedExtensionsMsg) error {
	.c.utls.peerApplicationSettings = .utls.applicationSettings
	.c.utls.applicationSettingsCodepoint = .utls.applicationSettingsCodepoint

	if .c.utls.applicationSettingsCodepoint != 0 {
		if .uconn.vers < VersionTLS13 {
			return errors.New("tls: server sent application settings at invalid version")
		}
		if len(.uconn.clientProtocol) == 0 {
			return errors.New("tls: server sent application settings without ALPN")
		}

		// Check if the ALPN selected by the server exists in the client's list.
		if ,  := .uconn.config.ApplicationSettings[.serverHello.alpnProtocol];  {
			.c.utls.localApplicationSettings = 
		} else {
			// return errors.New("tls: server selected ALPN doesn't match a client ALPS")
			return nil // ignore if client doesn't have ALPS in use.
			// TODO: is this a issue or not?
		}
	}

	return nil
}

func ( *Conn) () (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
	 := .config

	// [UTLS SECTION START]
	if len(.ServerName) == 0 && !.InsecureSkipVerify && len(.InsecureServerNameToVerify) == 0 {
		return nil, nil, nil, errors.New("tls: at least one of ServerName, InsecureSkipVerify or InsecureServerNameToVerify must be specified in the tls.Config")
	}
	// [UTLS SECTION END]

	 := 0
	for ,  := range .NextProtos {
		if  := len();  == 0 ||  > 255 {
			return nil, nil, nil, errors.New("tls: invalid NextProtos value")
		} else {
			 += 1 + 
		}
	}
	if  > 0xffff {
		return nil, nil, nil, errors.New("tls: NextProtos values too large")
	}

	 := .supportedVersions(roleClient)
	if len() == 0 {
		return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
	}
	 := .maxSupportedVersion(roleClient)

	 := &clientHelloMsg{
		vers:                         ,
		compressionMethods:           []uint8{compressionNone},
		random:                       make([]byte, 32),
		extendedMasterSecret:         true,
		ocspStapling:                 true,
		scts:                         true,
		serverName:                   hostnameInSNI(.ServerName),
		supportedCurves:              .curvePreferences(),
		supportedPoints:              []uint8{pointFormatUncompressed},
		secureRenegotiationSupported: true,
		alpnProtocols:                .NextProtos,
		supportedVersions:            ,
	}

	// The version at the beginning of the ClientHello was capped at TLS 1.2
	// for compatibility reasons. The supported_versions extension is used
	// to negotiate versions now. See RFC 8446, Section 4.2.1.
	if .vers > VersionTLS12 {
		.vers = VersionTLS12
	}

	if .handshakes > 0 {
		.secureRenegotiation = .clientFinished[:]
	}

	 := cipherSuitesPreferenceOrder
	if !hasAESGCMHardwareSupport {
		 = cipherSuitesPreferenceOrderNoAES
	}
	 := .cipherSuites()
	.cipherSuites = make([]uint16, 0, len())

	for ,  := range  {
		 := mutualCipherSuite(, )
		if  == nil {
			continue
		}
		// Don't advertise TLS 1.2-only cipher suites unless
		// we're attempting TLS 1.2.
		if  < VersionTLS12 && .flags&suiteTLS12 != 0 {
			continue
		}
		.cipherSuites = append(.cipherSuites, )
	}

	,  := io.ReadFull(.rand(), .random)
	if  != nil {
		return nil, nil, nil, errors.New("tls: short read from Rand: " + .Error())
	}

	// A random session ID is used to detect when the server accepted a ticket
	// and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
	// a compatibility measure (see RFC 8446, Section 4.1.2).
	//
	// The session ID is not set for QUIC connections (see RFC 9001, Section 8.4).
	if .quic == nil {
		.sessionId = make([]byte, 32)
		if ,  := io.ReadFull(.rand(), .sessionId);  != nil {
			return nil, nil, nil, errors.New("tls: short read from Rand: " + .Error())
		}
	}

	if  >= VersionTLS12 {
		.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
	}
	if testingOnlyForceClientHelloSignatureAlgorithms != nil {
		.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
	}

	var  *keySharePrivateKeys
	if .supportedVersions[0] == VersionTLS13 {
		// Reset the list of ciphers when the client only supports TLS 1.3.
		if len(.supportedVersions) == 1 {
			.cipherSuites = nil
		}
		if fips140tls.Required() {
			.cipherSuites = append(.cipherSuites, defaultCipherSuitesTLS13FIPS...)
		} else if hasAESGCMHardwareSupport {
			.cipherSuites = append(.cipherSuites, defaultCipherSuitesTLS13...)
		} else {
			.cipherSuites = append(.cipherSuites, defaultCipherSuitesTLS13NoAES...)
		}

		if len(.supportedCurves) == 0 {
			return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE")
		}
		// curveID := hello.supportedCurves[0]
		// keyShareKeys = &keySharePrivateKeys{curveID: curveID}
		// // Note that if X25519MLKEM768 is supported, it will be first because
		// // the preference order is fixed.
		// if curveID == X25519MLKEM768 {
		// 	keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519)
		// 	if err != nil {
		// 		return nil, nil, nil, err
		// 	}
		// 	seed := make([]byte, mlkem.SeedSize)
		// 	if _, err := io.ReadFull(config.rand(), seed); err != nil {
		// 		return nil, nil, nil, err
		// 	}
		// 	keyShareKeys.mlkem, err = mlkem.NewDecapsulationKey768(seed)
		// 	if err != nil {
		// 		return nil, nil, nil, err
		// 	}
		// 	mlkemEncapsulationKey := keyShareKeys.mlkem.EncapsulationKey().Bytes()
		// 	x25519EphemeralKey := keyShareKeys.ecdhe.PublicKey().Bytes()
		// 	hello.keyShares = []keyShare{
		// 		{group: X25519MLKEM768, data: append(mlkemEncapsulationKey, x25519EphemeralKey...)},
		// 	}
		// 	// If both X25519MLKEM768 and X25519 are supported, we send both key
		// 	// shares (as a fallback) and we reuse the same X25519 ephemeral
		// 	// key, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2.
		// 	if slices.Contains(hello.supportedCurves, X25519) {
		// 		hello.keyShares = append(hello.keyShares, keyShare{group: X25519, data: x25519EphemeralKey})
		// 	}
		// } else {
		// 	if _, ok := curveForCurveID(curveID); !ok {
		// 		return nil, nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
		// 	}
		// 	keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), curveID)
		// 	if err != nil {
		// 		return nil, nil, nil, err
		// 	}
		// 	hello.keyShares = []keyShare{{group: curveID, data: keyShareKeys.ecdhe.PublicKey().Bytes()}}
		// }
	}

	// [UTLS] We don't need this, since it is not ready yet
	// if c.quic != nil {
	// 	p, err := c.quicGetTransportParameters()
	// 	if err != nil {
	// 		return nil, nil, nil, err
	// 	}
	// 	if p == nil {
	// 		p = []byte{}
	// 	}
	// 	hello.quicTransportParameters = p
	// }

	var  *echClientContext
	if .config.EncryptedClientHelloConfigList != nil {
		if .config.MinVersion != 0 && .config.MinVersion < VersionTLS13 {
			return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
		}
		if .config.MaxVersion != 0 && .config.MaxVersion <= VersionTLS12 {
			return nil, nil, nil, errors.New("tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
		}
		,  := parseECHConfigList(.config.EncryptedClientHelloConfigList)
		if  != nil {
			return nil, nil, nil, 
		}
		 := pickECHConfig()
		if  == nil {
			return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs")
		}
		 = &echClientContext{config: }
		.encryptedClientHello = []byte{1} // indicate inner hello
		// We need to explicitly set these 1.2 fields to nil, as we do not
		// marshal them when encoding the inner hello, otherwise transcripts
		// will later mismatch.
		.supportedPoints = nil
		.ticketSupported = false
		.secureRenegotiationSupported = false
		.extendedMasterSecret = false

		,  := hpke.ParseHPKEPublicKey(.config.KemID, .config.PublicKey)
		if  != nil {
			return nil, nil, nil, 
		}
		,  := pickECHCipherSuite(.config.SymmetricCipherSuite)
		if  != nil {
			return nil, nil, nil, 
		}
		.kdfID, .aeadID = .KDFID, .AEADID
		 := append([]byte("tls ech\x00"), .config.raw...)
		.encapsulatedKey, .hpkeContext,  = hpke.SetupSender(.config.KemID, .KDFID, .AEADID, , )
		if  != nil {
			return nil, nil, nil, 
		}
	}

	return , , , nil
}

// clientHandshakeWithOneState checks that exactly one expected state is set (1.2 or 1.3)
// and performs client TLS handshake with that state
func ( *UConn) ( context.Context) ( error) {
	// [uTLS section begins]
	 := .HandshakeState.Hello.getPrivatePtr()
	 := .echCtx
	defer func() { .HandshakeState.Hello = .getPublicPtr() }()

	 := .utls.sessionController.isSessionLocked()

	// after this point exactly 1 out of 2 HandshakeState pointers is non-nil,
	// useTLS13 variable tells which pointer
	// [uTLS section ends]

	if .config == nil {
		.config = defaultConfig()
	}

	// This may be a renegotiation handshake, in which case some fields
	// need to be reset.
	.didResume = false

	// [uTLS section begins]
	// don't make new ClientHello, use hs.hello
	// preserve the checks from beginning and end of makeClientHello()
	if len(.config.ServerName) == 0 && !.config.InsecureSkipVerify && len(.config.InsecureServerNameToVerify) == 0 {
		return errors.New("tls: at least one of ServerName, InsecureSkipVerify or InsecureServerNameToVerify must be specified in the tls.Config")
	}

	 := 0
	for ,  := range .config.NextProtos {
		if  := len();  == 0 ||  > 255 {
			return errors.New("tls: invalid NextProtos value")
		} else {
			 += 1 + 
		}
	}

	if  > 0xffff {
		return errors.New("tls: NextProtos values too large")
	}

	if .handshakes > 0 {
		.secureRenegotiation = .clientFinished[:]
	}

	var (
		     *SessionState
		 *tls13.EarlySecret
		   []byte
	)
	if ! {
		// [uTLS section ends]

		, , ,  = .loadSession()

		// [uTLS section start]
	} else {
		 = .HandshakeState.Session

		if .HandshakeState.State13.EarlySecret != nil &&  != nil {
			 := cipherSuiteTLS13ByID(.cipherSuite)
			 = tls13.NewEarlySecretFromSecret(.hash.New, .HandshakeState.State13.EarlySecret)
		}

		 = .HandshakeState.State13.BinderKey
	}
	// [uTLS section ends]
	if  != nil {
		return 
	}
	if  != nil {
		defer func() {
			// If we got a handshake failure when resuming a session, throw away
			// the session ticket. See RFC 5077, Section 3.2.
			//
			// RFC 8446 makes no mention of dropping tickets on failure, but it
			// does require servers to abort on invalid binders, so we need to
			// delete tickets to recover from a corrupted PSK.
			if  != nil {
				if  := .clientSessionCacheKey();  != "" {
					.config.ClientSessionCache.Put(, nil)
				}
			}
		}()
	}

	if  != nil && .clientHelloBuildStatus != BuildByUtls {
		// Split hello into inner and outer
		.innerHello = .clone()

		// Overwrite the server name in the outer hello with the public facing
		// name.
		.serverName = string(.config.PublicName)
		// Generate a new random for the outer hello.
		.random = make([]byte, 32)
		_,  = io.ReadFull(.config.rand(), .random)
		if  != nil {
			return errors.New("tls: short read from Rand: " + .Error())
		}

		// NOTE: we don't do PSK GREASE, in line with boringssl, it's meant to
		// work around _possibly_ broken middleboxes, but there is little-to-no
		// evidence that this is actually a problem.

		if  := computeAndUpdateOuterECHExtension(, .innerHello, , true);  != nil {
			return 
		}
	}

	.serverName = .serverName

	if ,  := .writeHandshakeRecord(, nil);  != nil {
		return 
	}

	if .earlyData {
		 := cipherSuiteTLS13ByID(.cipherSuite)
		 := .hash.New()
		if  := transcriptMsg(, );  != nil {
			return 
		}
		 := .ClientEarlyTrafficSecret()
		.quicSetWriteSecret(QUICEncryptionLevelEarly, .id, )
	}

	// serverHelloMsg is not included in the transcript
	,  := .readHandshake(nil)
	if  != nil {
		return 
	}

	,  := .(*serverHelloMsg)
	if ! {
		.sendAlert(alertUnexpectedMessage)
		return unexpectedMessageError(, )
	}

	if  := .pickTLSVersion();  != nil {
		return 
	}

	// If we are negotiating a protocol version that's lower than what we
	// support, check for the server downgrade canaries.
	// See RFC 8446, Section 4.1.3.
	 := .config.maxSupportedVersion(roleClient)
	 := string(.random[24:]) == downgradeCanaryTLS12
	 := string(.random[24:]) == downgradeCanaryTLS11
	if  == VersionTLS13 && .vers <= VersionTLS12 && ( || ) ||
		 == VersionTLS12 && .vers <= VersionTLS11 &&  {
		.sendAlert(alertIllegalParameter)
		return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
	}

	// uTLS: do not create new handshakeState, use existing one
	.HandshakeState.ServerHello = .getPublicPtr()
	if .vers == VersionTLS13 {
		 := .HandshakeState.toPrivate13()
		.serverHello = 
		.hello = 
		.echContext = 
		if .HandshakeState.State13.EarlySecret != nil && .cipherSuite != 0 {
			.earlySecret = tls13.NewEarlySecretFromSecret(cipherSuiteTLS13ByID(.cipherSuite).hash.New, .HandshakeState.State13.EarlySecret)
		}
		if .HandshakeState.MasterSecret != nil && .cipherSuite != 0 {
			.masterSecret = tls13.NewMasterSecretFromSecret(cipherSuiteTLS13ByID(.cipherSuite).hash.New, .HandshakeState.MasterSecret)
		}
		if ! {
			.earlySecret = 
			.binderKey = 
			.session = 
		}
		.ctx = 
		// In TLS 1.3, session tickets are delivered after the handshake.
		 = .handshake()
		if  := .toPublic13();  != nil {
			.HandshakeState = *
		}
		return 
	}

	 := .HandshakeState.toPrivate12()
	.serverHello = 
	.hello = 
	.ctx = 
	.session = 
	 = .handshake()
	if  := .toPublic12();  != nil {
		.HandshakeState = *
	}
	if  != nil {
		return 
	}
	return nil
}

func ( *UConn) ( *clientHelloMsg,  *echClientContext) ( error) {
	// Recreate the inner ClientHello from its compressed form using server's decodeInnerClientHello function.
	// See https://github.com/refraction-networking/utls/blob/e430876b1d82fdf582efc57f3992d448e7ab3d8a/ech.go#L276-L283
	,  := encodeInnerClientHelloReorderOuterExts(.innerHello, int(.config.MaxNameLength), .extensionsList())
	if  != nil {
		return 
	}

	,  := decodeInnerClientHello(, )
	if  != nil {
		return 
	}

	if  := transcriptMsg(, .innerTranscript);  != nil {
		return 
	}

	return nil
}