golang/go · error

crypto/cipher: use of GCM with non-AES ciphers is not allowe

Error message

crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode

What it means

Inside newGCM (the internal constructor shared by NewGCM/NewGCMWithNonceSize/NewGCMWithTagSize), a type assertion checks that cipher is *aes.Block. If it is not and FIPS-only mode is on, the constructor refuses: FIPS 140-3 only certifies AES-based GCM. The non-AES fallback (newGCMFallback) is unreachable in FIPS-only mode.

Source

Thrown at src/crypto/cipher/gcm.go:66

// Counter Mode, which generates tags with the given length.
//
// Tag sizes between 12 and 16 bytes are allowed.
//
// Only use this function if you require compatibility with an existing
// cryptosystem that uses non-standard tag lengths. All other users should use
// [NewGCM], which is more resistant to misuse.
func NewGCMWithTagSize(cipher Block, tagSize int) (AEAD, error) {
	if fips140only.Enforced() {
		return nil, errors.New("crypto/cipher: use of GCM with arbitrary IVs is not allowed in FIPS 140-only mode, use NewGCMWithRandomNonce")
	}
	return newGCM(cipher, gcmStandardNonceSize, tagSize)
}

func newGCM(cipher Block, nonceSize, tagSize int) (AEAD, error) {
	c, ok := cipher.(*aes.Block)
	if !ok {
		if fips140only.Enforced() {
			return nil, errors.New("crypto/cipher: use of GCM with non-AES ciphers is not allowed in FIPS 140-only mode")
		}
		return newGCMFallback(cipher, nonceSize, tagSize)
	}
	// We don't return gcm.New directly, because it would always return a non-nil
	// AEAD interface value with type *gcm.GCM even if the *gcm.GCM is nil.
	g, err := gcm.New(c, nonceSize, tagSize)
	if err != nil {
		return nil, err
	}
	return g, nil
}

// NewGCMWithRandomNonce returns the given cipher wrapped in Galois Counter
// Mode, with randomly-generated nonces. The cipher must have been created by
// [crypto/aes.NewCipher].
//
// It generates a random 96-bit nonce, which is prepended to the ciphertext by Seal,
// and is extracted from the ciphertext by Open. The NonceSize of the AEAD is zero,

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Construct the block with aes.NewCipher(key) so the value passed to NewGCM* is *aes.Block.
  2. Drop non-AES cipher support entirely — GCM is only NIST-approved for AES anyway.
  3. For test stubs, gate the non-AES path behind a build tag that disables FIPS-only in tests.

Example fix

// before
block, _ := des.NewCipher(key) // non-AES
a, err := cipher.NewGCM(block) // "non-AES ciphers is not allowed"

// after
block, _ := aes.NewCipher(aesKey)
a, err := cipher.NewGCMWithRandomNonce(block)
Defensive patterns

Strategy: type-guard

Validate before calling

func newAEADFIPS(block cipher.Block) (cipher.AEAD, error) {
    if _, ok := block.(*aes.Block); !ok {
        return nil, errors.New("FIPS GCM requires an *aes.Block; construct with aes.NewCipher")
    }
    return cipher.NewGCMWithRandomNonce(block)
}

Type guard

func isAESBlock(b cipher.Block) bool {
    _, ok := b.(*aes.Block)
    return ok
}

Try / catch

a, err := cipher.NewGCM(block)
if err != nil && strings.Contains(err.Error(), "non-AES ciphers") {
    aesBlock, _ := aes.NewCipher(aesKey)
    a, err = cipher.NewGCMWithRandomNonce(aesBlock)
}

Prevention

When it happens

Trigger: Calling any NewGCM* constructor with a Block that is not *aes.Block (e.g., a *des.NonceSize, a *camellia block, or a custom Block implementation) while FIPS-only mode is enforced. Reachable when a caller smuggles a non-AES block through the Block interface.

Common situations: Generic encryption wrappers that accept cipher.Block and feed it to NewGCM; legacy code paths that still construct DES/Blowfish blocks; testing stubs that implement Block but are not AES.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/eabdd8d16cdc2186. Report an issue: GitHub.