Source file src/crypto/cipher/gcm.go

     1  // Copyright 2024 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 cipher
     6  
     7  import (
     8  	"crypto/internal/fips140/aes"
     9  	"crypto/internal/fips140/aes/gcm"
    10  	"crypto/internal/fips140/alias"
    11  	"crypto/internal/fips140only"
    12  	"crypto/subtle"
    13  	"errors"
    14  	"internal/byteorder"
    15  )
    16  
    17  const (
    18  	gcmBlockSize         = 16
    19  	gcmStandardNonceSize = 12
    20  	gcmTagSize           = 16
    21  	gcmMinimumTagSize    = 12 // NIST SP 800-38D recommends tags with 12 or more bytes.
    22  )
    23  
    24  // NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode
    25  // with the standard nonce length.
    26  //
    27  // In general, the GHASH operation performed by this implementation of GCM is not constant-time.
    28  // An exception is when the underlying [Block] was created by aes.NewCipher
    29  // on systems with hardware support for AES. See the [crypto/aes] package documentation for details.
    30  func NewGCM(cipher Block) (AEAD, error) {
    31  	if fips140only.Enabled {
    32  		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
    33  	}
    34  	return newGCM(cipher, gcmStandardNonceSize, gcmTagSize)
    35  }
    36  
    37  // NewGCMWithNonceSize returns the given 128-bit, block cipher wrapped in Galois
    38  // Counter Mode, which accepts nonces of the given length. The length must not
    39  // be zero.
    40  //
    41  // Only use this function if you require compatibility with an existing
    42  // cryptosystem that uses non-standard nonce lengths. All other users should use
    43  // [NewGCM], which is faster and more resistant to misuse.
    44  func NewGCMWithNonceSize(cipher Block, size int) (AEAD, error) {
    45  	if fips140only.Enabled {
    46  		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
    47  	}
    48  	return newGCM(cipher, size, gcmTagSize)
    49  }
    50  
    51  // NewGCMWithTagSize returns the given 128-bit, block cipher wrapped in Galois
    52  // Counter Mode, which generates tags with the given length.
    53  //
    54  // Tag sizes between 12 and 16 bytes are allowed.
    55  //
    56  // Only use this function if you require compatibility with an existing
    57  // cryptosystem that uses non-standard tag lengths. All other users should use
    58  // [NewGCM], which is more resistant to misuse.
    59  func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) {
    60  	if fips140only.Enabled {
    61  		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
    62  	}
    63  	return newGCM(cipher, gcmStandardNonceSize, tagSize)
    64  }
    65  
    66  func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) {
    67  	c, ok := cipher.(*aes.Block)
    68  	if !ok {
    69  		if fips140only.Enabled {
    70  			return nil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode")
    71  		}
    72  		return newGCMFallback(cipher, nonceSize, tagSize)
    73  	}
    74  	// We don't return gcm.New directly, because it would always return a non-nil
    75  	// AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil.
    76  	g, err := gcm.New(c, nonceSize, tagSize)
    77  	if err != nil {
    78  		return nil, err
    79  	}
    80  	return g, nil
    81  }
    82  
    83  // NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
    84  // Mode, with randomly-generated nonces. The cipher must have been created by
    85  // [aes.NewCipher].
    86  //
    87  // It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
    88  // and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,
    89  // while the Overhead is 28 bytes (the combination of nonce size and tag size).
    90  //
    91  // A given key MUST NOT be used to encrypt more than 2^32 messages, to limit the
    92  // risk of a random nonce collision to negligible levels.
    93  func NewGCMWithRandomNonce(cipher Block) (AEAD, error) {
    94  	c, ok := cipher.(*aes.Block)
    95  	if !ok {
    96  		return nil, errors.New("cipher: NewGCMWithRandomNonce requires aes.Block")
    97  	}
    98  	g, err := gcm.New(c, gcmStandardNonceSize, gcmTagSize)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	return gcmWithRandomNonce{g}, nil
   103  }
   104  
   105  type gcmWithRandomNonce struct {
   106  	*gcm.GCM
   107  }
   108  
   109  func (g gcmWithRandomNonce) NonceSize() int {
   110  	return 0
   111  }
   112  
   113  func (g gcmWithRandomNonce) Overhead() int {
   114  	return gcmStandardNonceSize + gcmTagSize
   115  }
   116  
   117  func (g gcmWithRandomNonce) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
   118  	if len(nonce) != 0 {
   119  		panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
   120  	}
   121  
   122  	ret, out := sliceForAppend(dst, gcmStandardNonceSize+len(plaintext)+gcmTagSize)
   123  	if alias.InexactOverlap(out, plaintext) {
   124  		panic("crypto/cipher: invalid buffer overlap of output and input")
   125  	}
   126  	if alias.AnyOverlap(out, additionalData) {
   127  		panic("crypto/cipher: invalid buffer overlap of output and additional data")
   128  	}
   129  	nonce = out[:gcmStandardNonceSize]
   130  	ciphertext := out[gcmStandardNonceSize:]
   131  
   132  	// The AEAD interface allows using plaintext[:0] or ciphertext[:0] as dst.
   133  	//
   134  	// This is kind of a problem when trying to prepend or trim a nonce, because the
   135  	// actual AES-GCTR blocks end up overlapping but not exactly.
   136  	//
   137  	// In Open, we write the output *before* the input, so unless we do something
   138  	// weird like working through a chunk of block backwards, it works out.
   139  	//
   140  	// In Seal, we could work through the input backwards or intentionally load
   141  	// ahead before writing.
   142  	//
   143  	// However, the crypto/internal/fips140/aes/gcm APIs also check for exact overlap,
   144  	// so for now we just do a memmove if we detect overlap.
   145  	//
   146  	//     ┌───────────────────────────┬ ─ ─
   147  	//     │PPPPPPPPPPPPPPPPPPPPPPPPPPP│    │
   148  	//     └▽─────────────────────────▲┴ ─ ─
   149  	//       ╲ Seal                    ╲
   150  	//        ╲                    Open ╲
   151  	//     ┌───▼─────────────────────────△──┐
   152  	//     │NN|CCCCCCCCCCCCCCCCCCCCCCCCCCC|T│
   153  	//     └────────────────────────────────┘
   154  	//
   155  	if alias.AnyOverlap(out, plaintext) {
   156  		copy(ciphertext, plaintext)
   157  		plaintext = ciphertext[:len(plaintext)]
   158  	}
   159  
   160  	gcm.SealWithRandomNonce(g.GCM, nonce, ciphertext, plaintext, additionalData)
   161  	return ret
   162  }
   163  
   164  func (g gcmWithRandomNonce) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
   165  	if len(nonce) != 0 {
   166  		panic("crypto/cipher: non-empty nonce passed to GCMWithRandomNonce")
   167  	}
   168  	if len(ciphertext) < gcmStandardNonceSize+gcmTagSize {
   169  		return nil, errOpen
   170  	}
   171  
   172  	ret, out := sliceForAppend(dst, len(ciphertext)-gcmStandardNonceSize-gcmTagSize)
   173  	if alias.InexactOverlap(out, ciphertext) {
   174  		panic("crypto/cipher: invalid buffer overlap of output and input")
   175  	}
   176  	if alias.AnyOverlap(out, additionalData) {
   177  		panic("crypto/cipher: invalid buffer overlap of output and additional data")
   178  	}
   179  	// See the discussion in Seal. Note that if there is any overlap at this
   180  	// point, it's because out = ciphertext, so out must have enough capacity
   181  	// even if we sliced the tag off. Also note how [AEAD] specifies that "the
   182  	// contents of dst, up to its capacity, may be overwritten".
   183  	if alias.AnyOverlap(out, ciphertext) {
   184  		nonce = make([]byte, gcmStandardNonceSize)
   185  		copy(nonce, ciphertext)
   186  		copy(out[:len(ciphertext)], ciphertext[gcmStandardNonceSize:])
   187  		ciphertext = out[:len(ciphertext)-gcmStandardNonceSize]
   188  	} else {
   189  		nonce = ciphertext[:gcmStandardNonceSize]
   190  		ciphertext = ciphertext[gcmStandardNonceSize:]
   191  	}
   192  
   193  	_, err := g.GCM.Open(out[:0], nonce, ciphertext, additionalData)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	return ret, nil
   198  }
   199  
   200  // gcmAble is an interface implemented by ciphers that have a specific optimized
   201  // implementation of GCM. crypto/aes doesn't use this anymore, and we'd like to
   202  // eventually remove it.
   203  type gcmAble interface {
   204  	NewGCM(nonceSize, tagSize int) (AEAD, error)
   205  }
   206  
   207  func newGCMFallback(cipher Block, nonceSize, tagSize int) (AEAD, error) {
   208  	if tagSize < gcmMinimumTagSize || tagSize > gcmBlockSize {
   209  		return nil, errors.New("cipher: incorrect tag size given to GCM")
   210  	}
   211  	if nonceSize <= 0 {
   212  		return nil, errors.New("cipher: the nonce can't have zero length")
   213  	}
   214  	if cipher, ok := cipher.(gcmAble); ok {
   215  		return cipher.NewGCM(nonceSize, tagSize)
   216  	}
   217  	if cipher.BlockSize() != gcmBlockSize {
   218  		return nil, errors.New("cipher: NewGCM requires 128-bit block cipher")
   219  	}
   220  	return &gcmFallback{cipher: cipher, nonceSize: nonceSize, tagSize: tagSize}, nil
   221  }
   222  
   223  // gcmFallback is only used for non-AES ciphers, which regrettably we
   224  // theoretically support. It's a copy of the generic implementation from
   225  // crypto/internal/fips140/aes/gcm/gcm_generic.go, refer to that file for more details.
   226  type gcmFallback struct {
   227  	cipher    Block
   228  	nonceSize int
   229  	tagSize   int
   230  }
   231  
   232  func (g *gcmFallback) NonceSize() int {
   233  	return g.nonceSize
   234  }
   235  
   236  func (g *gcmFallback) Overhead() int {
   237  	return g.tagSize
   238  }
   239  
   240  func (g *gcmFallback) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
   241  	if len(nonce) != g.nonceSize {
   242  		panic("crypto/cipher: incorrect nonce length given to GCM")
   243  	}
   244  	if g.nonceSize == 0 {
   245  		panic("crypto/cipher: incorrect GCM nonce size")
   246  	}
   247  	if uint64(len(plaintext)) > uint64((1<<32)-2)*gcmBlockSize {
   248  		panic("crypto/cipher: message too large for GCM")
   249  	}
   250  
   251  	ret, out := sliceForAppend(dst, len(plaintext)+g.tagSize)
   252  	if alias.InexactOverlap(out, plaintext) {
   253  		panic("crypto/cipher: invalid buffer overlap of output and input")
   254  	}
   255  	if alias.AnyOverlap(out, additionalData) {
   256  		panic("crypto/cipher: invalid buffer overlap of output and additional data")
   257  	}
   258  
   259  	var H, counter, tagMask [gcmBlockSize]byte
   260  	g.cipher.Encrypt(H[:], H[:])
   261  	deriveCounter(&H, &counter, nonce)
   262  	gcmCounterCryptGeneric(g.cipher, tagMask[:], tagMask[:], &counter)
   263  
   264  	gcmCounterCryptGeneric(g.cipher, out, plaintext, &counter)
   265  
   266  	var tag [gcmTagSize]byte
   267  	gcmAuth(tag[:], &H, &tagMask, out[:len(plaintext)], additionalData)
   268  	copy(out[len(plaintext):], tag[:])
   269  
   270  	return ret
   271  }
   272  
   273  var errOpen = errors.New("cipher: message authentication failed")
   274  
   275  func (g *gcmFallback) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
   276  	if len(nonce) != g.nonceSize {
   277  		panic("crypto/cipher: incorrect nonce length given to GCM")
   278  	}
   279  	if g.tagSize < gcmMinimumTagSize {
   280  		panic("crypto/cipher: incorrect GCM tag size")
   281  	}
   282  
   283  	if len(ciphertext) < g.tagSize {
   284  		return nil, errOpen
   285  	}
   286  	if uint64(len(ciphertext)) > uint64((1<<32)-2)*gcmBlockSize+uint64(g.tagSize) {
   287  		return nil, errOpen
   288  	}
   289  
   290  	ret, out := sliceForAppend(dst, len(ciphertext)-g.tagSize)
   291  	if alias.InexactOverlap(out, ciphertext) {
   292  		panic("crypto/cipher: invalid buffer overlap of output and input")
   293  	}
   294  	if alias.AnyOverlap(out, additionalData) {
   295  		panic("crypto/cipher: invalid buffer overlap of output and additional data")
   296  	}
   297  
   298  	var H, counter, tagMask [gcmBlockSize]byte
   299  	g.cipher.Encrypt(H[:], H[:])
   300  	deriveCounter(&H, &counter, nonce)
   301  	gcmCounterCryptGeneric(g.cipher, tagMask[:], tagMask[:], &counter)
   302  
   303  	tag := ciphertext[len(ciphertext)-g.tagSize:]
   304  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
   305  
   306  	var expectedTag [gcmTagSize]byte
   307  	gcmAuth(expectedTag[:], &H, &tagMask, ciphertext, additionalData)
   308  	if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
   309  		// We sometimes decrypt and authenticate concurrently, so we overwrite
   310  		// dst in the event of a tag mismatch. To be consistent across platforms
   311  		// and to avoid releasing unauthenticated plaintext, we clear the buffer
   312  		// in the event of an error.
   313  		clear(out)
   314  		return nil, errOpen
   315  	}
   316  
   317  	gcmCounterCryptGeneric(g.cipher, out, ciphertext, &counter)
   318  
   319  	return ret, nil
   320  }
   321  
   322  func deriveCounter(H, counter *[gcmBlockSize]byte, nonce []byte) {
   323  	if len(nonce) == gcmStandardNonceSize {
   324  		copy(counter[:], nonce)
   325  		counter[gcmBlockSize-1] = 1
   326  	} else {
   327  		lenBlock := make([]byte, 16)
   328  		byteorder.BEPutUint64(lenBlock[8:], uint64(len(nonce))*8)
   329  		J := gcm.GHASH(H, nonce, lenBlock)
   330  		copy(counter[:], J)
   331  	}
   332  }
   333  
   334  func gcmCounterCryptGeneric(b Block, out, src []byte, counter *[gcmBlockSize]byte) {
   335  	var mask [gcmBlockSize]byte
   336  	for len(src) >= gcmBlockSize {
   337  		b.Encrypt(mask[:], counter[:])
   338  		gcmInc32(counter)
   339  
   340  		subtle.XORBytes(out, src, mask[:])
   341  		out = out[gcmBlockSize:]
   342  		src = src[gcmBlockSize:]
   343  	}
   344  	if len(src) > 0 {
   345  		b.Encrypt(mask[:], counter[:])
   346  		gcmInc32(counter)
   347  		subtle.XORBytes(out, src, mask[:])
   348  	}
   349  }
   350  
   351  func gcmInc32(counterBlock *[gcmBlockSize]byte) {
   352  	ctr := counterBlock[len(counterBlock)-4:]
   353  	byteorder.BEPutUint32(ctr, byteorder.BEUint32(ctr)+1)
   354  }
   355  
   356  func gcmAuth(out []byte, H, tagMask *[gcmBlockSize]byte, ciphertext, additionalData []byte) {
   357  	lenBlock := make([]byte, 16)
   358  	byteorder.BEPutUint64(lenBlock[:8], uint64(len(additionalData))*8)
   359  	byteorder.BEPutUint64(lenBlock[8:], uint64(len(ciphertext))*8)
   360  	S := gcm.GHASH(H, additionalData, ciphertext, lenBlock)
   361  	subtle.XORBytes(out, S, tagMask[:])
   362  }
   363  
   364  // sliceForAppend takes a slice and a requested number of bytes. It returns a
   365  // slice with the contents of the given slice followed by that many bytes and a
   366  // second slice that aliases into it and contains only the extra bytes. If the
   367  // original slice has sufficient capacity then no allocation is performed.
   368  func sliceForAppend(in []byte, n int) (head, tail []byte) {
   369  	if total := len(in) + n; cap(in) >= total {
   370  		head = in[:total]
   371  	} else {
   372  		head = make([]byte, total)
   373  		copy(head, in)
   374  	}
   375  	tail = head[len(in):]
   376  	return
   377  }
   378  

View as plain text