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// +build ignore 6// -build amd64 7 8package aes 9 10import ( 11 "crypto/cipher" 12 "crypto/subtle" 13 "errors" 14) 15 16// The following functions are defined in gcm_amd64.s. 17func hasGCMAsm() bool 18 19//go:noescape 20func aesEncBlock(dst, src *[16]byte, ks []uint32) 21 22//go:noescape 23func gcmAesInit(productTable *[256]byte, ks []uint32) 24 25//go:noescape 26func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte) 27 28//go:noescape 29func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32) 30 31//go:noescape 32func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32) 33 34//go:noescape 35func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64) 36 37const ( 38 gcmBlockSize = 16 39 gcmTagSize = 16 40 gcmStandardNonceSize = 12 41) 42 43var errOpen = errors.New("cipher: message authentication failed") 44 45// aesCipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM 46// will use the optimised implementation in this file when possible. Instances 47// of this type only exist when hasGCMAsm returns true. 48type aesCipherGCM struct { 49 aesCipherAsm 50} 51 52// Assert that aesCipherGCM implements the gcmAble interface. 53var _ gcmAble = (*aesCipherGCM)(nil) 54 55// NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only 56// called by crypto/cipher.NewGCM via the gcmAble interface. 57func (c *aesCipherGCM) NewGCM(nonceSize int) (cipher.AEAD, error) { 58 g := &gcmAsm{ks: c.enc, nonceSize: nonceSize} 59 gcmAesInit(&g.productTable, g.ks) 60 return g, nil 61} 62 63type gcmAsm struct { 64 // ks is the key schedule, the length of which depends on the size of 65 // the AES key. 66 ks []uint32 67 // productTable contains pre-computed multiples of the binary-field 68 // element used in GHASH. 69 productTable [256]byte 70 // nonceSize contains the expected size of the nonce, in bytes. 71 nonceSize int 72} 73 74func (g *gcmAsm) NonceSize() int { 75 return g.nonceSize 76} 77 78func (*gcmAsm) Overhead() int { 79 return gcmTagSize 80} 81 82// sliceForAppend takes a slice and a requested number of bytes. It returns a 83// slice with the contents of the given slice followed by that many bytes and a 84// second slice that aliases into it and contains only the extra bytes. If the 85// original slice has sufficient capacity then no allocation is performed. 86func sliceForAppend(in []byte, n int) (head, tail []byte) { 87 if total := len(in) + n; cap(in) >= total { 88 head = in[:total] 89 } else { 90 head = make([]byte, total) 91 copy(head, in) 92 } 93 tail = head[len(in):] 94 return 95} 96 97// Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for 98// details. 99func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte { 100 if len(nonce) != g.nonceSize { 101 panic("cipher: incorrect nonce length given to GCM") 102 } 103 if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize { 104 panic("cipher: message too large for GCM") 105 } 106 107 var counter, tagMask [gcmBlockSize]byte 108 109 if len(nonce) == gcmStandardNonceSize { 110 // Init counter to nonce||1 111 copy(counter[:], nonce) 112 counter[gcmBlockSize-1] = 1 113 } else { 114 // Otherwise counter = GHASH(nonce) 115 gcmAesData(&g.productTable, nonce, &counter) 116 gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0)) 117 } 118 119 aesEncBlock(&tagMask, &counter, g.ks) 120 121 var tagOut [gcmTagSize]byte 122 gcmAesData(&g.productTable, data, &tagOut) 123 124 ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) 125 if len(plaintext) > 0 { 126 gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks) 127 } 128 gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data))) 129 copy(out[len(plaintext):], tagOut[:]) 130 131 return ret 132} 133 134// Open authenticates and decrypts ciphertext. See the cipher.AEAD interface 135// for details. 136func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { 137 if len(nonce) != g.nonceSize { 138 panic("cipher: incorrect nonce length given to GCM") 139 } 140 141 if len(ciphertext) < gcmTagSize { 142 return nil, errOpen 143 } 144 if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize { 145 return nil, errOpen 146 } 147 148 tag := ciphertext[len(ciphertext)-gcmTagSize:] 149 ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] 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 aesEncBlock(&tagMask, &counter, g.ks) 165 166 var expectedTag [gcmTagSize]byte 167 gcmAesData(&g.productTable, data, &expectedTag) 168 169 ret, out := sliceForAppend(dst, len(ciphertext)) 170 if len(ciphertext) > 0 { 171 gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks) 172 } 173 gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data))) 174 175 if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 { 176 for i := range out { 177 out[i] = 0 178 } 179 return nil, errOpen 180 } 181 182 return ret, nil 183} 184