OpenNHP/opennhp · error
cipherText length invalid: must be IV + multiple of block…
Error message
cipherText length invalid: must be IV + multiple of block size
What it means
After reserving the 16-byte IV, the remaining ciphertext in AESDecrypt must be an exact multiple of the 16-byte AES block size, since CBC decryption cannot process partial blocks (Go's CBC decrypter would panic). This guard rejects misaligned input with a clean error.
Solutions
- Validate (len(data)-16)%16 == 0 before calling; trace which serialization step changed the byte count.
- Store/transfer ciphertext as raw bytes or length-safe encodings (base64) and decode before decrypting.
- Only feed AESDecrypt data produced by AESEncrypt (IV + CBC blocks); use matching APIs for other modes (e.g. AeadFromKey for GCM).
- Verify no header/footer bytes are included in the slice passed in.
Example fix
// before
raw := []byte(b64Stored) // base64 text decrypted directly
plain, err := core.AESDecrypt(raw, key)
// after
data, err := base64.StdEncoding.DecodeString(b64Stored)
if (len(data)-16)%16 != 0 {
return nil, fmt.Errorf("corrupt blob: %d bytes", len(data))
}
plain, err := core.AESDecrypt(data, key) Defensive patterns
Strategy: validation
Validate before calling
if (len(blob)-16)%16 != 0 {
return fmt.Errorf("blob not IV+block-aligned: %d bytes", len(blob))
} Try / catch
plain, err := core.AESDecrypt(blob, key)
if err != nil {
return fmt.Errorf("AES decrypt framing invalid (len=%d): %w", len(blob), err)
} Prevention
- Decode base64/hex before decrypting; never pass encoded text as ciphertext bytes.
- Encrypt/decrypt with matching API pairs (AESEncrypt <-> AESDecrypt).
- Persist ciphertext as bytes or length-safe encodings to prevent byte loss.
When it happens
Trigger: Calling AESDecrypt where len(cipherText)-16 is not a multiple of 16 — one or more bytes were added or lost after the IV, or the input isn't AESEncrypt output at all (e.g. raw CBC ciphertext without an IV, or base64 text passed as bytes).
Common situations: Storing ciphertext as text and losing/altering bytes in encoding round-trips; concatenating multiple encrypted blobs; passing GCM or other-mode output to this CBC helper; manual slicing that keeps the wrong offset.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- cipherText too short: need at least
- failed to create AES-GCM
- failed to create AES cipher for CBC decryption
- ciphertext too short: need at least
- ciphertext length is not a multiple of block size
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/0f5de0799104d174.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/crypto.go:375
// pad adds PKCS#7 padding to data. Uses shared implementation from utils.
func pad(data []byte, blockSize int) []byte {
return utils.PKCS7Pad(data, blockSize)
}
func AESDecrypt(cipherText []byte, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// Validate ciphertext length:
// - Must have at least IV (16 bytes) + one encrypted block (16 bytes)
// - After IV extraction, remaining must be a multiple of block size
if len(cipherText) < aes.BlockSize*2 {
return nil, fmt.Errorf("cipherText too short: need at least %d bytes, got %d", aes.BlockSize*2, len(cipherText))
}
if (len(cipherText)-aes.BlockSize)%aes.BlockSize != 0 {
return nil, fmt.Errorf("cipherText length invalid: must be IV + multiple of block size")
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
// Decrypt
mode := cipher.NewCBCDecrypter(block, iv)
decrypted := make([]byte, len(cipherText))
mode.CryptBlocks(decrypted, cipherText)
// Remove padding
decrypted = unpad(decrypted, aes.BlockSize)
return decrypted, nil
}
// unpad removes PKCS#7 padding from data. Uses shared implementation from utils.
func unpad(padded []byte, blockSize int) []byte {
return utils.PKCS7Unpad(padded, blockSize)View on GitHub (pinned to 6e04ca5ff0)