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

package dtls

import (
	
	
	
	

	
	
	
	
	
)

// State holds the dtls connection state and implements both encoding.BinaryMarshaler and
// encoding.BinaryUnmarshaler.
type State struct {
	localEpoch, remoteEpoch   atomic.Value
	localSequenceNumber       []uint64 // uint48
	localRandom, remoteRandom handshake.Random
	masterSecret              []byte
	cipherSuite               CipherSuite // nil if a cipherSuite hasn't been chosen
	CipherSuiteID             CipherSuiteID

	srtpProtectionProfile         atomic.Value // Negotiated SRTPProtectionProfile
	remoteSRTPMasterKeyIdentifier []byte

	PeerCertificates [][]byte
	IdentityHint     []byte
	SessionID        []byte

	// Connection Identifiers must be negotiated afresh on session resumption.
	// https://datatracker.ietf.org/doc/html/rfc9146#name-the-connection_id-extension

	// localConnectionID is the locally generated connection ID that is expected
	// to be received from the remote endpoint.
	// For a server, this is the connection ID sent in ServerHello.
	// For a client, this is the connection ID sent in the ClientHello.
	localConnectionID atomic.Value
	// remoteConnectionID is the connection ID that the remote endpoint
	// specifies should be sent.
	// For a server, this is the connection ID received in the ClientHello.
	// For a client, this is the connection ID received in the ServerHello.
	remoteConnectionID []byte

	isClient bool

	preMasterSecret      []byte
	extendedMasterSecret bool

	namedCurve                 elliptic.Curve
	localKeypair               *elliptic.Keypair
	cookie                     []byte
	handshakeSendSequence      int
	handshakeRecvSequence      int
	serverName                 string
	remoteCertRequestAlgs      []signaturehash.Algorithm
	remoteRequestedCertificate bool   // Did we get a CertificateRequest
	localCertificatesVerify    []byte // cache CertificateVerify
	localVerifyData            []byte // cached VerifyData
	localKeySignature          []byte // cached keySignature
	peerCertificatesVerified   bool

	replayDetector []replaydetector.ReplayDetector

	peerSupportedProtocols []string
	NegotiatedProtocol     string
}

type serializedState struct {
	LocalEpoch            uint16
	RemoteEpoch           uint16
	LocalRandom           [handshake.RandomLength]byte
	RemoteRandom          [handshake.RandomLength]byte
	CipherSuiteID         uint16
	MasterSecret          []byte
	SequenceNumber        uint64
	SRTPProtectionProfile uint16
	PeerCertificates      [][]byte
	IdentityHint          []byte
	SessionID             []byte
	LocalConnectionID     []byte
	RemoteConnectionID    []byte
	IsClient              bool
	NegotiatedProtocol    string
}

var errCipherSuiteNotSet = &InternalError{Err: errors.New("cipher suite not set")} //nolint:goerr113

func ( *State) () (*State, error) {
	,  := .serialize()
	if  != nil {
		return nil, 
	}
	 := &State{}
	.deserialize(*)

	return , 
}

func ( *State) () (*serializedState, error) {
	if .cipherSuite == nil {
		return nil, errCipherSuiteNotSet
	}
	 := uint16(.cipherSuite.ID())

	// Marshal random values
	 := .localRandom.MarshalFixed()
	 := .remoteRandom.MarshalFixed()

	 := .getLocalEpoch()

	return &serializedState{
		LocalEpoch:            .getLocalEpoch(),
		RemoteEpoch:           .getRemoteEpoch(),
		CipherSuiteID:         ,
		MasterSecret:          .masterSecret,
		SequenceNumber:        atomic.LoadUint64(&.localSequenceNumber[]),
		LocalRandom:           ,
		RemoteRandom:          ,
		SRTPProtectionProfile: uint16(.getSRTPProtectionProfile()),
		PeerCertificates:      .PeerCertificates,
		IdentityHint:          .IdentityHint,
		SessionID:             .SessionID,
		LocalConnectionID:     .getLocalConnectionID(),
		RemoteConnectionID:    .remoteConnectionID,
		IsClient:              .isClient,
		NegotiatedProtocol:    .NegotiatedProtocol,
	}, nil
}

func ( *State) ( serializedState) {
	// Set epoch values
	 := .LocalEpoch
	.localEpoch.Store(.LocalEpoch)
	.remoteEpoch.Store(.RemoteEpoch)

	for len(.localSequenceNumber) <= int() {
		.localSequenceNumber = append(.localSequenceNumber, uint64(0))
	}

	// Set random values
	 := &handshake.Random{}
	.UnmarshalFixed(.LocalRandom)
	.localRandom = *

	 := &handshake.Random{}
	.UnmarshalFixed(.RemoteRandom)
	.remoteRandom = *

	.isClient = .IsClient

	// Set master secret
	.masterSecret = .MasterSecret

	// Set cipher suite
	.CipherSuiteID = CipherSuiteID(.CipherSuiteID)
	.cipherSuite = cipherSuiteForID(.CipherSuiteID, nil)

	atomic.StoreUint64(&.localSequenceNumber[], .SequenceNumber)
	.setSRTPProtectionProfile(SRTPProtectionProfile(.SRTPProtectionProfile))

	// Set remote certificate
	.PeerCertificates = .PeerCertificates

	.IdentityHint = .IdentityHint

	// Set local and remote connection IDs
	.setLocalConnectionID(.LocalConnectionID)
	.remoteConnectionID = .RemoteConnectionID

	.SessionID = .SessionID

	.NegotiatedProtocol = .NegotiatedProtocol
}

func ( *State) () error {
	if .cipherSuite.IsInitialized() {
		return nil
	}

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

	var  error
	if .isClient {
		 = .cipherSuite.Init(.masterSecret, [:], [:], true)
	} else {
		 = .cipherSuite.Init(.masterSecret, [:], [:], false)
	}
	if  != nil {
		return 
	}

	return nil
}

// MarshalBinary is a binary.BinaryMarshaler.MarshalBinary implementation.
func ( *State) () ([]byte, error) {
	,  := .serialize()
	if  != nil {
		return nil, 
	}

	var  bytes.Buffer
	 := gob.NewEncoder(&)
	if  := .Encode(*);  != nil {
		return nil, 
	}

	return .Bytes(), nil
}

// UnmarshalBinary is a binary.BinaryUnmarshaler.UnmarshalBinary implementation.
func ( *State) ( []byte) error {
	 := gob.NewDecoder(bytes.NewBuffer())
	var  serializedState
	if  := .Decode(&);  != nil {
		return 
	}

	.deserialize()

	return .initCipherSuite()
}

// ExportKeyingMaterial returns length bytes of exported key material in a new
// slice as defined in RFC 5705.
// This allows protocols to use DTLS for key establishment, but
// then use some of the keying material for their own purposes.
func ( *State) ( string,  []byte,  int) ([]byte, error) {
	if .getLocalEpoch() == 0 {
		return nil, errHandshakeInProgress
	} else if len() != 0 {
		return nil, errContextUnsupported
	} else if ,  := invalidKeyingLabels()[];  {
		return nil, errReservedExportKeyingMaterial
	}

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

	 := []byte()
	if .isClient {
		 = append(append(, [:]...), [:]...)
	} else {
		 = append(append(, [:]...), [:]...)
	}

	return prf.PHash(.masterSecret, , , .cipherSuite.HashFunc())
}

func ( *State) () uint16 {
	if ,  := .remoteEpoch.Load().(uint16);  {
		return 
	}

	return 0
}

func ( *State) () uint16 {
	if ,  := .localEpoch.Load().(uint16);  {
		return 
	}

	return 0
}

func ( *State) ( SRTPProtectionProfile) {
	.srtpProtectionProfile.Store()
}

func ( *State) () SRTPProtectionProfile {
	if ,  := .srtpProtectionProfile.Load().(SRTPProtectionProfile);  {
		return 
	}

	return 0
}

func ( *State) () []byte {
	if ,  := .localConnectionID.Load().([]byte);  {
		return 
	}

	return nil
}

func ( *State) ( []byte) {
	.localConnectionID.Store()
}

// RemoteRandomBytes returns the remote client hello random bytes.
func ( *State) () [handshake.RandomBytesLength]byte {
	return .remoteRandom.RandomBytes
}