// Copyright 2013 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 (
	
	
	
	
)

// GCM represents a Galois Counter Mode with a specific key.
type GCM struct {
	cipher    aes.Block
	nonceSize int
	tagSize   int
	gcmPlatformData
}

func ( *aes.Block, ,  int) (*GCM, error) {
	// This function is outlined to let the allocation happen on the parent stack.
	return newGCM(&GCM{}, , , )
}

// newGCM is marked go:noinline to avoid it inlining into New, and making New
// too complex to inline itself.
//
//go:noinline
func newGCM( *GCM,  *aes.Block, ,  int) (*GCM, 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 .BlockSize() != gcmBlockSize {
		return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
	}
	.cipher = *
	.nonceSize = 
	.tagSize = 
	initGCM()
	return , nil
}

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

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

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

func ( *GCM) (, , ,  []byte) []byte {
	fips140.RecordNonApproved()
	return .sealAfterIndicator(, , , )
}

func ( *GCM) (, , ,  []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")
	}

	seal(, , , , )
	return 
}

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

func ( *GCM) (, , ,  []byte) ([]byte, error) {
	if len() != .nonceSize {
		panic("crypto/cipher: incorrect nonce length given to GCM")
	}
	// Sanity check to prevent the authentication from always succeeding if an
	// implementation leaves tagSize uninitialized, for example.
	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")
	}

	fips140.RecordApproved()
	if  := open(, , , , );  != nil {
		// 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, 
	}
	return , nil
}

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