// Copyright 2019 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 (ppc64le || ppc64) && !purego package gcm import ( "crypto/internal/fips140/aes" "crypto/internal/fips140/subtle" "crypto/internal/fips140deps/byteorder" "crypto/internal/fips140deps/godebug" "crypto/internal/impl" "runtime" ) // This file implements GCM using an optimized GHASH function. //go:noescape func gcmInit(productTable *[256]byte, h []byte) //go:noescape func gcmHash(output []byte, productTable *[256]byte, inp []byte, len int) func counterCryptASM(nr int, out, in []byte, counter *[gcmBlockSize]byte, key *uint32) // The POWER architecture doesn't have a way to turn off AES-GCM support // at runtime with GODEBUG=cpu.something=off, so introduce a new GODEBUG // knob for that. It's intentionally only checked at init() time, to // avoid the performance overhead of checking it every time. var supportsAESGCM = godebug.Value("#ppc64gcm") != "off" func init() { impl.Register("gcm", "POWER8", &supportsAESGCM) } 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 } hle := make([]byte, gcmBlockSize) aes.EncryptBlockInternal(&g.cipher, hle, hle) // Reverse the bytes in each 8 byte chunk // Load little endian, store big endian var h1, h2 uint64 if runtime.GOARCH == "ppc64le" { h1 = byteorder.LEUint64(hle[:8]) h2 = byteorder.LEUint64(hle[8:]) } else { h1 = byteorder.BEUint64(hle[:8]) h2 = byteorder.BEUint64(hle[8:]) } byteorder.BEPutUint64(hle[:8], h1) byteorder.BEPutUint64(hle[8:], h2) gcmInit(&g.productTable, hle) } // deriveCounter computes the initial GCM counter state from the given nonce. func deriveCounter(counter *[gcmBlockSize]byte, nonce []byte, productTable *[256]byte) { if len(nonce) == gcmStandardNonceSize { copy(counter[:], nonce) counter[gcmBlockSize-1] = 1 } else { var hash [16]byte paddedGHASH(&hash, nonce, productTable) lens := gcmLengths(0, uint64(len(nonce))*8) paddedGHASH(&hash, lens[:], productTable) copy(counter[:], hash[:]) } } // counterCrypt encrypts in using AES in counter mode and places the result // into out. counter is the initial count value and will be updated with the next // count value. The length of out must be greater than or equal to the length // of in. // counterCryptASM implements counterCrypt which then allows the loop to // be unrolled and optimized. func counterCrypt(b *aes.Block, out, in []byte, counter *[gcmBlockSize]byte) { enc := aes.EncryptionKeySchedule(b) rounds := len(enc)/4 - 1 counterCryptASM(rounds, out, in, counter, &enc[0]) } // paddedGHASH pads data with zeroes until its length is a multiple of // 16-bytes. It then calculates a new value for hash using the ghash // algorithm. func paddedGHASH(hash *[16]byte, data []byte, productTable *[256]byte) { if siz := len(data) - (len(data) % gcmBlockSize); siz > 0 { gcmHash(hash[:], productTable, data[:], siz) data = data[siz:] } if len(data) > 0 { var s [16]byte copy(s[:], data) gcmHash(hash[:], productTable, s[:], len(s)) } } // auth calculates GHASH(ciphertext, additionalData), masks the result with // tagMask and writes the result to out. func auth(out, ciphertext, aad []byte, tagMask *[gcmTagSize]byte, productTable *[256]byte) { var hash [16]byte paddedGHASH(&hash, aad, productTable) paddedGHASH(&hash, ciphertext, productTable) lens := gcmLengths(uint64(len(aad))*8, uint64(len(ciphertext))*8) paddedGHASH(&hash, lens[:], productTable) copy(out, hash[:]) for i := range out { out[i] ^= tagMask[i] } } func seal(out []byte, g *GCM, nonce, plaintext, data []byte) { if !supportsAESGCM { sealGeneric(out, g, nonce, plaintext, data) return } var counter, tagMask [gcmBlockSize]byte deriveCounter(&counter, nonce, &g.productTable) aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) gcmInc32(&counter) counterCrypt(&g.cipher, out, plaintext, &counter) auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask, &g.productTable) } 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] var counter, tagMask [gcmBlockSize]byte deriveCounter(&counter, nonce, &g.productTable) aes.EncryptBlockInternal(&g.cipher, tagMask[:], counter[:]) gcmInc32(&counter) var expectedTag [gcmTagSize]byte auth(expectedTag[:], ciphertext, data, &tagMask, &g.productTable) if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { return errOpen } counterCrypt(&g.cipher, out, ciphertext, &counter) return nil } func gcmLengths(len0, len1 uint64) [16]byte { return [16]byte{ byte(len0 >> 56), byte(len0 >> 48), byte(len0 >> 40), byte(len0 >> 32), byte(len0 >> 24), byte(len0 >> 16), byte(len0 >> 8), byte(len0), byte(len1 >> 56), byte(len1 >> 48), byte(len1 >> 40), byte(len1 >> 32), byte(len1 >> 24), byte(len1 >> 16), byte(len1 >> 8), byte(len1), } }