golang/go · error
cipher: message authentication failed
Error message
cipher: message authentication failed
What it means
errOpen is returned by aesGCM.Open when the ciphertext cannot be authenticated — either because it is shorter than the GCM tag (16 bytes), exceeds the maximum GCM plaintext bound, or the authentication tag does not verify during decryption. It is the standard 'cipher: message authentication failed' sentinel reused by crypto/cipher.
Source
Thrown at src/crypto/internal/boring/aes.go:349
if inexactOverlap(dst[n:], plaintext) {
panic("cipher: invalid buffer overlap")
}
outLen := C.size_t(len(plaintext) + gcmTagSize)
ok := C.EVP_AEAD_CTX_seal_wrapper(
&g.ctx,
(*C.uint8_t)(unsafe.Pointer(&dst[n])), outLen,
base(nonce), C.size_t(len(nonce)),
base(plaintext), C.size_t(len(plaintext)),
base(additionalData), C.size_t(len(additionalData)))
runtime.KeepAlive(g)
if ok == 0 {
panic(fail("EVP_AEAD_CTX_seal"))
}
return dst[:n+int(outLen)]
}
var errOpen = errors.New("cipher: message authentication failed")
func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
if len(nonce) != gcmStandardNonceSize {
panic("cipher: incorrect nonce length given to GCM")
}
if len(ciphertext) < gcmTagSize {
return nil, errOpen
}
if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize {
return nil, errOpen
}
// Make room in dst to append ciphertext without tag.
n := len(dst)
for cap(dst) < n+len(ciphertext)-gcmTagSize {
dst = append(dst[:cap(dst)], 0)
}
dst = dst[:n+len(ciphertext)-gcmTagSize]View on GitHub (pinned to b6b368adc5)
Solutions
- Verify the nonce passed to Open matches the one used with Seal (and is unique per message).
- Ensure ciphertext was not truncated — check total length before calling Open.
- Confirm the key and additionalData (AAD) are identical on both sides.
- Treat this error as authentication failure: do not use any plaintext, surface/abort rather than retry.
Example fix
// before
plaintext, err := aead.Open(nil, nonce, cipherText[:15], aad) // shorter than tag -> errOpen
// after
if len(cipherText) < 16 { return errors.New("ciphertext too short") }
plaintext, err := aead.Open(nil, nonce, cipherText, aad) Defensive patterns
Strategy: try-catch
Validate before calling
func safeOpen(aead cipher.AEAD, nonce, ct, aad []byte) ([]byte, error) {
if len(ct) < aead.Overhead() {
return nil, errors.New("ciphertext shorter than tag")
}
return aead.Open(nil, nonce, ct, aad)
} Try / catch
pt, err := aead.Open(nil, nonce, ct, aad)
if err != nil {
if errors.Is(err, cipher.ErrAuth) || strings.Contains(err.Error(), "message authentication failed") {
// authentication failure: discard, abort, do not retry unchanged
return err
}
return err
} Prevention
- Always use a unique nonce per message and pass the same nonce to Open as to Seal.
- Length-check ciphertext before Open.
- Match key and AAD on both sides.
- Never reuse plaintext from a failed Open.
When it happens
Trigger: Calling AEAD.Open with ciphertext shorter than gcmTagSize (16), ciphertext larger than ((1<<32)-2)*blockSize + tagSize, a wrong/truncated nonce, or any bit-flip in ciphertext/tag that breaks GCM authentication.
Common situations: Wrong nonce reuse/mismatch between seal and open; corrupted ciphertext on the wire; truncated messages; replaying ciphertext with a different key; bit-flip injection attacks being correctly rejected.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- crypto/aes: GCM tag and nonce sizes can't be non-standard at
- cipher: message authentication failed
- crypto/cipher: use of GCM with arbitrary IVs is not allowed
- crypto/cipher: use of GCM with non-AES ciphers is not allowe
- cipher: NewGCMWithRandomNonce requires aes.Block
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/ff7d60f1b5f87252.
Report an issue: GitHub.