{"record":{"id":"97949b8ffc45fedc","repo":"Tencent/WeKnora","slug":"invalid-padding","errorCode":null,"errorMessage":"invalid padding","messagePattern":"invalid padding","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/im/wecom/webhook_adapter.go","lineNumber":473,"sourceCode":"\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}\n\t}\n\tplaintext := ciphertext[:len(ciphertext)-padLen]\n\n\t// WeCom format: random(16) + msg_len(4) + msg + corp_id\n\tif len(plaintext) < 20 {\n\t\treturn nil, fmt.Errorf(\"plaintext too short\")\n\t}\n\n\tmsgLen := binary.BigEndian.Uint32(plaintext[16:20])\n\tif uint32(len(plaintext)) < 20+msgLen {\n\t\treturn nil, fmt.Errorf(\"message length mismatch\")\n\t}\n","sourceCodeStart":455,"sourceCodeEnd":491,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/im/wecom/webhook_adapter.go#L455-L491","documentation":"After CBC decryption, decrypt() verifies PKCS#7 padding: the last byte gives the pad length, which must be between 1 and 16 and must fit within the plaintext. A padLen of 0, greater than the 16-byte block size, or larger than the buffer is impossible for valid WeCom messages and indicates wrong key or corrupted data. This is the first of two padding checks (range check here, byte-consistency at error 783).","triggerScenarios":"Decryption with a mismatched EncodingAESKey so the last decrypted block's final byte is an arbitrary value; corrupted ciphertext; feeding non-AES data through decrypt.","commonSituations":"AES key copied from the wrong WeCom app (multi-app configs); key contains whitespace/newline from config file; environment-specific keys out of sync with the WeCom console.","solutions":["Verify a.aesKey matches the EncodingAESKey of the exact WeCom application that sent the callback (43-char base64 key, decoded to 32 bytes)","Re-download/regenerate the EncodingAESKey from the WeCom admin console and update config","Log the first bytes and length of the ciphertext and key to confirm they come from the same app","Check for config loading issues (trailing newline, quotes) corrupting the key"],"exampleFix":"// before: key with newline from env\naesKey := os.Getenv(\"WECOM_AES_KEY\")\n// after: trimmed and validated\naesKey := strings.TrimSpace(os.Getenv(\"WECOM_AES_KEY\"))\nif len(aesKey) != 43 { return errors.New(\"invalid EncodingAESKey length\") }","handlingStrategy":"validation","validationCode":"if len(strings.TrimSpace(aesKey)) != 43 { return errors.New(\"EncodingAESKey must be 43 base64 chars\") }","typeGuard":null,"tryCatchPattern":"msg, err := adapter.ParseCallback(sig, ts, nonce, enc)\nif err != nil && strings.Contains(err.Error(), \"invalid padding\") {\n    logger.Error(\"WeCom decrypt failed — check EncodingAESKey matches the callback app\")\n    http.Error(w, \"decrypt failed\", http.StatusBadRequest)\n    return\n}","preventionTips":["Keep one Token/AESKey pair per WeCom app and load them together from the same config source","Trim whitespace/newlines when loading keys from env or files","Write an encrypt→decrypt round-trip test with your production key","Alert on 'invalid padding' spikes — they usually mean config drift"],"tags":["aes","pkcs7-padding","decryption","wrong-key","wecom"],"backgroundTag":"invalid-padding","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}