// CCM Mode, defined in
// NIST Special Publication SP 800-38C.

package ccm

import (
	
	
	
)

type ccm struct {
	c         cipher.Block
	mac       *mac
	nonceSize int
	tagSize   int
}

// NewCCMWithNonceAndTagSizes returns the given 128-bit, block cipher wrapped in Counter with CBC-MAC Mode, which accepts nonces of the given length.
// the formatting of this function is defined in SP800-38C, Appendix A.
// Each arguments have own valid range:
//   nonceSize should be one of the {7, 8, 9, 10, 11, 12, 13}.
//   tagSize should be one of the {4, 6, 8, 10, 12, 14, 16}.
//   Otherwise, it panics.
// The maximum payload size is defined as 1<<uint((15-nonceSize)*8)-1.
// If the given payload size exceeds the limit, it returns a error (Seal returns nil instead).
// The payload size is defined as len(plaintext) on Seal, len(ciphertext)-tagSize on Open.
func ( cipher.Block, ,  int) (cipher.AEAD, error) {
	if .BlockSize() != 16 {
		return nil, errors.New("cipher: CCM mode requires 128-bit block cipher")
	}

	if !(7 <=  &&  <= 13) {
		return nil, errors.New("cipher: invalid nonce size")
	}

	if !(4 <=  &&  <= 16 && &1 == 0) {
		return nil, errors.New("cipher: invalid tag size")
	}

	return &ccm{
		c:         ,
		mac:       newMAC(),
		nonceSize: ,
		tagSize:   ,
	}, nil
}

func ( *ccm) () int {
	return .nonceSize
}

func ( *ccm) () int {
	return .tagSize
}

func ( *ccm) (, , ,  []byte) []byte {
	if len() != .nonceSize {
		panic("cipher: incorrect nonce length given to CCM")
	}

	// AEAD interface doesn't provide a way to return errors.
	// So it returns nil instead.
	if maxUvarint(15-.nonceSize) < uint64(len()) {
		return nil
	}

	,  := sliceForAppend(, len()+.mac.Size())

	// Formatting of the Counter Blocks are defined in A.3.
	 := make([]byte, 16)               // Ctr0
	[0] = byte(15 - .nonceSize - 1) // [q-1]3
	copy([1:], )                  // N

	 := [len():] // S0
	.c.Encrypt(, )

	[15] = 1 // Ctr1

	 := cipher.NewCTR(.c, )

	.XORKeyStream(, )

	 := .getTag(, , )

	xorBytes(, , ) // T^S0

	return [:len()+.tagSize]
}

func ( *ccm) (, , ,  []byte) ([]byte, error) {
	if len() != .nonceSize {
		panic("cipher: incorrect nonce length given to CCM")
	}

	if len() <= .tagSize {
		panic("cipher: incorrect ciphertext length given to CCM")
	}

	if maxUvarint(15-.nonceSize) < uint64(len()-.tagSize) {
		return nil, errors.New("cipher: len(ciphertext)-tagSize exceeds the maximum payload size")
	}

	,  := sliceForAppend(, len()-.tagSize)

	// Formatting of the Counter Blocks are defined in A.3.
	 := make([]byte, 16)               // Ctr0
	[0] = byte(15 - .nonceSize - 1) // [q-1]3
	copy([1:], )                  // N

	 := make([]byte, 16) // S0
	.c.Encrypt(, )

	[15] = 1 // Ctr1

	 := cipher.NewCTR(.c, )

	.XORKeyStream(, [:len()])

	 := .getTag(, , )

	xorBytes(, , )

	if !bytes.Equal([:.tagSize], [len():]) {
		return nil, errors.New("crypto/ccm: message authentication failed")
	}

	return , nil
}

// getTag reuses a Ctr block for making the B0 block because of some parts are the same.
// For more details, see A.2 and A.3.
func ( *ccm) (, ,  []byte) []byte {
	.mac.Reset()

	 :=                                                 // B0
	[0] |= byte(((.tagSize - 2) / 2) << 3)              // [(t-2)/2]3
	putUvarint([1+.nonceSize:], uint64(len())) // Q

	if len() > 0 {
		[0] |= 1 << 6 // Adata

		.mac.Write()

		if len() < (1<<15 - 1<<7) {
			putUvarint([:2], uint64(len()))

			.mac.Write([:2])
		} else if len() <= 1<<31-1 {
			[0] = 0xff
			[1] = 0xfe
			putUvarint([2:6], uint64(len()))

			.mac.Write([:6])
		} else {
			[0] = 0xff
			[1] = 0xff
			putUvarint([2:10], uint64(len()))

			.mac.Write([:10])
		}
		.mac.Write()
		.mac.PadZero()
	} else {
		.mac.Write()
	}

	.mac.Write()
	.mac.PadZero()

	return .mac.Sum(nil)
}

func maxUvarint( int) uint64 {
	return 1<<uint(*8) - 1
}

// put uint64 as big endian.
func putUvarint( []byte,  uint64) {
	for  := 0;  < len(); ++ {
		[] = byte( >> uint(8*(len()-1-)))
	}
}