You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题原因与修复方案

核心问题

  1. 块大小假设错误:解密时默认每个加密块的大小都是1024字节,但实际ecc_encrypt_key输出的每个加密块长度是可变的(取决于ECC曲线参数)。直接按1024字节偏移读取加密数据,导致第二个块的起始位置错误,传入无效数据给ecc_decrypt_key。
  2. 全局变量pair的线程安全问题:如果Lua环境存在并发调用,全局的pair结构会被覆盖,导致块数或密钥长度错误。
  3. 加密数据存储方式错误:未记录每个加密块的实际长度,只传递总加密数据和块数,解密时无法正确分割每个块。

修复步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 23:49:54