// 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 cipher

import (
	
	
	
	
	
	
	
)

const (
	gcmBlockSize         = 16
	gcmStandardNonceSize = 12
	gcmTagSize           = 16
	gcmMinimumTagSize    = 12 // NIST SP 800-38D recommends tags with 12 or more bytes.
)

// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode
// with the standard nonce length.
//
// In general, the GHASH operation performed by this implementation of GCM is not constant-time.
// An exception is when the underlying [Block] was created by aes.NewCipher
// on systems with hardware support for AES. See the [crypto/aes] package documentation for details.
func ( Block) (AEAD, error) {
	if fips140only.Enabled {
		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
	}
	return newGCM(, gcmStandardNonceSize, gcmTagSize)
}

// NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
// Counter Mode, which accepts nonces of the given length. The length must not
// be zero.
//
// Only use this function if you require compatibility with an existing
// cryptosystem that uses non-standard nonce lengths. All other users should use
// [NewGCM], which is faster and more resistant to misuse.
func ( Block,  int) (AEAD, error) {
	if fips140only.Enabled {
		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
	}
	return newGCM(, , gcmTagSize)
}

// NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
// Counter Mode, which generates tags with the given length.
//
// Tag sizes between 12 and 16 bytes are allowed.
//
// Only use this function if you require compatibility with an existing
// cryptosystem that uses non-standard tag lengths. All other users should use
// [NewGCM], which is more resistant to misuse.
func ( Block,  int) (AEAD, error) {
	if fips140only.Enabled {
		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
	}
	return newGCM(, gcmStandardNonceSize, )
}

func newGCM( Block, ,  int) (AEAD, error) {
	,  := .(*aes.Block)
	if ! {
		if fips140only.Enabled {
			return nil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode")
		}
		return newGCMFallback(, , )
	}
	// We don't return gcm.New directly, because it would always return a non-nil
	// AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil.
	,  := gcm.New(, , )
	if  != nil {
		return nil, 
	}
	return , nil
}

// NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
// Mode, with randomly-generated nonces. The cipher must have been created by
// [aes.NewCipher].
//
// It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
// and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,
// while the Overhead is 28 bytes (the combination of nonce size and tag size).
//
// A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
// risk of a random nonce collision to negligible levels.
func ( Block) (AEAD, error) {
	,  := .(*aes.Block)
	if ! {
		return nil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block")
	}
	,  := gcm.New(, gcmStandardNonceSize, gcmTagSize)
	if  != nil {
		return nil, 
	}
	return gcmWithRandomNonce{}, nil
}

type gcmWithRandomNonce struct {
	*gcm.GCM
}

func ( gcmWithRandomNonce) () int {
	return 0
}

func ( gcmWithRandomNonce) () int {
	return gcmStandardNonceSize + gcmTagSize
}

func ( gcmWithRandomNonce) (, , ,  []byte) []byte {
	if len() != 0 {
		panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
	}

	,  := sliceForAppend(, gcmStandardNonceSize+len()+gcmTagSize)
	if alias.InexactOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and input")
	}
	if alias.AnyOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and additional data")
	}
	 = [:gcmStandardNonceSize]
	 := [gcmStandardNonceSize:]

	// The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst.
	//
	// This is kind of a problem when trying to prepend or trim a nonce, because the
	// actual AES-GCTR blocks end up overlapping but not exactly.
	//
	// In Open, we write the output *before* the input, so unless we do something
	// weird like working through a chunk of block backwards, it works out.
	//
	// In Seal, we could work through the input backwards or intentionally load
	// ahead before writing.
	//
	// However, the crypto/internal/fips140/aes/gcm APIs also check for exact overlap,
	// so for now we just do a memmove if we detect overlap.
	//
	//     ┌───────────────────────────┬ ─ ─
	//     │PPPPPPPPPPPPPPPPPPPPPPPPPPP│    │
	//     └▽─────────────────────────▲┴ ─ ─
	//       ╲ Seal                    ╲
	//        ╲                    Open ╲
	//     ┌───▼─────────────────────────△──┐
	//     │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│
	//     └────────────────────────────────┘
	//
	if alias.AnyOverlap(, ) {
		copy(, )
		 = [:len()]
	}

	gcm.SealWithRandomNonce(.GCM, , , , )
	return 
}

func ( gcmWithRandomNonce) (, , ,  []byte) ([]byte, error) {
	if len() != 0 {
		panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
	}
	if len() < gcmStandardNonceSize+gcmTagSize {
		return nil, errOpen
	}

	,  := sliceForAppend(, len()-gcmStandardNonceSize-gcmTagSize)
	if alias.InexactOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and input")
	}
	if alias.AnyOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and additional data")
	}
	// See the discussion in Seal. Note that if there is any overlap at this
	// point, it's because out = ciphertext, so out must have enough capacity
	// even if we sliced the tag off. Also note how [AEAD] specifies that "the
	// contents of dst, up to its capacity, may be overwritten".
	if alias.AnyOverlap(, ) {
		 = make([]byte, gcmStandardNonceSize)
		copy(, )
		copy([:len()], [gcmStandardNonceSize:])
		 = [:len()-gcmStandardNonceSize]
	} else {
		 = [:gcmStandardNonceSize]
		 = [gcmStandardNonceSize:]
	}

	,  := .GCM.Open([:0], , , )
	if  != nil {
		return nil, 
	}
	return , nil
}

// gcmAble is an interface implemented by ciphers that have a specific optimized
// implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to
// eventually remove it.
type gcmAble interface {
	NewGCM(nonceSize, tagSize int) (AEAD, error)
}

func newGCMFallback( Block, ,  int) (AEAD, error) {
	if  < gcmMinimumTagSize ||  > gcmBlockSize {
		return nil, errors.New("cipher: incorrect tag size given to GCM")
	}
	if  <= 0 {
		return nil, errors.New("cipher: the nonce can't have zero length")
	}
	if ,  := .(gcmAble);  {
		return .NewGCM(, )
	}
	if .BlockSize() != gcmBlockSize {
		return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
	}
	return &gcmFallback{cipher: , nonceSize: , tagSize: }, nil
}

// gcmFallback is only used for non-AES ciphers, which regrettably we
// theoretically support. It's a copy of the generic implementation from
// crypto/internal/fips140/aes/gcm/gcm_generic.go, refer to that file for more details.
type gcmFallback struct {
	cipher    Block
	nonceSize int
	tagSize   int
}

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

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

func ( *gcmFallback) (, , ,  []byte) []byte {
	if len() != .nonceSize {
		panic("crypto/cipher: incorrect nonce length given to GCM")
	}
	if .nonceSize == 0 {
		panic("crypto/cipher: incorrect GCM nonce size")
	}
	if uint64(len()) > uint64((1<<32)-2)*gcmBlockSize {
		panic("crypto/cipher: message too large for GCM")
	}

	,  := sliceForAppend(, len()+.tagSize)
	if alias.InexactOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and input")
	}
	if alias.AnyOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and additional data")
	}

	var , ,  [gcmBlockSize]byte
	.cipher.Encrypt([:], [:])
	deriveCounter(&, &, )
	gcmCounterCryptGeneric(.cipher, [:], [:], &)

	gcmCounterCryptGeneric(.cipher, , , &)

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

	return 
}

var errOpen = errors.New("cipher: message authentication failed")

func ( *gcmFallback) (, , ,  []byte) ([]byte, error) {
	if len() != .nonceSize {
		panic("crypto/cipher: incorrect nonce length given to GCM")
	}
	if .tagSize < gcmMinimumTagSize {
		panic("crypto/cipher: incorrect GCM tag size")
	}

	if len() < .tagSize {
		return nil, errOpen
	}
	if uint64(len()) > uint64((1<<32)-2)*gcmBlockSize+uint64(.tagSize) {
		return nil, errOpen
	}

	,  := sliceForAppend(, len()-.tagSize)
	if alias.InexactOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and input")
	}
	if alias.AnyOverlap(, ) {
		panic("crypto/cipher: invalid buffer overlap of output and additional data")
	}

	var , ,  [gcmBlockSize]byte
	.cipher.Encrypt([:], [:])
	deriveCounter(&, &, )
	gcmCounterCryptGeneric(.cipher, [:], [:], &)

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

	var  [gcmTagSize]byte
	gcmAuth([:], &, &, , )
	if subtle.ConstantTimeCompare([:.tagSize], ) != 1 {
		// We sometimes decrypt and authenticate concurrently, so we overwrite
		// dst in the event of a tag mismatch. To be consistent across platforms
		// and to avoid releasing unauthenticated plaintext, we clear the buffer
		// in the event of an error.
		clear()
		return nil, errOpen
	}

	gcmCounterCryptGeneric(.cipher, , , &)

	return , nil
}

func deriveCounter(,  *[gcmBlockSize]byte,  []byte) {
	if len() == gcmStandardNonceSize {
		copy([:], )
		[gcmBlockSize-1] = 1
	} else {
		 := make([]byte, 16)
		byteorder.BEPutUint64([8:], uint64(len())*8)
		 := gcm.GHASH(, , )
		copy([:], )
	}
}

func gcmCounterCryptGeneric( Block, ,  []byte,  *[gcmBlockSize]byte) {
	var  [gcmBlockSize]byte
	for len() >= gcmBlockSize {
		.Encrypt([:], [:])
		gcmInc32()

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

func gcmInc32( *[gcmBlockSize]byte) {
	 := [len()-4:]
	byteorder.BEPutUint32(, byteorder.BEUint32()+1)
}

func gcmAuth( []byte, ,  *[gcmBlockSize]byte, ,  []byte) {
	 := make([]byte, 16)
	byteorder.BEPutUint64([:8], uint64(len())*8)
	byteorder.BEPutUint64([8:], uint64(len())*8)
	 := gcm.GHASH(, , , )
	subtle.XORBytes(, , [:])
}

// sliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func sliceForAppend( []byte,  int) (,  []byte) {
	if  := len() + ; cap() >=  {
		 = [:]
	} else {
		 = make([]byte, )
		copy(, )
	}
	 = [len():]
	return
}