{"record":{"id":"3153e8d6d381a9a9","repo":"OpenNHP/opennhp","slug":"ciphertext-length-d-is-not-a-multiple-of-block-si","errorCode":null,"errorMessage":"ciphertext length %d is not a multiple of block size %d","messagePattern":"ciphertext length (.+?) is not a multiple of block size (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/crypto.go","lineNumber":267,"sourceCode":"\t\tblock, err = sm4.NewCipher(key[:16])\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to create SM4 cipher for CBC decryption: %w\", err)\n\t\t}\n\t\tiv = key[16:]\n\n\tcase GCM_CHACHA20POLY1305:\n\t\treturn nil, ErrNotApplicable\n\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported cipher type for CBC decryption: %d\", t)\n\t}\n\n\t// Validate ciphertext: must be at least one block and a multiple of block size\n\tif len(ciphertext) < block.BlockSize() {\n\t\treturn nil, fmt.Errorf(\"ciphertext too short: need at least %d bytes\", block.BlockSize())\n\t}\n\tif len(ciphertext)%block.BlockSize() != 0 {\n\t\treturn nil, fmt.Errorf(\"ciphertext length %d is not a multiple of block size %d\", len(ciphertext), block.BlockSize())\n\t}\n\n\tvar plaintext []byte\n\tif inPlace {\n\t\tplaintext = ciphertext\n\t} else {\n\t\tplaintext = make([]byte, len(ciphertext))\n\t}\n\n\tmode := cipher.NewCBCDecrypter(block, iv)\n\t// CryptBlocks can work in-place if the two arguments are the same.\n\tmode.CryptBlocks(plaintext, ciphertext)\n\n\tif len(plaintext)%block.BlockSize() == 0 {\n\t\t// skip unpadding\n\t} else {\n\t\t// Unpad plaintext\n\t\tpkcs7 := padding.NewPKCS7Padding(uint(block.BlockSize()))","sourceCodeStart":249,"sourceCodeEnd":285,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/crypto.go#L249-L285","documentation":"CBC is a block mode: ciphertext length must be an exact multiple of the block size (16 bytes for AES/SM4). CBCDecryption checks this before decrypting because cipher.NewCBCDecrypter would panic on partial blocks; this error converts that into a clean failure.","triggerScenarios":"Calling CBCDecryption with ciphertext whose length isn't a multiple of 16 — a byte dropped or added in transit, wrong slicing (headers/IV not removed), or base64/hex decoding that lost characters.","commonSituations":"Manual wire-format parsing that mis-slices the payload; corruption over an unprotected transport; mixing ciphertext formats (e.g. IV-prefixed AESEncrypt output fed to CBCDecryption without stripping the IV); copy-paste truncating trailing bytes.","solutions":["Validate len(ciphertext)%16 == 0 before the call and surface which encoding step broke alignment.","Strip any IV/header prefix before passing data to CBCDecryption (note AESEncrypt output is IV+ciphertext and meant for AESDecrypt, not this API).","Re-encode the payload (hex/base64) and compare lengths to find where bytes were lost.","Enable integrity protection (AEAD) on the transport if corruption in transit is recurring."],"exampleFix":"// before\nplain, err := core.CBCDecryption(core.GCM_AES256, key, ivAndCt, false) // includes 16-byte IV\n// after\nif len(ivAndCt) < 16 || (len(ivAndCt)-16)%16 != 0 {\n    return fmt.Errorf(\"bad ciphertext framing\")\n}\nplain, err := core.CBCDecryption(core.GCM_AES256, key, ivAndCt[16:], false)","handlingStrategy":"validation","validationCode":"if len(ct)%16 != 0 {\n    return fmt.Errorf(\"misaligned ciphertext: %d bytes (IV stripped?)\", len(ct))\n}","typeGuard":null,"tryCatchPattern":"plain, err := core.CBCDecryption(t, key, ct, false)\nif err != nil {\n    return fmt.Errorf(\"CBC decrypt (len=%d): %w\", len(ct), err)\n}","preventionTips":["Strip IV prefixes before CBCDecryption; AESEncrypt output belongs to AESDecrypt, not this API.","Use base64/hex round-trips carefully; compare lengths after encode/decode.","Prefer AEAD modes which detect corruption instead of misaligning."],"tags":["go","crypto","cbc","input-validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}