diff --git a/src/crypto/internal/fips/aes/gcm/gcm_nonces.go b/src/crypto/internal/fips/aes/gcm/gcm_nonces.go index 3dc02ac07a3..2d3164033ad 100644 --- a/src/crypto/internal/fips/aes/gcm/gcm_nonces.go +++ b/src/crypto/internal/fips/aes/gcm/gcm_nonces.go @@ -5,8 +5,11 @@ package gcm import ( + "crypto/internal/fips/aes" "crypto/internal/fips/alias" "crypto/internal/fips/drbg" + "internal/byteorder" + "math" ) // SealWithRandomNonce encrypts plaintext to out, and writes a random nonce to @@ -36,3 +39,98 @@ func SealWithRandomNonce(g *GCM, nonce, out, plaintext, additionalData []byte) { drbg.Read(nonce) seal(out, g, nonce, plaintext, additionalData) } + +// NewGCMForTLS12 returns a new AEAD that works like GCM, but enforces the +// construction of nonces as specified in RFC 5288, Section 3 and RFC 9325, +// Section 7.2.1. +// +// This complies with FIPS 140-3 IG C.H Resolution 1.a. +func NewGCMForTLS12(cipher *aes.Block) (*GCMForTLS12, error) { + g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) + if err != nil { + return nil, err + } + return &GCMForTLS12{g: *g}, nil +} + +type GCMForTLS12 struct { + g GCM + next uint64 +} + +func (g *GCMForTLS12) NonceSize() int { return gcmStandardNonceSize } + +func (g *GCMForTLS12) Overhead() int { return gcmTagSize } + +func (g *GCMForTLS12) Seal(dst, nonce, plaintext, data []byte) []byte { + if len(nonce) != gcmStandardNonceSize { + panic("crypto/cipher: incorrect nonce length given to GCM") + } + + counter := byteorder.BeUint64(nonce[len(nonce)-8:]) + + // Ensure the counter is monotonically increasing. + if counter == math.MaxUint64 { + panic("crypto/cipher: counter wrapped") + } + if counter < g.next { + panic("crypto/cipher: counter decreased") + } + g.next = counter + 1 + + return g.g.Seal(dst, nonce, plaintext, data) +} + +func (g *GCMForTLS12) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { + return g.g.Open(dst, nonce, ciphertext, data) +} + +// NewGCMForTLS13 returns a new AEAD that works like GCM, but enforces the +// construction of nonces as specified in RFC 8446, Section 5.3. +func NewGCMForTLS13(cipher *aes.Block) (*GCMForTLS13, error) { + g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) + if err != nil { + return nil, err + } + return &GCMForTLS13{g: *g}, nil +} + +type GCMForTLS13 struct { + g GCM + ready bool + mask uint64 + next uint64 +} + +func (g *GCMForTLS13) NonceSize() int { return gcmStandardNonceSize } + +func (g *GCMForTLS13) Overhead() int { return gcmTagSize } + +func (g *GCMForTLS13) Seal(dst, nonce, plaintext, data []byte) []byte { + if len(nonce) != gcmStandardNonceSize { + panic("crypto/cipher: incorrect nonce length given to GCM") + } + + counter := byteorder.BeUint64(nonce[len(nonce)-8:]) + if !g.ready { + // In the first call, the counter is zero, so we learn the XOR mask. + g.ready = true + g.mask = counter + } + counter ^= g.mask + + // Ensure the counter is monotonically increasing. + if counter == math.MaxUint64 { + panic("crypto/cipher: counter wrapped") + } + if counter < g.next { + panic("crypto/cipher: counter decreased") + } + g.next = counter + 1 + + return g.g.Seal(dst, nonce, plaintext, data) +} + +func (g *GCMForTLS13) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { + return g.g.Open(dst, nonce, ciphertext, data) +} diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index ec867ceab0a..1c849e3c274 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -11,6 +11,8 @@ import ( "crypto/des" "crypto/hmac" "crypto/internal/boring" + fipsaes "crypto/internal/fips/aes" + "crypto/internal/fips/aes/gcm" "crypto/rc4" "crypto/sha1" "crypto/sha256" @@ -521,7 +523,7 @@ func aeadAESGCM(key, noncePrefix []byte) aead { aead, err = boring.NewGCMTLS(aes) } else { boring.Unreachable() - aead, err = cipher.NewGCM(aes) + aead, err = gcm.NewGCMForTLS12(aes.(*fipsaes.Block)) } if err != nil { panic(err) @@ -555,7 +557,7 @@ func aeadAESGCMTLS13(key, nonceMask []byte) aead { aead, err = boring.NewGCMTLS13(aes) } else { boring.Unreachable() - aead, err = cipher.NewGCM(aes) + aead, err = gcm.NewGCMForTLS13(aes.(*fipsaes.Block)) } if err != nil { panic(err)