gcm_nonces.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"
   9  	"crypto/internal/fips140/aes"
  10  	"crypto/internal/fips140/alias"
  11  	"crypto/internal/fips140/drbg"
  12  	"crypto/internal/fips140deps/byteorder"
  13  	"math"
  14  )
  15  
  16  // SealWithRandomNonce encrypts plaintext to out, and writes a random nonce to
  17  // nonce. nonce must be 12 bytes, and out must be 16 bytes longer than plaintext.
  18  // out and plaintext may overlap exactly or not at all. additionalData and out
  19  // must not overlap.
  20  //
  21  // This complies with FIPS 140-3 IG C.H Scenario 2.
  22  //
  23  // Note that this is NOT a [cipher.AEAD].Seal method.
  24  func SealWithRandomNonce(g *GCM, nonce, out, plaintext, additionalData []byte) {
  25  	if uint64(len(plaintext)) > uint64((1<<32)-2)*gcmBlockSize {
  26  		panic("crypto/cipher: message too large for GCM")
  27  	}
  28  	if len(nonce) != gcmStandardNonceSize {
  29  		panic("crypto/cipher: incorrect nonce length given to GCMWithRandomNonce")
  30  	}
  31  	if len(out) != len(plaintext)+gcmTagSize {
  32  		panic("crypto/cipher: incorrect output length given to GCMWithRandomNonce")
  33  	}
  34  	if alias.InexactOverlap(out, plaintext) {
  35  		panic("crypto/cipher: invalid buffer overlap of output and input")
  36  	}
  37  	if alias.AnyOverlap(out, additionalData) {
  38  		panic("crypto/cipher: invalid buffer overlap of output and additional data")
  39  	}
  40  	fips140.RecordApproved()
  41  	drbg.Read(nonce)
  42  	seal(out, g, nonce, plaintext, additionalData)
  43  }
  44  
  45  // NewGCMWithCounterNonce returns a new AEAD that works like GCM, but enforces
  46  // the construction of deterministic nonces. The nonce must be 96 bits, the
  47  // first 32 bits must be an encoding of the module name, and the last 64 bits
  48  // must be a counter.
  49  //
  50  // This complies with FIPS 140-3 IG C.H Scenario 3.
  51  func NewGCMWithCounterNonce(cipher *aes.Block) (*GCMWithCounterNonce, error) {
  52  	g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize)
  53  	if err != nil {
  54  		return nil, err
  55  	}
  56  	return &GCMWithCounterNonce{g: *g}, nil
  57  }
  58  
  59  type GCMWithCounterNonce struct {
  60  	g         GCM
  61  	ready     bool
  62  	fixedName uint32
  63  	start     uint64
  64  	next      uint64
  65  }
  66  
  67  func (g *GCMWithCounterNonce) NonceSize() int { return gcmStandardNonceSize }
  68  
  69  func (g *GCMWithCounterNonce) Overhead() int { return gcmTagSize }
  70  
  71  func (g *GCMWithCounterNonce) Seal(dst, nonce, plaintext, data []byte) []byte {
  72  	if len(nonce) != gcmStandardNonceSize {
  73  		panic("crypto/cipher: incorrect nonce length given to GCM")
  74  	}
  75  
  76  	counter := byteorder.BEUint64(nonce[len(nonce)-8:])
  77  	if !g.ready {
  78  		// The first invocation sets the fixed name encoding and start counter.
  79  		g.ready = true
  80  		g.start = counter
  81  		g.fixedName = byteorder.BEUint32(nonce[:4])
  82  	}
  83  	if g.fixedName != byteorder.BEUint32(nonce[:4]) {
  84  		panic("crypto/cipher: incorrect module name given to GCMWithCounterNonce")
  85  	}
  86  	counter -= g.start
  87  
  88  	// Ensure the counter is monotonically increasing.
  89  	if counter == math.MaxUint64 {
  90  		panic("crypto/cipher: counter wrapped")
  91  	}
  92  	if counter < g.next {
  93  		panic("crypto/cipher: counter decreased")
  94  	}
  95  	g.next = counter + 1
  96  
  97  	fips140.RecordApproved()
  98  	return g.g.sealAfterIndicator(dst, nonce, plaintext, data)
  99  }
 100  
 101  func (g *GCMWithCounterNonce) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 102  	fips140.RecordApproved()
 103  	return g.g.Open(dst, nonce, ciphertext, data)
 104  }
 105  
 106  // NewGCMForTLS12 returns a new AEAD that works like GCM, but enforces the
 107  // construction of nonces as specified in RFC 5288, Section 3 and RFC 9325,
 108  // Section 7.2.1.
 109  //
 110  // This complies with FIPS 140-3 IG C.H Scenario 1.a.
 111  func NewGCMForTLS12(cipher *aes.Block) (*GCMForTLS12, error) {
 112  	g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize)
 113  	if err != nil {
 114  		return nil, err
 115  	}
 116  	return &GCMForTLS12{g: *g}, nil
 117  }
 118  
 119  type GCMForTLS12 struct {
 120  	g    GCM
 121  	next uint64
 122  }
 123  
 124  func (g *GCMForTLS12) NonceSize() int { return gcmStandardNonceSize }
 125  
 126  func (g *GCMForTLS12) Overhead() int { return gcmTagSize }
 127  
 128  func (g *GCMForTLS12) Seal(dst, nonce, plaintext, data []byte) []byte {
 129  	if len(nonce) != gcmStandardNonceSize {
 130  		panic("crypto/cipher: incorrect nonce length given to GCM")
 131  	}
 132  
 133  	counter := byteorder.BEUint64(nonce[len(nonce)-8:])
 134  
 135  	// Ensure the counter is monotonically increasing.
 136  	if counter == math.MaxUint64 {
 137  		panic("crypto/cipher: counter wrapped")
 138  	}
 139  	if counter < g.next {
 140  		panic("crypto/cipher: counter decreased")
 141  	}
 142  	g.next = counter + 1
 143  
 144  	fips140.RecordApproved()
 145  	return g.g.sealAfterIndicator(dst, nonce, plaintext, data)
 146  }
 147  
 148  func (g *GCMForTLS12) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 149  	fips140.RecordApproved()
 150  	return g.g.Open(dst, nonce, ciphertext, data)
 151  }
 152  
 153  // NewGCMForTLS13 returns a new AEAD that works like GCM, but enforces the
 154  // construction of nonces as specified in RFC 8446, Section 5.3.
 155  func NewGCMForTLS13(cipher *aes.Block) (*GCMForTLS13, error) {
 156  	g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize)
 157  	if err != nil {
 158  		return nil, err
 159  	}
 160  	return &GCMForTLS13{g: *g}, nil
 161  }
 162  
 163  type GCMForTLS13 struct {
 164  	g     GCM
 165  	ready bool
 166  	mask  uint64
 167  	next  uint64
 168  }
 169  
 170  func (g *GCMForTLS13) NonceSize() int { return gcmStandardNonceSize }
 171  
 172  func (g *GCMForTLS13) Overhead() int { return gcmTagSize }
 173  
 174  func (g *GCMForTLS13) Seal(dst, nonce, plaintext, data []byte) []byte {
 175  	if len(nonce) != gcmStandardNonceSize {
 176  		panic("crypto/cipher: incorrect nonce length given to GCM")
 177  	}
 178  
 179  	counter := byteorder.BEUint64(nonce[len(nonce)-8:])
 180  	if !g.ready {
 181  		// In the first call, the counter is zero, so we learn the XOR mask.
 182  		g.ready = true
 183  		g.mask = counter
 184  	}
 185  	counter ^= g.mask
 186  
 187  	// Ensure the counter is monotonically increasing.
 188  	if counter == math.MaxUint64 {
 189  		panic("crypto/cipher: counter wrapped")
 190  	}
 191  	if counter < g.next {
 192  		panic("crypto/cipher: counter decreased")
 193  	}
 194  	g.next = counter + 1
 195  
 196  	fips140.RecordApproved()
 197  	return g.g.sealAfterIndicator(dst, nonce, plaintext, data)
 198  }
 199  
 200  func (g *GCMForTLS13) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 201  	fips140.RecordApproved()
 202  	return g.g.Open(dst, nonce, ciphertext, data)
 203  }
 204  
 205  // NewGCMForSSH returns a new AEAD that works like GCM, but enforces the
 206  // construction of nonces as specified in RFC 5647.
 207  //
 208  // This complies with FIPS 140-3 IG C.H Scenario 1.d.
 209  func NewGCMForSSH(cipher *aes.Block) (*GCMForSSH, error) {
 210  	g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize)
 211  	if err != nil {
 212  		return nil, err
 213  	}
 214  	return &GCMForSSH{g: *g}, nil
 215  }
 216  
 217  type GCMForSSH struct {
 218  	g     GCM
 219  	ready bool
 220  	start uint64
 221  	next  uint64
 222  }
 223  
 224  func (g *GCMForSSH) NonceSize() int { return gcmStandardNonceSize }
 225  
 226  func (g *GCMForSSH) Overhead() int { return gcmTagSize }
 227  
 228  func (g *GCMForSSH) Seal(dst, nonce, plaintext, data []byte) []byte {
 229  	if len(nonce) != gcmStandardNonceSize {
 230  		panic("crypto/cipher: incorrect nonce length given to GCM")
 231  	}
 232  
 233  	counter := byteorder.BEUint64(nonce[len(nonce)-8:])
 234  	if !g.ready {
 235  		// In the first call we learn the start value.
 236  		g.ready = true
 237  		g.start = counter
 238  	}
 239  	counter -= g.start
 240  
 241  	// Ensure the counter is monotonically increasing.
 242  	if counter == math.MaxUint64 {
 243  		panic("crypto/cipher: counter wrapped")
 244  	}
 245  	if counter < g.next {
 246  		panic("crypto/cipher: counter decreased")
 247  	}
 248  	g.next = counter + 1
 249  
 250  	fips140.RecordApproved()
 251  	return g.g.sealAfterIndicator(dst, nonce, plaintext, data)
 252  }
 253  
 254  func (g *GCMForSSH) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
 255  	fips140.RecordApproved()
 256  	return g.g.Open(dst, nonce, ciphertext, data)
 257  }
 258