1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5//go:build (amd64 || arm64) && !purego 6 7package aes 8 9import ( 10 "crypto/cipher" 11 "crypto/internal/alias" 12 "crypto/subtle" 13 "errors" 14) 15 16// The following functions are defined in gcm_*.s. 17 18//go:noescape 19func gcmAesInit(productTable *[256]byte, ks []uint32) 20 21//go:noescape 22func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte) 23 24//go:noescape 25func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32) 26 27//go:noescape 28func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32) 29 30//go:noescape 31func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64) 32 33const ( 34 gcmBlockSize = 16 35 gcmTagSize = 16 36 gcmMinimumTagSize = 12 // NIST SP 800-38D recommends tags with 12 or more bytes. 37 gcmStandardNonceSize = 12 38) 39 40var errOpen = errors.New("cipher: message authentication failed") 41 42// Assert that aesCipherGCM implements the gcmAble interface. 43var _ gcmAble = (*aesCipherGCM)(nil) 44 45// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only 46// called by [crypto/cipher.NewGCM] via the gcmAble interface. 47func (c *aesCipherGCM) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { 48 g := &gcmAsm{ks: c.enc[:c.l], nonceSize: nonceSize, tagSize: tagSize} 49 gcmAesInit(&g.productTable, g.ks) 50 return g, nil 51} 52 53type gcmAsm struct { 54 // ks is the key schedule, the length of which depends on the size of 55 // the AES key. 56 ks []uint32 57 // productTable contains pre-computed multiples of the binary-field 58 // element used in GHASH. 59 productTable [256]byte 60 // nonceSize contains the expected size of the nonce, in bytes. 61 nonceSize int 62 // tagSize contains the size of the tag, in bytes. 63 tagSize int 64} 65 66func (g *gcmAsm) NonceSize() int { 67 return g.nonceSize 68} 69 70func (g *gcmAsm) Overhead() int { 71 return g.tagSize 72} 73 74// sliceForAppend takes a slice and a requested number of bytes. It returns a 75// slice with the contents of the given slice followed by that many bytes and a 76// second slice that aliases into it and contains only the extra bytes. If the 77// original slice has sufficient capacity then no allocation is performed. 78func sliceForAppend(in []byte, n int) (head, tail []byte) { 79 if total := len(in) + n; cap(in) >= total { 80 head = in[:total] 81 } else { 82 head = make([]byte, total) 83 copy(head, in) 84 } 85 tail = head[len(in):] 86 return 87} 88 89// Seal encrypts and authenticates plaintext. See the [cipher.AEAD] interface for 90// details. 91func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { 92 if len(nonce) != g.nonceSize { 93 panic("crypto/cipher: incorrect nonce length given to GCM") 94 } 95 if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize { 96 panic("crypto/cipher: message too large for GCM") 97 } 98 99 var counter, tagMask [gcmBlockSize]byte 100 101 if len(nonce) == gcmStandardNonceSize { 102 // Init counter to nonce||1 103 copy(counter[:], nonce) 104 counter[gcmBlockSize-1] = 1 105 } else { 106 // Otherwise counter = GHASH(nonce) 107 gcmAesData(&g.productTable, nonce, &counter) 108 gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) 109 } 110 111 encryptBlockAsm(len(g.ks)/4-1, &g.ks[0], &tagMask[0], &counter[0]) 112 113 var tagOut [gcmTagSize]byte 114 gcmAesData(&g.productTable, data, &tagOut) 115 116 ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize) 117 if alias.InexactOverlap(out[:len(plaintext)], plaintext) { 118 panic("crypto/cipher: invalid buffer overlap") 119 } 120 if len(plaintext) > 0 { 121 gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks) 122 } 123 gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data))) 124 copy(out[len(plaintext):], tagOut[:]) 125 126 return ret 127} 128 129// Open authenticates and decrypts ciphertext. See the [cipher.AEAD] interface 130// for details. 131func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { 132 if len(nonce) != g.nonceSize { 133 panic("crypto/cipher: incorrect nonce length given to GCM") 134 } 135 // Sanity check to prevent the authentication from always succeeding if an implementation 136 // leaves tagSize uninitialized, for example. 137 if g.tagSize < gcmMinimumTagSize { 138 panic("crypto/cipher: incorrect GCM tag size") 139 } 140 141 if len(ciphertext) < g.tagSize { 142 return nil, errOpen 143 } 144 if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) { 145 return nil, errOpen 146 } 147 148 tag := ciphertext[len(ciphertext)-g.tagSize:] 149 ciphertext = ciphertext[:len(ciphertext)-g.tagSize] 150 151 // See GCM spec, section 7.1. 152 var counter, tagMask [gcmBlockSize]byte 153 154 if len(nonce) == gcmStandardNonceSize { 155 // Init counter to nonce||1 156 copy(counter[:], nonce) 157 counter[gcmBlockSize-1] = 1 158 } else { 159 // Otherwise counter = GHASH(nonce) 160 gcmAesData(&g.productTable, nonce, &counter) 161 gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) 162 } 163 164 encryptBlockAsm(len(g.ks)/4-1, &g.ks[0], &tagMask[0], &counter[0]) 165 166 var expectedTag [gcmTagSize]byte 167 gcmAesData(&g.productTable, data, &expectedTag) 168 169 ret, out := sliceForAppend(dst, len(ciphertext)) 170 if alias.InexactOverlap(out, ciphertext) { 171 panic("crypto/cipher: invalid buffer overlap") 172 } 173 if len(ciphertext) > 0 { 174 gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks) 175 } 176 gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data))) 177 178 if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 { 179 clear(out) 180 return nil, errOpen 181 } 182 183 return ret, nil 184} 185