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

package ciphersuite

import (
	
	
	
	

	
	
	
)

// CCMTagLen is the length of Authentication Tag.
type CCMTagLen int

// CCM Enums.
const (
	CCMTagLength8  CCMTagLen = 8
	CCMTagLength   CCMTagLen = 16
	ccmNonceLength           = 12
)

// CCM Provides an API to Encrypt/Decrypt DTLS 1.2 Packets.
type CCM struct {
	localCCM, remoteCCM         ccm.CCM
	localWriteIV, remoteWriteIV []byte
	tagLen                      CCMTagLen
}

// NewCCM creates a DTLS GCM Cipher.
func ( CCMTagLen, , , ,  []byte) (*CCM, error) {
	,  := aes.NewCipher()
	if  != nil {
		return nil, 
	}
	,  := ccm.NewCCM(, int(), ccmNonceLength)
	if  != nil {
		return nil, 
	}

	,  := aes.NewCipher()
	if  != nil {
		return nil, 
	}
	,  := ccm.NewCCM(, int(), ccmNonceLength)
	if  != nil {
		return nil, 
	}

	return &CCM{
		localCCM:      ,
		localWriteIV:  ,
		remoteCCM:     ,
		remoteWriteIV: ,
		tagLen:        ,
	}, nil
}

// Encrypt encrypt a DTLS RecordLayer message.
func ( *CCM) ( *recordlayer.RecordLayer,  []byte) ([]byte, error) {
	 := [.Header.Size():]
	 = [:.Header.Size()]

	 := append(append([]byte{}, .localWriteIV[:4]...), make([]byte, 8)...)
	if ,  := rand.Read([4:]);  != nil {
		return nil, 
	}

	var  []byte
	if .Header.ContentType == protocol.ContentTypeConnectionID {
		 = generateAEADAdditionalDataCID(&.Header, len())
	} else {
		 = generateAEADAdditionalData(&.Header, len())
	}
	 := .localCCM.Seal(nil, , , )

	 = append([4:], ...)
	 = append(, ...)

	// Update recordLayer size to include explicit nonce
	binary.BigEndian.PutUint16([.Header.Size()-2:], uint16(len()-.Header.Size())) //nolint:gosec //G115

	return , nil
}

// Decrypt decrypts a DTLS RecordLayer message.
func ( *CCM) ( recordlayer.Header,  []byte) ([]byte, error) {
	if  := .Unmarshal();  != nil {
		return nil, 
	}
	switch {
	case .ContentType == protocol.ContentTypeChangeCipherSpec:
		// Nothing to encrypt with ChangeCipherSpec
		return , nil
	case len() <= (8 + .Size()):
		return nil, errNotEnoughRoomForNonce
	}

	 := append(append([]byte{}, .remoteWriteIV[:4]...), [.Size():.Size()+8]...)
	 := [.Size()+8:]

	var  []byte
	if .ContentType == protocol.ContentTypeConnectionID {
		 = generateAEADAdditionalDataCID(&, len()-int(.tagLen))
	} else {
		 = generateAEADAdditionalData(&, len()-int(.tagLen))
	}
	var  error
	,  = .remoteCCM.Open([:0], , , )
	if  != nil {
		return nil, fmt.Errorf("%w: %v", errDecryptPacket, ) //nolint:errorlint
	}

	return append([:.Size()], ...), nil
}