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