gcm.mx raw

   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  // [crypto/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 = []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 := []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 := []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 = []byte{:total}
 373  		copy(head, in)
 374  	}
 375  	tail = head[len(in):]
 376  	return
 377  }
 378