...

Source file src/pkg/crypto/aes/aes_gcm.go

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

View as plain text