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