| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package gcm |
| |
| import ( |
| "crypto/internal/fips140" |
| "crypto/internal/fips140/aes" |
| "crypto/internal/fips140/alias" |
| "crypto/internal/fips140/drbg" |
| "crypto/internal/fips140deps/byteorder" |
| "errors" |
| "math" |
| ) |
| |
| // SealWithRandomNonce encrypts plaintext to out, and writes a random nonce to |
| // nonce. nonce must be 12 bytes, and out must be 16 bytes longer than plaintext. |
| // out and plaintext may overlap exactly or not at all. additionalData and out |
| // must not overlap. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 2. |
| // |
| // Note that this is NOT a [cipher.AEAD].Seal method. |
| func SealWithRandomNonce(g *GCM, nonce, out, plaintext, additionalData []byte) { |
| if uint64(len(plaintext)) > uint64((1<<32)-2)*gcmBlockSize { |
| panic("crypto/cipher: message too large for GCM") |
| } |
| if len(nonce) != gcmStandardNonceSize { |
| panic("crypto/cipher: incorrect nonce length given to GCMWithRandomNonce") |
| } |
| if len(out) != len(plaintext)+gcmTagSize { |
| panic("crypto/cipher: incorrect output length given to GCMWithRandomNonce") |
| } |
| if alias.InexactOverlap(out, plaintext) { |
| panic("crypto/cipher: invalid buffer overlap of output and input") |
| } |
| if alias.AnyOverlap(out, additionalData) { |
| panic("crypto/cipher: invalid buffer overlap of output and additional data") |
| } |
| fips140.RecordApproved() |
| drbg.Read(nonce) |
| seal(out, g, nonce, plaintext, additionalData) |
| } |
| |
| // NewGCMWithCounterNonce returns a new AEAD that works like GCM, but enforces |
| // the construction of deterministic nonces. The nonce must be 96 bits, the |
| // first 32 bits must be an encoding of the module name, and the last 64 bits |
| // must be a counter. The starting value of the counter is set on the first call |
| // to Seal, and each subsequent call must increment it as a big-endian uint64. |
| // If the counter reaches the starting value minus one, Seal will panic. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 3. |
| func NewGCMWithCounterNonce(cipher *aes.Block) (*GCMWithCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| return &GCMWithCounterNonce{g: *g}, nil |
| } |
| |
| // 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 Scenario 1.a. |
| func NewGCMForTLS12(cipher *aes.Block) (*GCMWithCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| // TLS 1.2 counters always start at zero. |
| return &GCMWithCounterNonce{g: *g, startReady: true}, nil |
| } |
| |
| // NewGCMForSSH returns a new AEAD that works like GCM, but enforces the |
| // construction of nonces as specified in RFC 5647. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 1.d. |
| func NewGCMForSSH(cipher *aes.Block) (*GCMWithCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| return &GCMWithCounterNonce{g: *g}, nil |
| } |
| |
| type GCMWithCounterNonce struct { |
| g GCM |
| prefixReady bool |
| prefix uint32 |
| startReady bool |
| start uint64 |
| next uint64 |
| } |
| |
| func (g *GCMWithCounterNonce) NonceSize() int { return gcmStandardNonceSize } |
| |
| func (g *GCMWithCounterNonce) Overhead() int { return gcmTagSize } |
| |
| // Seal implements the [cipher.AEAD] interface, checking that the nonce prefix |
| // is stable and that the counter is strictly increasing. |
| // |
| // It is not safe for concurrent use. |
| func (g *GCMWithCounterNonce) Seal(dst, nonce, plaintext, data []byte) []byte { |
| if len(nonce) != gcmStandardNonceSize { |
| panic("crypto/cipher: incorrect nonce length given to GCM") |
| } |
| |
| if fips140.Enabled { |
| if !g.prefixReady { |
| // The first invocation sets the fixed prefix. |
| g.prefixReady = true |
| g.prefix = byteorder.BEUint32(nonce[:4]) |
| } |
| if g.prefix != byteorder.BEUint32(nonce[:4]) { |
| panic("crypto/cipher: GCM nonce prefix changed") |
| } |
| |
| counter := byteorder.BEUint64(nonce[len(nonce)-8:]) |
| if !g.startReady { |
| // The first invocation sets the starting counter, if not fixed. |
| g.startReady = true |
| g.start = counter |
| } |
| counter -= g.start |
| |
| // Ensure the counter is strictly increasing. |
| if counter == math.MaxUint64 { |
| panic("crypto/cipher: counter exhausted") |
| } |
| if counter < g.next { |
| panic("crypto/cipher: counter decreased or remained the same") |
| } |
| g.next = counter + 1 |
| } |
| |
| fips140.RecordApproved() |
| return g.g.sealAfterIndicator(dst, nonce, plaintext, data) |
| } |
| |
| func (g *GCMWithCounterNonce) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { |
| fips140.RecordApproved() |
| return g.g.Open(dst, nonce, ciphertext, data) |
| } |
| |
| // NewGCMWithXORCounterNonce returns a new AEAD that works like GCM, but |
| // enforces the construction of deterministic nonces. The nonce must be 96 bits, |
| // the first 32 bits must be an encoding of the module name, and the last 64 |
| // bits must be a counter XOR'd with a fixed value. The module name and XOR mask |
| // can be set with [GCMWithCounterNonce.SetNoncePrefixAndMask], or they are set |
| // on the first call to Seal, assuming the counter starts at zero. Each |
| // subsequent call must increment the counter as a big-endian uint64. If the |
| // counter reaches 2⁶⁴ minus one, Seal will panic. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 3. |
| func NewGCMWithXORCounterNonce(cipher *aes.Block) (*GCMWithXORCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| return &GCMWithXORCounterNonce{g: *g}, nil |
| } |
| |
| // NewGCMForTLS13 returns a new AEAD that works like GCM, but enforces the |
| // construction of nonces as specified in RFC 8446, Section 5.3. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 1.a. |
| func NewGCMForTLS13(cipher *aes.Block) (*GCMWithXORCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| return &GCMWithXORCounterNonce{g: *g}, nil |
| } |
| |
| // NewGCMForHPKE returns a new AEAD that works like GCM, but enforces the |
| // construction of nonces as specified in RFC 9180, Section 5.2. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 5. |
| func NewGCMForHPKE(cipher *aes.Block) (*GCMWithXORCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| return &GCMWithXORCounterNonce{g: *g}, nil |
| } |
| |
| // NewGCMForQUIC returns a new AEAD that works like GCM, but enforces the |
| // construction of nonces as specified in RFC 9001, Section 5.3. |
| // |
| // Unlike in TLS 1.3, the QUIC nonce counter does not always start at zero, as |
| // the packet number does not reset on key updates, so the XOR mask must be |
| // provided explicitly instead of being learned on the first Seal call. Note |
| // that the nonce passed to Seal must already be XOR'd with the IV, the IV is |
| // provided here only to allow Seal to enforce that the counter is strictly |
| // increasing. |
| // |
| // This complies with FIPS 140-3 IG C.H Scenario 5. |
| func NewGCMForQUIC(cipher *aes.Block, iv []byte) (*GCMWithXORCounterNonce, error) { |
| g, err := newGCM(&GCM{}, cipher, gcmStandardNonceSize, gcmTagSize) |
| if err != nil { |
| return nil, err |
| } |
| gcm := &GCMWithXORCounterNonce{g: *g} |
| if err := gcm.SetNoncePrefixAndMask(iv); err != nil { |
| return nil, err |
| } |
| return gcm, nil |
| } |
| |
| type GCMWithXORCounterNonce struct { |
| g GCM |
| ready bool |
| prefix uint32 |
| mask uint64 |
| next uint64 |
| } |
| |
| // SetNoncePrefixAndMask sets the fixed prefix and XOR mask for the nonces used |
| // in Seal. It must be called before the first call to Seal. |
| // |
| // The first 32 bits of nonce are used as the fixed prefix, and the last 64 bits |
| // are used as the XOR mask. |
| // |
| // Note that Seal expects the nonce to be already XOR'd with the mask. The mask |
| // is provided here only to allow Seal to enforce that the counter is strictly |
| // increasing. |
| func (g *GCMWithXORCounterNonce) SetNoncePrefixAndMask(nonce []byte) error { |
| if len(nonce) != gcmStandardNonceSize { |
| return errors.New("crypto/cipher: incorrect nonce length given to SetNoncePrefixAndMask") |
| } |
| if g.ready { |
| return errors.New("crypto/cipher: SetNoncePrefixAndMask called twice or after first Seal") |
| } |
| g.prefix = byteorder.BEUint32(nonce[:4]) |
| g.mask = byteorder.BEUint64(nonce[4:]) |
| g.ready = true |
| return nil |
| } |
| |
| func (g *GCMWithXORCounterNonce) NonceSize() int { return gcmStandardNonceSize } |
| |
| func (g *GCMWithXORCounterNonce) Overhead() int { return gcmTagSize } |
| |
| // Seal implements the [cipher.AEAD] interface, checking that the nonce prefix |
| // is stable and that the counter is strictly increasing. |
| // |
| // It is not safe for concurrent use. |
| func (g *GCMWithXORCounterNonce) Seal(dst, nonce, plaintext, data []byte) []byte { |
| if len(nonce) != gcmStandardNonceSize { |
| panic("crypto/cipher: incorrect nonce length given to GCM") |
| } |
| |
| if fips140.Enabled { |
| counter := byteorder.BEUint64(nonce[len(nonce)-8:]) |
| if !g.ready { |
| // In the first call, if [GCMWithXORCounterNonce.SetNoncePrefixAndMask] |
| // wasn't used, we assume the counter is zero to learn the XOR mask and |
| // fixed prefix. |
| g.ready = true |
| g.mask = counter |
| g.prefix = byteorder.BEUint32(nonce[:4]) |
| } |
| if g.prefix != byteorder.BEUint32(nonce[:4]) { |
| panic("crypto/cipher: GCM nonce prefix changed") |
| } |
| counter ^= g.mask |
| |
| // Ensure the counter is strictly increasing. |
| if counter == math.MaxUint64 { |
| panic("crypto/cipher: counter exhausted") |
| } |
| if counter < g.next { |
| panic("crypto/cipher: counter decreased or remained the same") |
| } |
| g.next = counter + 1 |
| } |
| |
| fips140.RecordApproved() |
| return g.g.sealAfterIndicator(dst, nonce, plaintext, data) |
| } |
| |
| func (g *GCMWithXORCounterNonce) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { |
| fips140.RecordApproved() |
| return g.g.Open(dst, nonce, ciphertext, data) |
| } |