// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gcm

import (
	
	
	
)

func sealGeneric( []byte,  *GCM, , ,  []byte) {
	var , ,  [gcmBlockSize]byte
	.cipher.Encrypt([:], [:])
	deriveCounterGeneric(&, &, )
	gcmCounterCryptGeneric(&.cipher, [:], [:], &)

	gcmCounterCryptGeneric(&.cipher, , , &)

	var  [gcmTagSize]byte
	gcmAuthGeneric([:], &, &, [:len()], )
	copy([len():], [:])
}

func openGeneric( []byte,  *GCM, , ,  []byte) error {
	var , ,  [gcmBlockSize]byte
	.cipher.Encrypt([:], [:])
	deriveCounterGeneric(&, &, )
	gcmCounterCryptGeneric(&.cipher, [:], [:], &)

	 := [len()-.tagSize:]
	 = [:len()-.tagSize]

	var  [gcmTagSize]byte
	gcmAuthGeneric([:], &, &, , )
	if subtle.ConstantTimeCompare([:.tagSize], ) != 1 {
		return errOpen
	}

	gcmCounterCryptGeneric(&.cipher, , , &)

	return nil
}

// deriveCounterGeneric computes the initial GCM counter state from the given nonce.
// See NIST SP 800-38D, section 7.1. This assumes that counter is filled with
// zeros on entry.
func deriveCounterGeneric(,  *[gcmBlockSize]byte,  []byte) {
	// GCM has two modes of operation with respect to the initial counter
	// state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path"
	// for nonces of other lengths. For a 96-bit nonce, the nonce, along
	// with a four-byte big-endian counter starting at one, is used
	// directly as the starting counter. For other nonce sizes, the counter
	// is computed by passing it through the GHASH function.
	if len() == gcmStandardNonceSize {
		copy([:], )
		[gcmBlockSize-1] = 1
	} else {
		 := make([]byte, 16)
		byteorder.BEPutUint64([8:], uint64(len())*8)
		ghash(, , , )
	}
}

// gcmCounterCryptGeneric encrypts src using AES in counter mode with 32-bit
// wrapping (which is different from AES-CTR) and places the result into out.
// counter is the initial value and will be updated with the next value.
func gcmCounterCryptGeneric( *aes.Block, ,  []byte,  *[gcmBlockSize]byte) {
	var  [gcmBlockSize]byte

	for len() >= gcmBlockSize {
		.Encrypt([:], [:])
		gcmInc32()

		subtle.XORBytes(, , [:])
		 = [gcmBlockSize:]
		 = [gcmBlockSize:]
	}

	if len() > 0 {
		.Encrypt([:], [:])
		gcmInc32()
		subtle.XORBytes(, , [:])
	}
}

// gcmInc32 treats the final four bytes of counterBlock as a big-endian value
// and increments it.
func gcmInc32( *[gcmBlockSize]byte) {
	 := [len()-4:]
	byteorder.BEPutUint32(, byteorder.BEUint32()+1)
}

// gcmAuthGeneric calculates GHASH(additionalData, ciphertext), masks the result
// with tagMask and writes the result to out.
func gcmAuthGeneric( []byte, ,  *[gcmBlockSize]byte, ,  []byte) {
	checkGenericIsExpected()
	 := make([]byte, 16)
	byteorder.BEPutUint64([:8], uint64(len())*8)
	byteorder.BEPutUint64([8:], uint64(len())*8)
	var  [gcmBlockSize]byte
	ghash(&, , , , )
	subtle.XORBytes(, [:], [:])
}