// Copyright 2015 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.

//go:build (amd64 || arm64) && !purego

package gcm

import (
	
	
	
	
)

// The following functions are defined in gcm_*.s.

//go:noescape
func gcmAesInit( *[256]byte,  []uint32)

//go:noescape
func gcmAesData( *[256]byte,  []byte,  *[16]byte)

//go:noescape
func gcmAesEnc( *[256]byte, ,  []byte, ,  *[16]byte,  []uint32)

//go:noescape
func gcmAesDec( *[256]byte, ,  []byte, ,  *[16]byte,  []uint32)

//go:noescape
func gcmAesFinish( *[256]byte, ,  *[16]byte, ,  uint64)

// Keep in sync with crypto/tls.hasAESGCMHardwareSupport.
var supportsAESGCM = cpu.X86HasAES && cpu.X86HasPCLMULQDQ && cpu.X86HasSSE41 && cpu.X86HasSSSE3 ||
	cpu.ARM64HasAES && cpu.ARM64HasPMULL

func init() {
	if cpu.AMD64 {
		impl.Register("gcm", "AES-NI", &supportsAESGCM)
	}
	if cpu.ARM64 {
		impl.Register("gcm", "Armv8.0", &supportsAESGCM)
	}
}

// checkGenericIsExpected is called by the variable-time implementation to make
// sure it is not used when hardware support is available. It shouldn't happen,
// but this way it's more evidently correct.
func checkGenericIsExpected() {
	if supportsAESGCM {
		panic("gcm: internal error: using generic implementation despite hardware support")
	}
}

type gcmPlatformData struct {
	productTable [256]byte
}

func initGCM( *GCM) {
	if !supportsAESGCM {
		return
	}
	gcmAesInit(&.productTable, aes.EncryptionKeySchedule(&.cipher))
}

func seal( []byte,  *GCM, , ,  []byte) {
	if !supportsAESGCM {
		sealGeneric(, , , , )
		return
	}

	var ,  [gcmBlockSize]byte

	if len() == gcmStandardNonceSize {
		// Init counter to nonce||1
		copy([:], )
		[gcmBlockSize-1] = 1
	} else {
		// Otherwise counter = GHASH(nonce)
		gcmAesData(&.productTable, , &)
		gcmAesFinish(&.productTable, &, &, uint64(len()), uint64(0))
	}

	.cipher.Encrypt([:], [:])

	var  [gcmTagSize]byte
	gcmAesData(&.productTable, , &)

	if len() > 0 {
		gcmAesEnc(&.productTable, , , &, &, aes.EncryptionKeySchedule(&.cipher))
	}
	gcmAesFinish(&.productTable, &, &, uint64(len()), uint64(len()))
	copy([len():], [:])
}

func open( []byte,  *GCM, , ,  []byte) error {
	if !supportsAESGCM {
		return openGeneric(, , , , )
	}

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

	// See GCM spec, section 7.1.
	var ,  [gcmBlockSize]byte

	if len() == gcmStandardNonceSize {
		// Init counter to nonce||1
		copy([:], )
		[gcmBlockSize-1] = 1
	} else {
		// Otherwise counter = GHASH(nonce)
		gcmAesData(&.productTable, , &)
		gcmAesFinish(&.productTable, &, &, uint64(len()), uint64(0))
	}

	.cipher.Encrypt([:], [:])

	var  [gcmTagSize]byte
	gcmAesData(&.productTable, , &)

	if len() > 0 {
		gcmAesDec(&.productTable, , , &, &, aes.EncryptionKeySchedule(&.cipher))
	}
	gcmAesFinish(&.productTable, &, &, uint64(len()), uint64(len()))

	if subtle.ConstantTimeCompare([:.tagSize], ) != 1 {
		return errOpen
	}
	return nil
}