// 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) {
	 := [recordlayer.HeaderSize:]
	 = [:recordlayer.HeaderSize]

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

	 := generateAEADAdditionalData(&.Header, len())
	 := .localCCM.Seal(nil, , , )

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

	// Update recordLayer size to include explicit nonce
	binary.BigEndian.PutUint16([recordlayer.HeaderSize-2:], uint16(len()-recordlayer.HeaderSize))
	return , nil
}

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

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

	 := generateAEADAdditionalData(&, len()-int(.tagLen))
	,  = .remoteCCM.Open([:0], , , )
	if  != nil {
		return nil, fmt.Errorf("%w: %v", errDecryptPacket, ) //nolint:errorlint
	}
	return append([:recordlayer.HeaderSize], ...), nil
}