{"record":{"id":"f513da6a8211e646","repo":"Tencent/WeKnora","slug":"ciphertext-too-short","errorCode":null,"errorMessage":"ciphertext too short","messagePattern":"ciphertext too short","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/wecom/webhook_adapter.go","lineNumber":460,"sourceCode":"\tcomputed := fmt.Sprintf(\"%x\", hash.Sum(nil))\n\n\treturn hmac.Equal([]byte(computed), []byte(signature))\n}\n\n// decrypt decrypts a WeCom AES-encrypted message.\nfunc (a *WebhookAdapter) decrypt(encrypted string) ([]byte, error) {\n\tciphertext, err := base64.StdEncoding.DecodeString(encrypted)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"base64 decode: %w\", err)\n\t}\n\n\tblock, err := aes.NewCipher(a.aesKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"new cipher: %w\", err)\n\t}\n\n\tif len(ciphertext) < aes.BlockSize {\n\t\treturn nil, fmt.Errorf(\"ciphertext too short\")\n\t}\n\tif len(ciphertext)%aes.BlockSize != 0 {\n\t\treturn nil, fmt.Errorf(\"ciphertext length is not a multiple of AES block size\")\n\t}\n\n\tiv := a.aesKey[:aes.BlockSize]\n\tmode := cipher.NewCBCDecrypter(block, iv)\n\tmode.CryptBlocks(ciphertext, ciphertext)\n\n\t// Remove and verify PKCS#7 padding\n\tpadLen := int(ciphertext[len(ciphertext)-1])\n\tif padLen > wecomPKCS7BlockSize || padLen == 0 || padLen > len(ciphertext) {\n\t\treturn nil, fmt.Errorf(\"invalid padding\")\n\t}\n\tfor i := 0; i < padLen; i++ {\n\t\tif ciphertext[len(ciphertext)-1-i] != byte(padLen) {\n\t\t\treturn nil, fmt.Errorf(\"invalid padding\")\n\t\t}","sourceCodeStart":442,"sourceCodeEnd":478,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/wecom/webhook_adapter.go#L442-L478","documentation":"decrypt() validates that the AES-encrypted ciphertext from a WeCom callback is at least one 16-byte AES block long before attempting CBC decryption. Since AES-CBC output always consists of full blocks, any payload shorter than aes.BlockSize cannot be validly encrypted data, so the function rejects it early. This guards against truncated, empty, or malformed encrypted payloads before any crypto work is done.","triggerScenarios":"Calling decrypt (indirectly via HandleURLVerification or ParseCallback) with an encrypted_msg / echostr string that is empty or shorter than 16 bytes after base64 decoding — e.g. a test harness posting a truncated or empty Encrypt field.","commonSituations":"Unit tests feeding synthetic callback payloads; a proxy or middleware stripping/trimming the Encrypt parameter; base64-decoding the wrong field so the decoded buffer is empty or tiny.","solutions":["Ensure the Encrypt (or echostr) parameter is the full base64 string WeCom sent, not truncated or trimmed","Verify you are base64-decoding the correct field; decode the encrypted payload before decrypt, never a signature string","Log len(ciphertext) at the call site and compare against what WeCom actually posted","Enable the full message-mode callback in WeCom admin so Encrypt contains real encrypted data"],"exampleFix":"// before: passing raw signature instead of payload\nmsg, err := adapter.ParseCallback(msgSignature, timestamp, nonce, sigString)\n// after: pass the base64 Encrypt field\nmsg, err := adapter.ParseCallback(msgSignature, timestamp, nonce, encryptField)","handlingStrategy":"validation","validationCode":"raw, _ := base64.StdEncoding.DecodeString(encryptParam)\nif len(raw) < 16 { /* skip decrypt; log truncated payload */ }","typeGuard":"func hasValidCiphertextLength(b []byte) bool { return len(b) >= 16 }","tryCatchPattern":"msg, err := adapter.ParseCallback(sig, ts, nonce, enc)\nif err != nil && strings.Contains(err.Error(), \"ciphertext too short\") {\n    logger.Warn(\"truncated WeCom callback payload\")\n    return\n}","preventionTips":["Pass the raw Encrypt parameter through untouched — no trimming or re-encoding","Log payload length on decrypt failures","Use WeCom's echostr verification flow in integration tests"],"tags":["aes","decryption","validation","wecom"],"backgroundTag":"ciphertext-too-short","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}