// 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 ( "crypto/internal/fips140/aes" "crypto/internal/fips140/subtle" "crypto/internal/fips140deps/cpu" "crypto/internal/impl" ) // The following functions are defined in gcm_*.s. //go:noescape func gcmAesInit(productTable *[256]byte, ks []uint32) //go:noescape func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte) //go:noescape func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32) //go:noescape func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32) //go:noescape func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen 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(g *GCM) { if !supportsAESGCM { return } gcmAesInit(&g.productTable, aes.EncryptionKeySchedule(&g.cipher)) } func seal(out []byte, g *GCM, nonce, plaintext, data []byte) { if !supportsAESGCM { sealGeneric(out, g, nonce, plaintext, data) return } var counter, tagMask [gcmBlockSize]byte if len(nonce) == gcmStandardNonceSize { // Init counter to nonce||1 copy(counter[:], nonce) counter[gcmBlockSize-1] = 1 } else { // Otherwise counter = GHASH(nonce) gcmAesData(&g.productTable, nonce, &counter) gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) } aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) var tagOut [gcmTagSize]byte gcmAesData(&g.productTable, data, &tagOut) if len(plaintext) > 0 { gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, aes.EncryptionKeySchedule(&g.cipher)) } gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data))) copy(out[len(plaintext):], tagOut[:]) } func open(out []byte, g *GCM, nonce, ciphertext, data []byte) error { if !supportsAESGCM { return openGeneric(out, g, nonce, ciphertext, data) } tag := ciphertext[len(ciphertext)-g.tagSize:] ciphertext = ciphertext[:len(ciphertext)-g.tagSize] // See GCM spec, section 7.1. var counter, tagMask [gcmBlockSize]byte if len(nonce) == gcmStandardNonceSize { // Init counter to nonce||1 copy(counter[:], nonce) counter[gcmBlockSize-1] = 1 } else { // Otherwise counter = GHASH(nonce) gcmAesData(&g.productTable, nonce, &counter) gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) } aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) var expectedTag [gcmTagSize]byte gcmAesData(&g.productTable, data, &expectedTag) if len(ciphertext) > 0 { gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, aes.EncryptionKeySchedule(&g.cipher)) } gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data))) if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { return errOpen } return nil }