OpenNHP/opennhp · error
ciphertext too short: need at least
Error message
ciphertext too short: need at least %d bytes
What it means
After the block cipher is constructed, CBCDecryption requires the ciphertext to contain at least one full block (16 bytes for AES, 16 for SM4). Empty or truncated ciphertext cannot yield any plaintext block, so it is rejected before CBC decryption. This is a data-integrity guard, not a key problem.
Solutions
- Check len(ciphertext) >= 16 before calling CBCDecryption and return a caller-level error if empty.
- Verify the buffer slice passed excludes headers/IV correctly so the ciphertext region is intact.
- Log the ciphertext length at the call site to find where it becomes empty or truncated.
- Handle sender-side failure: the encrypting peer may have produced no output — validate before sending.
Example fix
// before
plain, err := core.CBCDecryption(core.GCM_AES256, key, payload, false)
// after
if len(payload) < 16 {
return fmt.Errorf("payload too short to decrypt: %d bytes", len(payload))
}
plain, err := core.CBCDecryption(core.GCM_AES256, key, payload, false) Defensive patterns
Strategy: validation
Validate before calling
if len(ct) < 16 {
return fmt.Errorf("ciphertext empty or truncated: %d bytes", len(ct))
} Try / catch
plain, err := core.CBCDecryption(t, key, ct, false)
if err != nil && strings.Contains(err.Error(), "ciphertext too short") {
return ErrCorruptPayload
} Prevention
- Length-check payloads before any decrypt call.
- Verify slicing logic strips only headers/IVs, leaving full ciphertext blocks.
- Treat empty reads from storage/network as errors, not decryptable input.
When it happens
Trigger: Calling CBCDecryption with an empty slice, a nil slice, or ciphertext truncated to fewer than 16 bytes — e.g. a packet body sliced wrongly or a failed read stored as empty bytes.
Common situations: Decoding a zero-length payload from a malformed packet; slicing off the wrong prefix and leaving <16 bytes; a DB/network layer returning empty bytes treated as ciphertext.
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 is not a multiple of block size
- unsupported cipher type for CBC decryption
- cipherText too short: need at least
- cipherText length invalid: must be IV + multiple of block…
- failed to create device
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/f7b7faa1a7724cff.
Report an issue: GitHub.
Appendix: source
Thrown at nhp/core/crypto.go:264
iv = key[8:24]
case GCM_SM4:
block, err = sm4.NewCipher(key[:16])
if err != nil {
return nil, fmt.Errorf("failed to create SM4 cipher for CBC decryption: %w", err)
}
iv = key[16:]
case GCM_CHACHA20POLY1305:
return nil, ErrNotApplicable
default:
return nil, fmt.Errorf("unsupported cipher type for CBC decryption: %d", t)
}
// Validate ciphertext: must be at least one block and a multiple of block size
if len(ciphertext) < block.BlockSize() {
return nil, fmt.Errorf("ciphertext too short: need at least %d bytes", block.BlockSize())
}
if len(ciphertext)%block.BlockSize() != 0 {
return nil, fmt.Errorf("ciphertext length %d is not a multiple of block size %d", len(ciphertext), block.BlockSize())
}
var plaintext []byte
if inPlace {
plaintext = ciphertext
} else {
plaintext = make([]byte, len(ciphertext))
}
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(plaintext, ciphertext)
if len(plaintext)%block.BlockSize() == 0 {
// skip unpaddingView on GitHub (pinned to 6e04ca5ff0)