gcm_generic.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 gcm
   6  
   7  import (
   8  	"crypto/internal/fips140/aes"
   9  	"crypto/internal/fips140/subtle"
  10  	"crypto/internal/fips140deps/byteorder"
  11  )
  12  
  13  func sealGeneric(out []byte, g *GCM, nonce, plaintext, additionalData []byte) {
  14  	var H, counter, tagMask [gcmBlockSize]byte
  15  	aes.EncryptBlockInternal(&g.cipher, H[:], H[:])
  16  	deriveCounterGeneric(&H, &counter, nonce)
  17  	gcmCounterCryptGeneric(&g.cipher, tagMask[:], tagMask[:], &counter)
  18  
  19  	gcmCounterCryptGeneric(&g.cipher, out, plaintext, &counter)
  20  
  21  	var tag [gcmTagSize]byte
  22  	gcmAuthGeneric(tag[:], &H, &tagMask, out[:len(plaintext)], additionalData)
  23  	copy(out[len(plaintext):], tag[:])
  24  }
  25  
  26  func openGeneric(out []byte, g *GCM, nonce, ciphertext, additionalData []byte) error {
  27  	var H, counter, tagMask [gcmBlockSize]byte
  28  	aes.EncryptBlockInternal(&g.cipher, H[:], H[:])
  29  	deriveCounterGeneric(&H, &counter, nonce)
  30  	gcmCounterCryptGeneric(&g.cipher, tagMask[:], tagMask[:], &counter)
  31  
  32  	tag := ciphertext[len(ciphertext)-g.tagSize:]
  33  	ciphertext = ciphertext[:len(ciphertext)-g.tagSize]
  34  
  35  	var expectedTag [gcmTagSize]byte
  36  	gcmAuthGeneric(expectedTag[:], &H, &tagMask, ciphertext, additionalData)
  37  	if subtle.ConstantTimeCompare(expectedTag[:g.tagSize], tag) != 1 {
  38  		return errOpen
  39  	}
  40  
  41  	gcmCounterCryptGeneric(&g.cipher, out, ciphertext, &counter)
  42  
  43  	return nil
  44  }
  45  
  46  // deriveCounterGeneric computes the initial GCM counter state from the given nonce.
  47  // See NIST SP 800-38D, section 7.1. This assumes that counter is filled with
  48  // zeros on entry.
  49  func deriveCounterGeneric(H, counter *[gcmBlockSize]byte, nonce []byte) {
  50  	// GCM has two modes of operation with respect to the initial counter
  51  	// state: a "fast path" for 96-bit (12-byte) nonces, and a "slow path"
  52  	// for nonces of other lengths. For a 96-bit nonce, the nonce, along
  53  	// with a four-byte big-endian counter starting at one, is used
  54  	// directly as the starting counter. For other nonce sizes, the counter
  55  	// is computed by passing it through the GHASH function.
  56  	if len(nonce) == gcmStandardNonceSize {
  57  		copy(counter[:], nonce)
  58  		counter[gcmBlockSize-1] = 1
  59  	} else {
  60  		lenBlock := []byte{:16}
  61  		byteorder.BEPutUint64(lenBlock[8:], uint64(len(nonce))*8)
  62  		ghash(counter, H, nonce, lenBlock)
  63  	}
  64  }
  65  
  66  // gcmCounterCryptGeneric encrypts src using AES in counter mode with 32-bit
  67  // wrapping (which is different from AES-CTR) and places the result into out.
  68  // counter is the initial value and will be updated with the next value.
  69  func gcmCounterCryptGeneric(b *aes.Block, out, src []byte, counter *[gcmBlockSize]byte) {
  70  	var mask [gcmBlockSize]byte
  71  
  72  	for len(src) >= gcmBlockSize {
  73  		aes.EncryptBlockInternal(b, mask[:], counter[:])
  74  		gcmInc32(counter)
  75  
  76  		subtle.XORBytes(out, src, mask[:])
  77  		out = out[gcmBlockSize:]
  78  		src = src[gcmBlockSize:]
  79  	}
  80  
  81  	if len(src) > 0 {
  82  		aes.EncryptBlockInternal(b, mask[:], counter[:])
  83  		gcmInc32(counter)
  84  		subtle.XORBytes(out, src, mask[:])
  85  	}
  86  }
  87  
  88  // gcmInc32 treats the final four bytes of counterBlock as a big-endian value
  89  // and increments it.
  90  func gcmInc32(counterBlock *[gcmBlockSize]byte) {
  91  	ctr := counterBlock[len(counterBlock)-4:]
  92  	byteorder.BEPutUint32(ctr, byteorder.BEUint32(ctr)+1)
  93  }
  94  
  95  // gcmAuthGeneric calculates GHASH(additionalData, ciphertext), masks the result
  96  // with tagMask and writes the result to out.
  97  func gcmAuthGeneric(out []byte, H, tagMask *[gcmBlockSize]byte, ciphertext, additionalData []byte) {
  98  	checkGenericIsExpected()
  99  	lenBlock := []byte{:16}
 100  	byteorder.BEPutUint64(lenBlock[:8], uint64(len(additionalData))*8)
 101  	byteorder.BEPutUint64(lenBlock[8:], uint64(len(ciphertext))*8)
 102  	var S [gcmBlockSize]byte
 103  	ghash(&S, H, additionalData, ciphertext, lenBlock)
 104  	subtle.XORBytes(out, S[:], tagMask[:])
 105  }
 106