Source file src/crypto/internal/fips140/aes/gcm/gcm_nonces.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 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  

View as plain text