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

package dtls

import (
	
	
	
	

	
	
	
	
	
	
)

func flight5Parse(
	 context.Context,
	 flightConn,
	 *State,
	 *handshakeCache,
	 *handshakeConfig,
) (flightVal, *alert.Alert, error) {
	, ,  := .fullPullMap(.handshakeRecvSequence, .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},
		handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, false, false},
		handshakeCachePullRule{handshake.TypeServerKeyExchange, .initialEpoch, false, false},
		handshakeCachePullRule{handshake.TypeCertificateRequest, .initialEpoch, false, false},
		handshakeCachePullRule{handshake.TypeServerHelloDone, .initialEpoch, false, false},
		handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, true, false},
		handshakeCachePullRule{handshake.TypeClientKeyExchange, .initialEpoch, true, false},
		handshakeCachePullRule{handshake.TypeCertificateVerify, .initialEpoch, true, false},
		handshakeCachePullRule{handshake.TypeFinished, .initialEpoch + 1, true, 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
	}

	if len(.SessionID) > 0 {
		 := Session{
			ID:     .SessionID,
			Secret: .masterSecret,
		}
		.log.Tracef("[handshake] save new session: %x", .ID)
		if  := .sessionStore.Set(.sessionKey(), );  != nil {
			return 0, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
	}

	return flight5, nil, nil
}

//nolint:gocognit,cyclop,maintidx
func flight5Generate(
	 flightConn,
	 *State,
	 *handshakeCache,
	 *handshakeConfig,
) ([]*packet, *alert.Alert, error) {
	var  crypto.Signer
	var  []*packet
	if .remoteRequestedCertificate { //nolint:nestif
		, ,  := .fullPullMap(.handshakeRecvSequence-2, .cipherSuite,
			handshakeCachePullRule{handshake.TypeCertificateRequest, .initialEpoch, false, false})
		if ! {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
		}
		 := CertificateRequestInfo{}
		if ,  := [handshake.TypeCertificateRequest].(*handshake.MessageCertificateRequest);  {
			.AcceptableCAs = .CertificateAuthoritiesNames
		} else {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errClientCertificateRequired
		}
		,  := .getClientCertificate(&)
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, 
		}
		if  == nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errNotAcceptableCertificateChain
		}
		if .Certificate != nil {
			,  = .PrivateKey.(crypto.Signer)
			if ! {
				return nil, &alert.Alert{Level: alert.Fatal, Description: alert.HandshakeFailure}, errInvalidPrivateKey
			}
		}
		 = append(,
			&packet{
				record: &recordlayer.RecordLayer{
					Header: recordlayer.Header{
						Version: protocol.Version1_2,
					},
					Content: &handshake.Handshake{
						Message: &handshake.MessageCertificate{
							Certificate: .Certificate,
						},
					},
				},
			})
	}

	 := &handshake.MessageClientKeyExchange{}
	if .localPSKCallback == nil {
		.PublicKey = .localKeypair.PublicKey
	} else {
		.IdentityHint = .localPSKIdentityHint
	}
	if  != nil && .localKeypair != nil && len(.localKeypair.PublicKey) > 0 {
		.PublicKey = .localKeypair.PublicKey
	}

	 = append(,
		&packet{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Version: protocol.Version1_2,
				},
				Content: &handshake.Handshake{
					Message: ,
				},
			},
		})

	 := .pullAndMerge(
		handshakeCachePullRule{handshake.TypeServerKeyExchange, .initialEpoch, false, false},
	)

	 := &handshake.MessageServerKeyExchange{}

	// handshakeMessageServerKeyExchange is optional for PSK
	if len() == 0 {
		,  := handleServerKeyExchange(, , , &handshake.MessageServerKeyExchange{})
		if  != nil {
			return nil, , 
		}
	} else {
		 := &handshake.Handshake{
			KeyExchangeAlgorithm: .cipherSuite.KeyExchangeAlgorithm(),
		}
		 := .Unmarshal()
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, 
		}

		switch h := .Message.(type) {
		case *handshake.MessageServerKeyExchange:
			 = 
		default:
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.UnexpectedMessage}, errInvalidContentType
		}
	}

	// Append not-yet-sent packets
	 := []byte{}
	 := uint16(.handshakeSendSequence) //nolint:gosec // G115
	for ,  := range  {
		,  := .record.Content.(*handshake.Handshake)
		if ! {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType
		}
		.Header.MessageSequence = 
		++
		,  := .Marshal()
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
		 = append(, ...)
	}

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

	// If the client has sent a certificate with signing ability, a digitally-signed
	// CertificateVerify message is sent to explicitly verify possession of the
	// private key in the certificate.
	if .remoteRequestedCertificate &&  != nil {
		 := append(.pullAndMerge(
			handshakeCachePullRule{handshake.TypeClientHello, .initialEpoch, true, false},
			handshakeCachePullRule{handshake.TypeServerHello, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeServerKeyExchange, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificateRequest, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeServerHelloDone, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, true, false},
			handshakeCachePullRule{handshake.TypeClientKeyExchange, .initialEpoch, true, false},
		), ...)

		// Find compatible signature scheme

		,  := signaturehash.SelectSignatureScheme(.remoteCertRequestAlgs, )
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, 
		}

		,  := generateCertificateVerify(, , .Hash)
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
		.localCertificatesVerify = 

		 := &packet{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Version: protocol.Version1_2,
				},
				Content: &handshake.Handshake{
					Message: &handshake.MessageCertificateVerify{
						HashAlgorithm:      .Hash,
						SignatureAlgorithm: .Signature,
						Signature:          .localCertificatesVerify,
					},
				},
			},
		}
		 = append(, )

		,  := .record.Content.(*handshake.Handshake)
		if ! {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, errInvalidContentType
		}
		.Header.MessageSequence = 
		// seqPred++ // this is the last use of seqPred
		,  := .Marshal()
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
		 = append(, ...)
	}

	 = append(,
		&packet{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Version: protocol.Version1_2,
				},
				Content: &protocol.ChangeCipherSpec{},
			},
		})

	if len(.localVerifyData) == 0 {
		 := .pullAndMerge(
			handshakeCachePullRule{handshake.TypeClientHello, .initialEpoch, true, false},
			handshakeCachePullRule{handshake.TypeServerHello, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeServerKeyExchange, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificateRequest, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeServerHelloDone, .initialEpoch, false, false},
			handshakeCachePullRule{handshake.TypeCertificate, .initialEpoch, true, false},
			handshakeCachePullRule{handshake.TypeClientKeyExchange, .initialEpoch, true, false},
			handshakeCachePullRule{handshake.TypeCertificateVerify, .initialEpoch, true, false},
			handshakeCachePullRule{handshake.TypeFinished, .initialEpoch + 1, true, false},
		)

		var  error
		.localVerifyData,  = prf.VerifyDataClient(
			.masterSecret,
			append(, ...),
			.cipherSuite.HashFunc(),
		)
		if  != nil {
			return nil, &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
	}

	 = append(,
		&packet{
			record: &recordlayer.RecordLayer{
				Header: recordlayer.Header{
					Version: protocol.Version1_2,
					Epoch:   1,
				},
				Content: &handshake.Handshake{
					Message: &handshake.MessageFinished{
						VerifyData: .localVerifyData,
					},
				},
			},
			shouldWrapCID:            len(.remoteConnectionID) > 0,
			shouldEncrypt:            true,
			resetLocalSequenceNumber: true,
		})

	return , nil, nil
}

//nolint:gocognit,cyclop
func initializeCipherSuite(
	 *State,
	 *handshakeCache,
	 *handshakeConfig,
	 *handshake.MessageServerKeyExchange,
	 []byte,
) (*alert.Alert, error) {
	if .cipherSuite.IsInitialized() {
		return nil, nil //nolint
	}

	 := .localRandom.MarshalFixed()
	 := .remoteRandom.MarshalFixed()

	var  error

	if .extendedMasterSecret {
		var  []byte
		,  = .sessionHash(.cipherSuite.HashFunc(), .initialEpoch, )
		if  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}

		.masterSecret,  = prf.ExtendedMasterSecret(.preMasterSecret, , .cipherSuite.HashFunc())
		if  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.IllegalParameter}, 
		}
	} else {
		.masterSecret,  = prf.MasterSecret(
			.preMasterSecret,
			[:],
			[:],
			.cipherSuite.HashFunc(),
		)
		if  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
	}

	if .cipherSuite.AuthenticationType() == CipherSuiteAuthenticationTypeCertificate { //nolint:nestif
		// Verify that the pair of hash algorithm and signiture is listed.
		var  bool
		for ,  := range .localSignatureSchemes {
			if .Hash == .HashAlgorithm && .Signature == .SignatureAlgorithm {
				 = true

				break
			}
		}
		if ! {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InsufficientSecurity}, errNoAvailableSignatureSchemes
		}

		 := valueKeyMessage(
			[:],
			[:],
			.PublicKey,
			.NamedCurve,
		)
		if  = verifyKeySignature(
			,
			.
				Signature,
			.HashAlgorithm,
			.PeerCertificates,
		);  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, 
		}
		var  [][]*x509.Certificate
		if !.insecureSkipVerify {
			if ,  = verifyServerCert(.PeerCertificates, .rootCAs, .serverName);  != nil {
				return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, 
			}
		}
		if .verifyPeerCertificate != nil {
			if  = .verifyPeerCertificate(.PeerCertificates, );  != nil {
				return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, 
			}
		}
	}
	if .verifyConnection != nil {
		,  := .clone()
		if  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
		}
		if  = .verifyConnection();  != nil {
			return &alert.Alert{Level: alert.Fatal, Description: alert.BadCertificate}, 
		}
	}

	if  = .cipherSuite.Init(.masterSecret, [:], [:], true);  != nil {
		return &alert.Alert{Level: alert.Fatal, Description: alert.InternalError}, 
	}

	.writeKeyLog(keyLogLabelTLS12, [:], .masterSecret)

	return nil, nil //nolint
}