fix(encryption): return an error instead of panicking on a short GCM ciphertext (#3527)

* fix(encryption): return an error instead of panicking on a short GCM ciphertext

gcmCipher.Decrypt slices ciphertext[:nonceSize] without first checking the
length, so a ciphertext shorter than the 12-byte GCM nonce triggers a
slice-bounds-out-of-range panic instead of returning an error. The sibling
cfbCipher.Decrypt already guards its IV length and returns a descriptive error;
mirror that guard for GCM so decrypting a malformed (e.g. truncated) cookie
fails cleanly. Adds a test covering both ciphers.

Signed-off-by: Madan Kumar <winklemad@outlook.com>

* docs(changelog): add entry for the GCM short-ciphertext fix

Signed-off-by: Madan Kumar <winklemad@outlook.com>

---------

Signed-off-by: Madan Kumar <winklemad@outlook.com>
This commit is contained in:
Madan Kumar
2026-10-01 09:18:17 +02:00
committed by GitHub
parent 4b94ed869b
commit e05f04ef08
3 changed files with 23 additions and 0 deletions
+3
View File
@@ -122,6 +122,9 @@ func (c *gcmCipher) Decrypt(ciphertext []byte) ([]byte, error) {
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("encrypted value should be at least %d bytes, but is only %d bytes", nonceSize, len(ciphertext))
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
+18
View File
@@ -89,6 +89,24 @@ func TestEncryptAndDecrypt(t *testing.T) {
}
}
func TestDecryptShortCiphertextReturnsError(t *testing.T) {
// A ciphertext shorter than the cipher's IV/nonce prefix must yield an
// error, not a slice-bounds panic. CFB already guarded this; GCM must too.
cipherInits := map[string]func([]byte) (Cipher, error){
"CFB": NewCFBCipher,
"GCM": NewGCMCipher,
}
for name, initCipher := range cipherInits {
t.Run(name, func(t *testing.T) {
c, err := initCipher([]byte("0123456789abcdef"))
assert.NoError(t, err)
_, err = c.Decrypt([]byte{1, 2, 3, 4, 5})
assert.Error(t, err)
})
}
}
func runEncryptAndDecrypt(t *testing.T, c Cipher, dataSize int) {
data := make([]byte, dataSize)
_, err := io.ReadFull(rand.Reader, data)