{"record":{"id":"7a2b6ca5011d73be","repo":"OpenNHP/opennhp","slug":"ciphertext-too-short-need-at-least-d-bytes-got","errorCode":null,"errorMessage":"cipherText too short: need at least %d bytes, got %d","messagePattern":"cipherText too short: need at least (.+?) bytes, got (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nhp/core/crypto.go","lineNumber":372,"sourceCode":"\tmode.CryptBlocks(cipherText[aes.BlockSize:], plainText)\n\treturn cipherText, nil\n}\n\n// pad adds PKCS#7 padding to data. Uses shared implementation from utils.\nfunc pad(data []byte, blockSize int) []byte {\n\treturn utils.PKCS7Pad(data, blockSize)\n}\n\nfunc AESDecrypt(cipherText []byte, key []byte) ([]byte, error) {\n\tblock, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Validate ciphertext length:\n\t// - Must have at least IV (16 bytes) + one encrypted block (16 bytes)\n\t// - After IV extraction, remaining must be a multiple of block size\n\tif len(cipherText) < aes.BlockSize*2 {\n\t\treturn nil, fmt.Errorf(\"cipherText too short: need at least %d bytes, got %d\", aes.BlockSize*2, len(cipherText))\n\t}\n\tif (len(cipherText)-aes.BlockSize)%aes.BlockSize != 0 {\n\t\treturn nil, fmt.Errorf(\"cipherText length invalid: must be IV + multiple of block size\")\n\t}\n\tiv := cipherText[:aes.BlockSize]\n\tcipherText = cipherText[aes.BlockSize:]\n\n\t// Decrypt\n\tmode := cipher.NewCBCDecrypter(block, iv)\n\tdecrypted := make([]byte, len(cipherText))\n\tmode.CryptBlocks(decrypted, cipherText)\n\n\t// Remove padding\n\tdecrypted = unpad(decrypted, aes.BlockSize)\n\n\treturn decrypted, nil\n}\n","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/nhp/core/crypto.go#L354-L390","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nplain, err := core.AESDecrypt(token, key) // token is 16 bytes\n// after\nif len(token) < 32 {\n    return nil, fmt.Errorf(\"stored value too short to be AES-CBC blob: %d\", len(token))\n}\nplain, err := core.AESDecrypt(token, key)","handlingStrategy":"validation","validationCode":"if len(blob) < 32 {\n    return fmt.Errorf(\"blob too short for IV+ciphertext: %d\", len(blob))\n}","typeGuard":null,"tryCatchPattern":"plain, err := core.AESDecrypt(blob, key)\nif err != nil {\n    return fmt.Errorf(\"AES decrypt failed (corrupt/truncated blob): %w\", err)\n}","preventionTips":["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."],"tags":["go","crypto","aes","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"}