LibTomcrypt解密多块数据时出现Invalid input packet错误求助
ECC加密解密多块时解密第二个块出现“Invalid input packet”错误
我正在开发一个C到Lua的API,让Lua调用LibTomcrypt的ecc_encrypt和decrypt函数。加密时按块处理消息,但解密第二个块时始终触发“Invalid input packet”错误——当Lua测试代码中的原文本多一个“M”从而生成额外块时,这个错误就会出现。
C代码
#define LTM_DESC #include <tomcrypt.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "lua.h" #include "lauxlib.h" // 全局变量 prng_state prng; int err, prng_idx , hash_idx; // 输出结构定义 typedef struct { unsigned long exported_key_len; unsigned long ctlen; } Pair; Pair pair; static int gera_chave_lua(lua_State *L) { ecc_key mykey; unsigned char exported_key_public[1024]; size_t exported_key_public_len = sizeof(exported_key_public); unsigned char exported_key_private[1024]; size_t exported_key_private_len = sizeof(exported_key_private); if (register_prng(&yarrow_desc) == -1) { lua_pushstring(L, "注册Yarrow失败。"); lua_error(L); } if (register_hash(&sha512_desc) == -1) { lua_pushstring(L, "注册sha256失败。"); lua_error(L); } prng_idx = find_prng("yarrow"); hash_idx = find_hash("sha512"); if ((err = rng_make_prng(256, prng_idx ,&prng, NULL)) != CRYPT_OK) { lua_pushfstring(L, "配置PRNG失败: %s", error_to_string(err)); lua_error(L); } if ((err = ecc_make_key(&prng, prng_idx, 65, &mykey)) != CRYPT_OK) { lua_pushfstring(L, "生成密钥失败: %s", error_to_string(err)); lua_error(L); } if ((err = ecc_export(exported_key_public ,&exported_key_public_len , PK_PUBLIC, &mykey)) != CRYPT_OK) { lua_pushfstring(L, "导出公钥失败: %s", error_to_string(err)); lua_error(L); } if ((err = ecc_export(exported_key_private ,&exported_key_private_len , PK_PRIVATE, &mykey)) != CRYPT_OK) { lua_pushfstring(L, "导出私钥失败: %s", error_to_string(err)); lua_error(L); } lua_pushlstring(L, (const char *)exported_key_public, exported_key_public_len); lua_pushlstring(L, (const char *)exported_key_private, exported_key_private_len); return 2; } // 修改加密函数以接收Lua传入的文本和公钥 static int encriptar_lua(lua_State *L) { ecc_key mykey; const char *plaintext = lua_tostring(L, 1); const char *pb_key = lua_tostring(L, 2); size_t ptlen = strlen(plaintext); size_t numBlocks = ptlen / 64 + ((ptlen % 64 != 0) ? 1 : 0); // 计算所需块数 size_t blockCount = 1; // 当前块计数器 size_t totalLen = numBlocks * 1024; // 缓冲区预估总大小 unsigned char *buffer = (unsigned char *)malloc((totalLen + 1) * sizeof(unsigned char)); // 分配缓冲区内存 if (buffer == NULL) { lua_pushstring(L, "分配缓冲区内存失败。"); lua_error(L); } size_t bufferIndex = 0; if ((err = ecc_import((unsigned char *)pb_key, &pair.exported_key_len, &mykey)) != CRYPT_OK) { free(buffer); // 释放缓冲区内存 lua_pushfstring(L, "导入ECC密钥失败: %s", error_to_string(err)); lua_error(L); } while (ptlen > 0) { unsigned char ciphertext[1024]; size_t ctlen = sizeof(ciphertext); size_t len = (ptlen > 64) ? 64 : ptlen; // 计算当前块大小 printf("进入循环x次\n"); if ((err = ecc_encrypt_key((const unsigned char *)plaintext, len, ciphertext, &ctlen, &prng, prng_idx, hash_idx, &mykey)) != CRYPT_OK) { free(buffer); // 释放缓冲区内存 lua_pushfstring(L, "加密文本失败: %s", error_to_string(err)); lua_error(L); } // 将加密数据写入缓冲区 memcpy(buffer + bufferIndex, ciphertext, ctlen); bufferIndex += ctlen; ptlen -= len; plaintext += len; blockCount++; } buffer[bufferIndex] = '\0'; // 在缓冲区末尾添加空字符 pair.ctlen = numBlocks; // 更新Pair结构中的块数 // 将结果字符串压入Lua栈 lua_pushlstring(L, (const char *)buffer, bufferIndex); free(buffer); // 释放缓冲区内存 return 1; } // 使用私钥解密加密文本的函数 static int descriptar_lua(lua_State *L) { ecc_key mykey; const char *pv_key = lua_tostring(L, 1); const char *ciphertext = lua_tostring(L, 2); if ((err = ecc_import((unsigned char *)pv_key, &pair.exported_key_len, &mykey)) != CRYPT_OK) { lua_pushfstring(L, "导入ECC密钥失败: %s", error_to_string(err)); lua_error(L); } int blockCount = pair.ctlen; // Pair结构中保存的块数 size_t totalLen = blockCount * 1024; // 解密文本预估总大小 unsigned char *buffer = (unsigned char *)malloc((totalLen + 1) * sizeof(unsigned char)); // 分配缓冲区内存 if (buffer == NULL) { lua_pushstring(L, "分配缓冲区内存失败。"); lua_error(L); } size_t bufferIndex = 0; for (int i = 0; i < blockCount; ++i) { unsigned char plaintext[1024]; size_t ptlen = sizeof(plaintext); if ((err = ecc_decrypt_key((const unsigned char *)(ciphertext + i * 1024), 1024, plaintext, &ptlen, &mykey)) != CRYPT_OK) { free(buffer); // 释放缓冲区内存 lua_pushfstring(L, "解密文本失败: %s", error_to_string(err)); lua_error(L); } // 将解密数据写入缓冲区 memcpy(buffer + bufferIndex, plaintext, ptlen); bufferIndex += ptlen; } buffer[bufferIndex] = '\0'; // 在缓冲区末尾添加空字符 // 将结果字符串压入Lua栈 lua_pushlstring(L, (const char *)buffer, bufferIndex); free(buffer); // 释放缓冲区内存 return 1; } int luaopen_mylib(lua_State *L) { luaL_Reg funcs[] = { {"gera_chave_lua", gera_chave_lua}, {"encriptar_lua", encriptar_lua}, {"descriptar_lua", descriptar_lua}, {NULL, NULL} }; luaL_newlib(L, funcs); return 1; } int main(void) { ltc_mp = ltm_desc; lua_State *L = luaL_newstate(); luaL_openlibs(L); luaL_requiref(L, "mylib", luaopen_mylib, 1); if (luaL_dofile(L, "teste_lua.lua")) { printf("执行Lua文件失败: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); } lua_close(L); return 0; }
Lua测试代码
local mylib = require("mylib") -- 生成密钥 local pub_key, prv_key = mylib.gera_chave_lua() print("公钥:") print(pub_key) print("\n私钥:") print(prv_key) -- 使用公钥加密文本 local texto_original = "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" print("\n原始文本是") print(texto_original) local texto_encriptado = mylib.encriptar_lua(texto_original, pub_key) print("\n加密文本:") print(texto_encriptado) -- 使用私钥解密加密文本 local texto_descriptado = mylib.descriptar_lua(prv_key, texto_encriptado) print("\n解密文本:") print(texto_descriptado)
编译命令
gcc -I/usr/include/lua5.3 to_lua.c -o to_lua -ltomcrypt -llua5.3
问题原因与修复方案
核心问题
- 块大小假设错误:解密时默认每个加密块的大小都是1024字节,但实际
ecc_encrypt_key输出的每个加密块长度是可变的(取决于ECC曲线参数)。直接按1024字节偏移读取加密数据,导致第二个块的起始位置错误,传入无效数据给ecc_decrypt_key。 - 全局变量
pair的线程安全问题:如果Lua环境存在并发调用,全局的pair结构会被覆盖,导致块数或密钥长度错误。 - 加密数据存储方式错误:未记录每个加密块的实际长度,只传递总加密数据和块数,解密时无法正确分割每个块。
修复步骤
1. 传递加密块的长度信息给Lua
修改加密函数,返回加密数据的同时返回每个块的长度数组,确保解密时能准确分割数据:
static int encriptar_lua(lua_State *L) { ecc_key mykey; const char *plaintext = lua_tostring(L, 1); const char *pb_key = lua_tostring(L, 2); size_t ptlen = strlen(plaintext); size_t numBlocks = ptlen / 64 + ((ptlen % 64 != 0) ? 1 : 0); size_t totalLen = numBlocks * 1024; unsigned char *buffer = (unsigned char *)malloc(totalLen); if (!buffer) { lua_pushstring(L, "分配缓冲区内存失败。"); lua_error(L); } // 存储每个块的实际长度 size_t *blockLengths = (size_t *)malloc(numBlocks * sizeof(size_t)); if (!blockLengths) { free(buffer); lua_pushstring(L, "分配块长度数组内存失败。"); lua_error(L); } size_t bufferIndex = 0; if ((err = ecc_import((unsigned char *)pb_key, &pair.exported_key_len, &mykey)) != CRYPT_OK) { free(buffer); free(blockLengths); lua_pushfstring(L, "导入ECC密钥失败: %s", error_to_string(err)); lua_error(L); } size_t blockIdx = 0; while (ptlen > 0) { unsigned char ciphertext[1024]; size_t ctlen = sizeof(ciphertext); size_t len = (ptlen > 64) ? 64 : ptlen; if ((err = ecc_encrypt_key((const unsigned char *)plaintext, len, ciphertext, &ctlen, &prng, prng_idx, hash_idx, &mykey)) != CRYPT_OK) { free(buffer); free(blockLengths); lua_pushfstring(L, "加密文本失败: %s", error_to_string(err)); lua_error(L); } memcpy(buffer + bufferIndex, ciphertext, ctlen); blockLengths[blockIdx++] = ctlen; bufferIndex += ctlen; ptlen -= len; plaintext += len; } // 将加密数据和块长度数组压入Lua栈 lua_pushlstring(L, (const char *)buffer, bufferIndex); lua_createtable(L, numBlocks, 0); for (size_t i = 0; i < numBlocks; i++) { lua_pushinteger(L, blockLengths[i]); lua_rawseti(L, -2, i+1); // Lua表索引从1开始 } free(buffer); free(blockLengths); return 2; // 返回两个值:加密数据、块长度数组 }
2. 修改解密函数,使用块长度数组分割加密数据
static int descriptar_lua(lua_State *L) { ecc_key mykey; const char *pv_key = lua_tostring(L, 1); size_t ciphertextLen; const unsigned char *ciphertext = (const unsigned char *)lua_tolstring(L, 2, &ciphertextLen); // 获取块长度数组 luaL_checktype(L, 3, LUA_TTABLE); size_t numBlocks = lua_rawlen(L, 3); if ((err = ecc_import((unsigned char *)pv_key, &pair.exported_key_len, &mykey)) != CRYPT_OK) { lua_pushfstring(L, "导入ECC密钥失败: %s", error_to_string(err)); lua_error(L); } size_t totalLen = numBlocks * 64; // 每个明文块最大64字节 unsigned char *buffer = (unsigned char *)malloc(totalLen + 1); if (!buffer) { lua_pushstring(L, "分配缓冲区内存失败。"); lua_error(L); } size_t bufferIndex = 0; size_t cipherOffset = 0; for (size_t i = 0; i < numBlocks; i++) { lua_pushinteger(L, i+1); lua_rawget(L, 3); size_t blockLen = lua_tointeger(L, -1); lua_pop(L, 1); if (cipherOffset + blockLen > ciphertextLen) { free(buffer); lua_pushstring(L, "加密数据长度不足。"); lua_error(L); } unsigned char plaintext[1024]; size_t ptlen = sizeof(plaintext); if ((err = ecc_decrypt_key(ciphertext + cipherOffset, blockLen, plaintext, &ptlen, &mykey)) != CRYPT_OK) { free(buffer); lua_pushfstring(L, "解密第%zu块失败: %s", i+1, error_to_string(err)); lua_error(L); } memcpy(buffer + bufferIndex, plaintext, ptlen); bufferIndex += ptlen; cipherOffset += blockLen; } buffer[bufferIndex] = '\0'; lua_pushlstring(L, (const char *)buffer, bufferIndex); free(buffer); return 1; }
3. 更新Lua测试代码,传递块长度数组给解密函数
local mylib = require("mylib") -- 生成密钥 local pub_key, prv_key = mylib.gera_chave_lua() print("公钥:") print(pub_key) print("\n私钥:") print(prv_key) -- 使用公钥加密文本 local texto_original = "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM" print("\n原始文本是") print(texto_original) local texto_encriptado, block_lengths = mylib.encriptar_lua(texto_original, pub_key) print("\n加密文本:") print(texto_encriptado) -- 使用私钥解密加密文本 local texto_descriptado = mylib.descriptar_lua(prv_key, texto_encriptado, block_lengths) print("\n解密文本:") print(texto_descriptado)
4. 移除全局变量pair的依赖(可选但推荐)
全局变量pair在多线程环境下不安全,建议将密钥长度等信息改为局部变量,避免全局污染。比如在gera_chave_lua中直接返回密钥长度,或者在加密/解密函数中使用局部变量存储导入的密钥长度。
内容的提问来源于stack exchange,提问作者Breno Marot
相关产品推荐
相关产品推荐

