OpenNHP/opennhp · error
cipherText too short: need at least
Error message
cipherText too short: need at least %d bytes, got %d
What it means
AESDecrypt expects output of AESEncrypt: a random 16-byte IV prepended to the CBC ciphertext. It requires at least 32 bytes total (IV + at least one encrypted block); anything shorter cannot contain both the IV and data and is rejected before decryption. This is a framing/length guard on the input buffer.
Solutions
- Check len(cipherText) >= 32 before calling AESDecrypt and treat shorter inputs as corrupt data.
- Confirm the input is full AESEncrypt output (IV prefix + ciphertext), not a truncated or different encoding.
- Verify storage/serialization preserves the full byte slice (e.g. BLOB/[]byte columns, not fixed char buffers).
- If the data is truly shorter, it was not produced by AESEncrypt — re-encrypt at the source.
Example fix
// before
plain, err := core.AESDecrypt(token, key) // token is 16 bytes
// after
if len(token) < 32 {
return nil, fmt.Errorf("stored value too short to be AES-CBC blob: %d", len(token))
}
plain, err := core.AESDecrypt(token, key) Defensive patterns
Strategy: validation
Validate before calling
if len(blob) < 32 {
return fmt.Errorf("blob too short for IV+ciphertext: %d", len(blob))
} Try / catch
plain, err := core.AESDecrypt(blob, key)
if err != nil {
return fmt.Errorf("AES decrypt failed (corrupt/truncated blob): %w", err)
} Prevention
- Only decrypt full AESEncrypt outputs (>= 32 bytes).
- Use []byte/BLOB storage so leading IV bytes are never truncated.
- Log blob lengths on failure to spot storage-layer truncation.
When it happens
Trigger: Calling AESDecrypt with ciphertext shorter than 32 bytes: empty input, only the 16-byte IV, a truncated buffer, or plaintext/other encoding passed instead of AESEncrypt output.
Common situations: Decrypting a value stored/serialized with its leading bytes lost (DB column truncation, fixed-size buffer copy); passing a raw-key-hash or short token to AESDecrypt by mistake; reading a partially-written file.
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 length invalid: must be IV + multiple of block…
- 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/7a2b6ca5011d73be.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/crypto.go:372
mode.CryptBlocks(cipherText[aes.BlockSize:], plainText)
return cipherText, nil
}
// 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
}
View on GitHub (pinned to 6e04ca5ff0)