// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package dtls

import (
	
	

	
	
	
	
	
	
	
	
)

//nolint:gocognit,gocyclo,maintidx,cyclop
func flight3Parse(
	 context.Context,
	 flightConn,
	 *State,
	 *handshakeCache,
	 *handshakeConfig,
) (flightVal, *alert.Alert, error) {
	// Clients may receive multiple HelloVerifyRequest messages with different cookies.
	// Clients SHOULD handle this by sending a new ClientHello with a cookie in response
	// to the new HelloVerifyRequest. RFC 6347 Section 4.2.1
	, ,  := .fullPullMap(.handshakeRecvSequence, .cipherSuite,
		handshakeCachePullRule{handshake.TypeHelloVerifyRequest, .initialEpoch, false, true},
	)
	if  {
		if ,  := [handshake.TypeHelloVerifyRequest].(*handshake.MessageHelloVerifyRequest);  {
			// DTLS 1.2 clients must not assume that the server will use the protocol version
			// specified in HelloVerifyRequest message. RFC 6347 Section 4.2.1
			if !.Version.Equal(protocol.Version1_0) && !.Version.Equal(protocol.Version1_2) {
				return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
			}
			.cookie = append([]byte{}, .Cookie...)
			.handshakeRecvSequence = 

			return flight3, nil, nil
		}
	}

	_, ,  = .fullPullMap(.handshakeRecvSequence, .cipherSuite,
		handshakeCachePullRule{handshake.TypeServerHello, .initialEpoch, false, false},
	)
	if ! {
		// Don't have enough messages. Keep reading
		return 0, nil, nil
	}

	if ,  := [handshake.TypeServerHello].(*handshake.MessageServerHello);  { //nolint:nestif
		if !.Version.Equal(protocol.Version1_2) {
			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.ProtocolVersion}, errUnsupportedProtocolVersion
		}
		for ,  := range .Extensions {
			switch ext := .(type) {
			case *extension.UseSRTP:
				,  := findMatchingSRTPProfile(.ProtectionProfiles, .localSRTPProtectionProfiles)
				if ! {
					return 0, &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, errClientNoMatchingSRTPProfile
				}
				.setSRTPProtectionProfile()
				.remoteSRTPMasterKeyIdentifier = .MasterKeyIdentifier
			case *extension.UseExtendedMasterSecret:
				if .extendedMasterSecret != DisableExtendedMasterSecret {
					.extendedMasterSecret = true
				}
			case *extension.ALPN:
				if len(.ProtocolNameList) > 1 { // This should be exactly 1, the zero case is handle when unmarshalling
					return 0, &alert.Alert{
						Level:       alert.Fatal,
						Description: alert.InternalError,
					}, extension.ErrALPNInvalidFormat // Meh, internal error?
				}
				.NegotiatedProtocol = .ProtocolNameList[0]
			case *extension.ConnectionID:
				// Only set connection ID to be sent if client supports connection
				// IDs.
				if .connectionIDGenerator != nil {
					.remoteConnectionID = .CID
				}
			}
		}
		// If the server doesn't support connection IDs, the client should not
		// expect one to be sent.
		if .remoteConnectionID == nil {
			.setLocalConnectionID(nil)
		}

		if .extendedMasterSecret == RequireExtendedMasterSecret && !.extendedMasterSecret {
			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errClientRequiredButNoServerEMS
		}
		if len(.localSRTPProtectionProfiles) > 0 && .getSRTPProtectionProfile() == 0 {
			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errRequestedButNoSRTPExtension
		}

		 := cipherSuiteForID(CipherSuiteID(*.CipherSuiteID), .customCipherSuites)
		if  == nil {
			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errCipherSuiteNoIntersection
		}

		,  := findMatchingCipherSuite([]CipherSuite{}, .localCipherSuites)
		if ! {
			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
		}

		.cipherSuite = 
		.remoteRandom = .Random
		.log.Tracef("[handshake] use cipher suite: %s", .String())

		if len(.SessionID) > 0 && bytes.Equal(.SessionID, .SessionID) {
			return handleResumption(, , , , )
		}

		if len(.SessionID) > 0 {
			.log.Tracef("[handshake] clean old session : %s", .SessionID)
			if  := .sessionStore.Del(.SessionID);  != nil {
				return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
			}
		}

		if .sessionStore == nil {
			.SessionID = []byte{}
		} else {
			.SessionID = .SessionID
		}

		.masterSecret = []byte{}
	}

	if .localPSKCallback != nil {
		, ,  = .fullPullMap(.handshakeRecvSequence+1, .cipherSuite,
			handshakeCachePullRule{handshake.TypeServerKeyExchange, .initialEpoch, false, true},
			handshakeCachePullRule{handshake.TypeServerHelloDone, .initialEpoch, false, false},
		)
	} else {
		, ,  = .fullPullMap(.handshakeRecvSequence+1, .cipherSuite,
			handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, false, true},
			handshakeCachePullRule{handshake.TypeServerKeyExchange, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificateRequest, .initialEpoch, false, true},
			handshakeCachePullRule{handshake.TypeServerHelloDone, .initialEpoch, false, false},
		)
	}
	if ! {
		// Don't have enough messages. Keep reading
		return 0, nil, nil
	}
	.handshakeRecvSequence = 

	if ,  := [handshake.TypeCertificate].(*handshake.MessageCertificate);  {
		.PeerCertificates = .Certificate
	} else if .cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate {
		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.NoCertificate}, errInvalidCertificate
	}

	if ,  := [handshake.TypeServerKeyExchange].(*handshake.MessageServerKeyExchange);  {
		,  := handleServerKeyExchange(, , , )
		if  != nil {
			return 0, , 
		}
	}

	if ,  := [handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest);  {
		.remoteCertRequestAlgs = .SignatureHashAlgorithms
		.remoteRequestedCertificate = true
	}

	return flight5, nil, nil
}

func handleResumption(
	 context.Context,
	 flightConn,
	 *State,
	 *handshakeCache,
	 *handshakeConfig,
) (flightVal, *alert.Alert, error) {
	if  := .initCipherSuite();  != nil {
		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
	}

	// Now, encrypted packets can be handled
	if  := .handleQueuedPackets();  != nil {
		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
	}

	, ,  := .fullPullMap(.handshakeRecvSequence+1, .cipherSuite,
		handshakeCachePullRule{handshake.TypeFinished, .initialEpoch + 1, false, false},
	)
	if ! {
		// No valid message received. Keep reading
		return 0, nil, nil
	}

	var  *handshake.MessageFinished
	if ,  = [handshake.TypeFinished].(*handshake.MessageFinished); ! {
		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, nil
	}
	 := .pullAndMerge(
		handshakeCachePullRule{handshake.TypeClientHello, .initialEpoch, true, false},
		handshakeCachePullRule{handshake.TypeServerHello, .initialEpoch, false, false},
	)

	,  := prf.VerifyDataServer(.masterSecret, , .cipherSuite.HashFunc())
	if  != nil {
		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
	}
	if !bytes.Equal(, .VerifyData) {
		return 0, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errVerifyDataMismatch
	}

	 := .localRandom.MarshalFixed()
	.writeKeyLog(keyLogLabelTLS12, [:], .masterSecret)

	return flight5b, nil, nil
}

//nolint:cyclop
func handleServerKeyExchange(
	 flightConn,
	 *State,
	 *handshakeConfig,
	 *handshake.MessageServerKeyExchange,
) (*alert.Alert, error) {
	var  error
	if .cipherSuite == nil {
		return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
	}
	if .localPSKCallback != nil { //nolint:nestif
		var  []byte
		if ,  = .localPSKCallback(.IdentityHint);  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
		.IdentityHint = .IdentityHint
		switch .cipherSuite.KeyExchangeAlgorithm() {
		case types.KeyExchangeAlgorithmPsk:
			.preMasterSecret = prf.PSKPreMasterSecret()
		case (types.KeyExchangeAlgorithmEcdhe | types.KeyExchangeAlgorithmPsk):
			if .localKeypair,  = elliptic.GenerateKeypair(.NamedCurve);  != nil {
				return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
			}
			.preMasterSecret,  = prf.EcdhePSKPreMasterSecret(
				,
				.PublicKey,
				.localKeypair.PrivateKey,
				.localKeypair.Curve,
			)
			if  != nil {
				return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
			}
		default:
			return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errInvalidCipherSuite
		}
	} else {
		if .localKeypair,  = elliptic.GenerateKeypair(.NamedCurve);  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}

		if .preMasterSecret,  = prf.PreMasterSecret(
			.PublicKey,
			.localKeypair.PrivateKey,
			.localKeypair.Curve,
		);  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
	}

	return nil, nil //nolint:nilnil
}

func flight3Generate(
	 flightConn,
	 *State,
	 *handshakeCache,
	 *handshakeConfig,
) ([]*packet, *alert.Alert, error) {
	 := []extension.Extension{
		&extension.SupportedSignatureAlgorithms{
			SignatureHashAlgorithms: .localSignatureSchemes,
		},
		&extension.RenegotiationInfo{
			RenegotiatedConnection: 0,
		},
	}

	if .namedCurve != 0 {
		 = append(, []extension.Extension{
			&extension.SupportedEllipticCurves{
				EllipticCurves: .ellipticCurves,
			},
			&extension.SupportedPointFormats{
				PointFormats: []elliptic.CurvePointFormat{elliptic.CurvePointFormatUncompressed},
			},
		}...)
	}

	if len(.localSRTPProtectionProfiles) > 0 {
		 = append(, &extension.UseSRTP{
			ProtectionProfiles: .localSRTPProtectionProfiles,
		})
	}

	if .extendedMasterSecret == RequestExtendedMasterSecret ||
		.extendedMasterSecret == RequireExtendedMasterSecret {
		 = append(, &extension.UseExtendedMasterSecret{
			Supported: true,
		})
	}

	if len(.serverName) > 0 {
		 = append(, &extension.ServerName{ServerName: .serverName})
	}

	if len(.supportedProtocols) > 0 {
		 = append(, &extension.ALPN{ProtocolNameList: .supportedProtocols})
	}

	// If we sent a connection ID on the first ClientHello, send it on the
	// second.
	if .getLocalConnectionID() != nil {
		 = append(, &extension.ConnectionID{CID: .getLocalConnectionID()})
	}

	 := &handshake.MessageClientHello{
		Version:            protocol.Version1_2,
		SessionID:          .SessionID,
		Cookie:             .cookie,
		Random:             .localRandom,
		CipherSuiteIDs:     cipherSuiteIDs(.localCipherSuites),
		CompressionMethods: defaultCompressionMethods(),
		Extensions:         ,
	}

	var  handshake.Handshake

	if .clientHelloMessageHook != nil {
		 = handshake.Handshake{Message: .clientHelloMessageHook(*)}
	} else {
		 = handshake.Handshake{Message: }
	}

	return []*packet{
		{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Version: protocol.Version1_2,
				},
				Content: &,
			},
		},
	}, nil, nil
}