...

Source file src/crypto/aes/aes_gcm.go

Documentation: crypto/aes

		 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
		 6  // +build amd64 arm64
		 7  
		 8  package aes
		 9  
		10  import (
		11  	"crypto/cipher"
		12  	subtleoverlap "crypto/internal/subtle"
		13  	"crypto/subtle"
		14  	"errors"
		15  )
		16  
		17  // The following functions are defined in gcm_*.s.
		18  
		19  //go:noescape
		20  func gcmAesInit(productTable *[256]byte, ks []uint32)
		21  
		22  //go:noescape
		23  func gcmAesData(productTable *[256]byte, data []byte, T *[16]byte)
		24  
		25  //go:noescape
		26  func gcmAesEnc(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
		27  
		28  //go:noescape
		29  func gcmAesDec(productTable *[256]byte, dst, src []byte, ctr, T *[16]byte, ks []uint32)
		30  
		31  //go:noescape
		32  func gcmAesFinish(productTable *[256]byte, tagMask, T *[16]byte, pLen, dLen uint64)
		33  
		34  const (
		35  	gcmBlockSize				 = 16
		36  	gcmTagSize					 = 16
		37  	gcmMinimumTagSize		= 12 // NIST SP 800-38D recommends tags with 12 or more bytes.
		38  	gcmStandardNonceSize = 12
		39  )
		40  
		41  var errOpen = errors.New("cipher: message authentication failed")
		42  
		43  // aesCipherGCM implements crypto/cipher.gcmAble so that crypto/cipher.NewGCM
		44  // will use the optimised implementation in this file when possible. Instances
		45  // of this type only exist when hasGCMAsm returns true.
		46  type aesCipherGCM struct {
		47  	aesCipherAsm
		48  }
		49  
		50  // Assert that aesCipherGCM implements the gcmAble interface.
		51  var _ gcmAble = (*aesCipherGCM)(nil)
		52  
		53  // NewGCM returns the AES cipher wrapped in Galois Counter Mode. This is only
		54  // called by crypto/cipher.NewGCM via the gcmAble interface.
		55  func (c *aesCipherGCM) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
		56  	g := &gcmAsm{ks: c.enc, nonceSize: nonceSize, tagSize: tagSize}
		57  	gcmAesInit(&g.productTable, g.ks)
		58  	return g, nil
		59  }
		60  
		61  type gcmAsm struct {
		62  	// ks is the key schedule, the length of which depends on the size of
		63  	// the AES key.
		64  	ks []uint32
		65  	// productTable contains pre-computed multiples of the binary-field
		66  	// element used in GHASH.
		67  	productTable [256]byte
		68  	// nonceSize contains the expected size of the nonce, in bytes.
		69  	nonceSize int
		70  	// tagSize contains the size of the tag, in bytes.
		71  	tagSize int
		72  }
		73  
		74  func (g *gcmAsm) NonceSize() int {
		75  	return g.nonceSize
		76  }
		77  
		78  func (g *gcmAsm) Overhead() int {
		79  	return g.tagSize
		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.
		86  func 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.
		99  func (g *gcmAsm) Seal(dst, nonce, plaintext, data []byte) []byte {
	 100  	if len(nonce) != g.nonceSize {
	 101  		panic("crypto/cipher: incorrect nonce length given to GCM")
	 102  	}
	 103  	if uint64(len(plaintext)) > ((1<<32)-2)*BlockSize {
	 104  		panic("crypto/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  	encryptBlockAsm(len(g.ks)/4-1, &g.ks[0], &tagMask[0], &counter[0])
	 120  
	 121  	var tagOut [gcmTagSize]byte
	 122  	gcmAesData(&g.productTable, data, &tagOut)
	 123  
	 124  	ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
	 125  	if subtleoverlap.InexactOverlap(out[:len(plaintext)], plaintext) {
	 126  		panic("crypto/cipher: invalid buffer overlap")
	 127  	}
	 128  	if len(plaintext) > 0 {
	 129  		gcmAesEnc(&g.productTable, out, plaintext, &counter, &tagOut, g.ks)
	 130  	}
	 131  	gcmAesFinish(&g.productTable, &tagMask, &tagOut, uint64(len(plaintext)), uint64(len(data)))
	 132  	copy(out[len(plaintext):], tagOut[:])
	 133  
	 134  	return ret
	 135  }
	 136  
	 137  // Open authenticates and decrypts ciphertext. See the cipher.AEAD interface
	 138  // for details.
	 139  func (g *gcmAsm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
	 140  	if len(nonce) != g.nonceSize {
	 141  		panic("crypto/cipher: incorrect nonce length given to GCM")
	 142  	}
	 143  	// Sanity check to prevent the authentication from always succeeding if an implementation
	 144  	// leaves tagSize uninitialized, for example.
	 145  	if g.tagSize < gcmMinimumTagSize {
	 146  		panic("crypto/cipher: incorrect GCM tag size")
	 147  	}
	 148  
	 149  	if len(ciphertext) < g.tagSize {
	 150  		return nil, errOpen
	 151  	}
	 152  	if uint64(len(ciphertext)) > ((1<<32)-2)*uint64(BlockSize)+uint64(g.tagSize) {
	 153  		return nil, errOpen
	 154  	}
	 155  
	 156  	tag := ciphertext[len(ciphertext)-g.tagSize:]
	 157  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
	 158  
	 159  	// See GCM spec, section 7.1.
	 160  	var counter, tagMask [gcmBlockSize]byte
	 161  
	 162  	if len(nonce) == gcmStandardNonceSize {
	 163  		// Init counter to nonce||1
	 164  		copy(counter[:], nonce)
	 165  		counter[gcmBlockSize-1] = 1
	 166  	} else {
	 167  		// Otherwise counter = GHASH(nonce)
	 168  		gcmAesData(&g.productTable, nonce, &counter)
	 169  		gcmAesFinish(&g.productTable, &tagMask, &counter, uint64(len(nonce)), uint64(0))
	 170  	}
	 171  
	 172  	encryptBlockAsm(len(g.ks)/4-1, &g.ks[0], &tagMask[0], &counter[0])
	 173  
	 174  	var expectedTag [gcmTagSize]byte
	 175  	gcmAesData(&g.productTable, data, &expectedTag)
	 176  
	 177  	ret, out := sliceForAppend(dst, len(ciphertext))
	 178  	if subtleoverlap.InexactOverlap(out, ciphertext) {
	 179  		panic("crypto/cipher: invalid buffer overlap")
	 180  	}
	 181  	if len(ciphertext) > 0 {
	 182  		gcmAesDec(&g.productTable, out, ciphertext, &counter, &expectedTag, g.ks)
	 183  	}
	 184  	gcmAesFinish(&g.productTable, &tagMask, &expectedTag, uint64(len(ciphertext)), uint64(len(data)))
	 185  
	 186  	if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
	 187  		for i := range out {
	 188  			out[i] = 0
	 189  		}
	 190  		return nil, errOpen
	 191  	}
	 192  
	 193  	return ret, nil
	 194  }
	 195  

View as plain text