Home | History | Annotate | Download | only in aes
      1 // Copyright 2016 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 package aes
      6 
      7 import (
      8 	"crypto/cipher"
      9 	"crypto/subtle"
     10 	"errors"
     11 )
     12 
     13 // gcmCount represents a 16-byte big-endian count value.
     14 type gcmCount [16]byte
     15 
     16 // inc increments the rightmost 32-bits of the count value by 1.
     17 func (x *gcmCount) inc() {
     18 	// The compiler should optimize this to a 32-bit addition.
     19 	n := uint32(x[15]) | uint32(x[14])<<8 | uint32(x[13])<<16 | uint32(x[12])<<24
     20 	n += 1
     21 	x[12] = byte(n >> 24)
     22 	x[13] = byte(n >> 16)
     23 	x[14] = byte(n >> 8)
     24 	x[15] = byte(n)
     25 }
     26 
     27 // gcmLengths writes len0 || len1 as big-endian values to a 16-byte array.
     28 func gcmLengths(len0, len1 uint64) [16]byte {
     29 	return [16]byte{
     30 		byte(len0 >> 56),
     31 		byte(len0 >> 48),
     32 		byte(len0 >> 40),
     33 		byte(len0 >> 32),
     34 		byte(len0 >> 24),
     35 		byte(len0 >> 16),
     36 		byte(len0 >> 8),
     37 		byte(len0),
     38 		byte(len1 >> 56),
     39 		byte(len1 >> 48),
     40 		byte(len1 >> 40),
     41 		byte(len1 >> 32),
     42 		byte(len1 >> 24),
     43 		byte(len1 >> 16),
     44 		byte(len1 >> 8),
     45 		byte(len1),
     46 	}
     47 }
     48 
     49 // gcmHashKey represents the 16-byte hash key required by the GHASH algorithm.
     50 type gcmHashKey [16]byte
     51 
     52 type gcmAsm struct {
     53 	block     *aesCipherAsm
     54 	hashKey   gcmHashKey
     55 	nonceSize int
     56 }
     57 
     58 const (
     59 	gcmBlockSize         = 16
     60 	gcmTagSize           = 16
     61 	gcmStandardNonceSize = 12
     62 )
     63 
     64 var errOpen = errors.New("cipher: message authentication failed")
     65 
     66 // Assert that aesCipherAsm implements the gcmAble interface.
     67 var _ gcmAble = (*aesCipherAsm)(nil)
     68 
     69 // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
     70 // called by crypto/cipher.NewGCM via the gcmAble interface.
     71 func (c *aesCipherAsm) NewGCM(nonceSize int) (cipher.AEAD, error) {
     72 	var hk gcmHashKey
     73 	c.Encrypt(hk[:], hk[:])
     74 	g := &gcmAsm{
     75 		block:     c,
     76 		hashKey:   hk,
     77 		nonceSize: nonceSize,
     78 	}
     79 	return g, nil
     80 }
     81 
     82 func (g *gcmAsm) NonceSize() int {
     83 	return g.nonceSize
     84 }
     85 
     86 func (*gcmAsm) Overhead() int {
     87 	return gcmTagSize
     88 }
     89 
     90 // sliceForAppend takes a slice and a requested number of bytes. It returns a
     91 // slice with the contents of the given slice followed by that many bytes and a
     92 // second slice that aliases into it and contains only the extra bytes. If the
     93 // original slice has sufficient capacity then no allocation is performed.
     94 func sliceForAppend(in []byte, n int) (head, tail []byte) {
     95 	if total := len(in) + n; cap(in) >= total {
     96 		head = in[:total]
     97 	} else {
     98 		head = make([]byte, total)
     99 		copy(head, in)
    100 	}
    101 	tail = head[len(in):]
    102 	return
    103 }
    104 
    105 // ghash uses the GHASH algorithm to hash data with the given key. The initial
    106 // hash value is given by hash which will be updated with the new hash value.
    107 // The length of data must be a multiple of 16-bytes.
    108 //go:noescape
    109 func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
    110 
    111 // paddedGHASH pads data with zeroes until its length is a multiple of
    112 // 16-bytes. It then calculates a new value for hash using the GHASH algorithm.
    113 func (g *gcmAsm) paddedGHASH(hash *[16]byte, data []byte) {
    114 	siz := len(data) &^ 0xf // align size to 16-bytes
    115 	if siz > 0 {
    116 		ghash(&g.hashKey, hash, data[:siz])
    117 		data = data[siz:]
    118 	}
    119 	if len(data) > 0 {
    120 		var s [16]byte
    121 		copy(s[:], data)
    122 		ghash(&g.hashKey, hash, s[:])
    123 	}
    124 }
    125 
    126 // cryptBlocksGCM encrypts src using AES in counter mode using the given
    127 // function code and key. The rightmost 32-bits of the counter are incremented
    128 // between each block as required by the GCM spec. The initial counter value
    129 // is given by cnt, which is updated with the value of the next counter value
    130 // to use.
    131 //
    132 // The lengths of both dst and buf must be greater than or equal to the length
    133 // of src. buf may be partially or completely overwritten during the execution
    134 // of the function.
    135 //go:noescape
    136 func cryptBlocksGCM(fn code, key, dst, src, buf []byte, cnt *gcmCount)
    137 
    138 // counterCrypt encrypts src using AES in counter mode and places the result
    139 // into dst. cnt is the initial count value and will be updated with the next
    140 // count value. The length of dst must be greater than or equal to the length
    141 // of src.
    142 func (g *gcmAsm) counterCrypt(dst, src []byte, cnt *gcmCount) {
    143 	// Copying src into a buffer improves performance on some models when
    144 	// src and dst point to the same underlying array. We also need a
    145 	// buffer for counter values.
    146 	var ctrbuf, srcbuf [2048]byte
    147 	for len(src) >= 16 {
    148 		siz := len(src)
    149 		if len(src) > len(ctrbuf) {
    150 			siz = len(ctrbuf)
    151 		}
    152 		siz &^= 0xf // align siz to 16-bytes
    153 		copy(srcbuf[:], src[:siz])
    154 		cryptBlocksGCM(g.block.function, g.block.key, dst[:siz], srcbuf[:siz], ctrbuf[:], cnt)
    155 		src = src[siz:]
    156 		dst = dst[siz:]
    157 	}
    158 	if len(src) > 0 {
    159 		var x [16]byte
    160 		g.block.Encrypt(x[:], cnt[:])
    161 		for i := range src {
    162 			dst[i] = src[i] ^ x[i]
    163 		}
    164 		cnt.inc()
    165 	}
    166 }
    167 
    168 // deriveCounter computes the initial GCM counter state from the given nonce.
    169 // See NIST SP 800-38D, section 7.1.
    170 func (g *gcmAsm) deriveCounter(nonce []byte) gcmCount {
    171 	// GCM has two modes of operation with respect to the initial counter
    172 	// state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path"
    173 	// for nonces of other lengths. For a 96-bit nonce, the nonce, along
    174 	// with a four-byte big-endian counter starting at one, is used
    175 	// directly as the starting counter. For other nonce sizes, the counter
    176 	// is computed by passing it through the GHASH function.
    177 	var counter gcmCount
    178 	if len(nonce) == gcmStandardNonceSize {
    179 		copy(counter[:], nonce)
    180 		counter[gcmBlockSize-1] = 1
    181 	} else {
    182 		var hash [16]byte
    183 		g.paddedGHASH(&hash, nonce)
    184 		lens := gcmLengths(0, uint64(len(nonce))*8)
    185 		g.paddedGHASH(&hash, lens[:])
    186 		copy(counter[:], hash[:])
    187 	}
    188 	return counter
    189 }
    190 
    191 // auth calculates GHASH(ciphertext, additionalData), masks the result with
    192 // tagMask and writes the result to out.
    193 func (g *gcmAsm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) {
    194 	var hash [16]byte
    195 	g.paddedGHASH(&hash, additionalData)
    196 	g.paddedGHASH(&hash, ciphertext)
    197 	lens := gcmLengths(uint64(len(additionalData))*8, uint64(len(ciphertext))*8)
    198 	g.paddedGHASH(&hash, lens[:])
    199 
    200 	copy(out, hash[:])
    201 	for i := range out {
    202 		out[i] ^= tagMask[i]
    203 	}
    204 }
    205 
    206 // Seal encrypts and authenticates plaintext. See the cipher.AEAD interface for
    207 // details.
    208 func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
    209 	if len(nonce) != g.nonceSize {
    210 		panic("cipher: incorrect nonce length given to GCM")
    211 	}
    212 	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
    213 		panic("cipher: message too large for GCM")
    214 	}
    215 
    216 	ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
    217 
    218 	counter := g.deriveCounter(nonce)
    219 
    220 	var tagMask [gcmBlockSize]byte
    221 	g.block.Encrypt(tagMask[:], counter[:])
    222 	counter.inc()
    223 
    224 	g.counterCrypt(out, plaintext, &counter)
    225 	g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask)
    226 
    227 	return ret
    228 }
    229 
    230 // Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
    231 // for details.
    232 func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
    233 	if len(nonce) != g.nonceSize {
    234 		panic("cipher: incorrect nonce length given to GCM")
    235 	}
    236 	if len(ciphertext) < gcmTagSize {
    237 		return nil, errOpen
    238 	}
    239 	if uint64(len(ciphertext)) > ((1<<32)-2)*BlockSize+gcmTagSize {
    240 		return nil, errOpen
    241 	}
    242 
    243 	tag := ciphertext[len(ciphertext)-gcmTagSize:]
    244 	ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
    245 
    246 	counter := g.deriveCounter(nonce)
    247 
    248 	var tagMask [gcmBlockSize]byte
    249 	g.block.Encrypt(tagMask[:], counter[:])
    250 	counter.inc()
    251 
    252 	var expectedTag [gcmTagSize]byte
    253 	g.auth(expectedTag[:], ciphertext, data, &tagMask)
    254 
    255 	ret, out := sliceForAppend(dst, len(ciphertext))
    256 
    257 	if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 {
    258 		// The AESNI code decrypts and authenticates concurrently, and
    259 		// so overwrites dst in the event of a tag mismatch. That
    260 		// behavior is mimicked here in order to be consistent across
    261 		// platforms.
    262 		for i := range out {
    263 			out[i] = 0
    264 		}
    265 		return nil, errOpen
    266 	}
    267 
    268 	g.counterCrypt(out, ciphertext, &counter)
    269 	return ret, nil
    270 }
    271