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

View as plain text