github.com/peggyl/go@v0.0.0-20151008231540-ae315999c2d5/src/crypto/aes/aes_gcm.go (about)

     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  }