Skip to content

crypto — 加密模块 ​

模块名:crypto

提供 AES 加密、Base64 编解码、MD5 摘要和随机字节。全部是库级函数,数据以字节串形式传递。

哪些机型有:LP3232、LP3288(ESP32-S3 平台),LP8910、LP8911、LP8912、LP8913、LP8915、LP8916、LP8922(4G 平台),LP2300、LP2323(STM32H750 平台),以及上位机的内置软设备与软 PLC 模拟器。

哪些机型没有:经典 ESP32 平台的机型(LP3200–LP3223 等)与 STM32F103 平台的机型(LP1616、LP2222、LP2248–LP2252、LP2923、LP2925、LP2927 等)没有这个模块,调用 crypto.xxx(...) 会得到 attempt to index a nil value。

跨机型通用写法 —— 先判断再调用,没有的机型退回 plc.aes_encode:

lua
if crypto then
    local enc = crypto.aes_cbc_encrypt(data, key, iv)   -- 密钥与 IV 由脚本自己定
else
    local enc = plc.aes_encode(data)                              -- 退回设备固化密钥的 CBC
end

能力边界(所有平台一致):AES 三种模式(ECB / CBC / CTR),密钥长度 128/192/256 位;摘要只有 MD5,不支持任何 SHA 系列;虽然定义了 MODE_GCM 常量,但没有任何平台实现 GCM 加解密函数。

随机字节的强度按平台不同:ESP32-S3 用芯片硬件随机数,上位机与软 PLC 模拟器用操作系统提供的随机数,4G 机型(LP89xx 系列)用的是模组 SDK 的伪随机接口 —— 用它生成长期使用的密钥或 IV 时,4G 机型弱于其它平台。

AES 三种模式怎么选、CBC 的标准封装流程,以及固件的能力边界

函数总览 ​

函数作用
crypto.aes_ecb_encryptAES-ECB 加密(无填充)
crypto.aes_ecb_decryptAES-ECB 解密
crypto.aes_cbc_encryptAES-CBC 加密(自动 PKCS7 填充)
crypto.aes_cbc_decryptAES-CBC 解密(自动去填充)
crypto.aes_ctrAES-CTR 流加密/解密(同一个函数)
crypto.base64_encodeBase64 编码
crypto.base64_decodeBase64 解码
crypto.md5计算 MD5 摘要
crypto.random_bytes(integer)生成硬件随机字节

常量 ​

常量值说明
crypto.MODE_ECB0ECB 模式标识
crypto.MODE_CBC1CBC 模式标识
crypto.MODE_CTR2CTR 模式标识
crypto.MODE_GCM3GCM 模式标识。只有常量,没有对应函数

这些常量只作标识用,各加解密函数按名字区分模式,不需要传模式参数。


crypto.aes_ecb_encrypt ​

lua
crypto.aes_ecb_encrypt(plaintext, key) --> string

AES-ECB 模式加密。不做填充。

参数

#参数类型必填默认值说明
1plaintextstring是—明文,长度必须是 16 字节的整数倍
2keystring是—密钥,必须为 16、24 或 32 字节(AES-128/192/256)

返回值

类型说明
string密文,长度与明文相同

说明

  • 密钥长度非法、或明文长度不是 16 的整数倍时会直接报错。
  • ECB 对相同明文块产生相同密文块,安全性弱。新设计一律优先用 CBC 或 CTR,ECB 只用于对接老系统。

crypto.aes_ecb_decrypt ​

lua
crypto.aes_ecb_decrypt(ciphertext, key) --> string

AES-ECB 模式解密。

参数

#参数类型必填默认值说明
1ciphertextstring是—密文,长度必须是 16 字节的整数倍
2keystring是—密钥,16 / 24 / 32 字节

返回值

类型说明
string明文,长度与密文相同

crypto.aes_cbc_encrypt ​

lua
crypto.aes_cbc_encrypt(plaintext, key, iv) --> string

AES-CBC 模式加密。内部自动做 PKCS7 填充,明文可任意长度。

参数

#参数类型必填默认值说明
1plaintextstring是—明文,任意长度
2keystring是—密钥,16 / 24 / 32 字节
3ivstring是—初始向量,必须正好 16 字节

返回值

类型说明
string密文,长度为填充后的 16 字节整数倍

说明

  • 填充是固定 PKCS7,无法关闭;即使明文已是 16 字节整数倍也会额外补一整块,所以密文总比明文长。
  • IV 不需要保密,但同一密钥下每次加密都应换 IV,可用 crypto.random_bytes(16) 生成后随密文一起发给对方。

crypto.aes_cbc_decrypt ​

lua
crypto.aes_cbc_decrypt(ciphertext, key, iv) --> string

AES-CBC 模式解密,自动校验并去除 PKCS7 填充。

参数

#参数类型必填默认值说明
1ciphertextstring是—密文,长度必须是 16 字节的整数倍
2keystring是—密钥,16 / 24 / 32 字节
3ivstring是—加密时用的同一个 IV,16 字节

返回值

类型说明
string去掉填充后的明文

说明

  • 填充校验是常量时间实现,可抵御 Padding Oracle 类攻击;填充非法时报错。

crypto.aes_ctr ​

lua
crypto.aes_ctr(data, key, nonce) --> string

AES-CTR 流模式。加密和解密是同一操作,对密文再调用一次即还原。

参数

#参数类型必填默认值说明
1datastring是—明文或密文,任意长度,无需填充
2keystring是—密钥,16 / 24 / 32 字节
3noncestring是—计数器初值,必须正好 16 字节

返回值

类型说明
string结果,长度与输入相同

示例

lua
local key   = "0123456789abcdef"
local nonce = crypto.random_bytes(16)
local enc = crypto.aes_ctr("hello lpplc", key, nonce)
local dec = crypto.aes_ctr(enc, key, nonce)     -- 同一函数还原
print(dec)   -- hello lpplc

说明

  • 同一 key 下 nonce 绝不可重复,重复会导致密钥流复用被破解。每条消息用新的随机 nonce。

crypto.base64_encode ​

lua
crypto.base64_encode(data) --> string

Base64 编码(mbedTLS 实现)。

参数

#参数类型必填默认值说明
1datastring是—原始字节串

返回值

类型说明
stringBase64 字符串

说明


crypto.base64_decode ​

lua
crypto.base64_decode(str) --> string

Base64 解码。

参数

#参数类型必填默认值说明
1strstring是—Base64 字符串

返回值

类型说明
string解码后的字节串

crypto.md5 ​

lua
crypto.md5(data) --> string

计算 MD5 摘要。

参数

#参数类型必填默认值说明
1datastring是—输入数据

返回值

类型说明
string32 个字符的十六进制摘要字符串

示例

lua
print(crypto.md5("hello"))   -- 5d41402abc4b2a76b9719d911017c592

说明

  • MD5 已不抗碰撞,不要用于数字签名或密码存储。做接口签名、完整性校验或对接要求 MD5 的老平台可以用。
  • 固件里没有 SHA 系列函数,需要 SHA256 只能在服务端做。

crypto.random_bytes ​

lua
crypto.random_bytes(length) --> string

用 ESP32 硬件随机数发生器生成指定长度的随机字节。

参数

#参数类型必填默认值说明
1lengthinteger是—要生成的字节数

返回值

类型说明
string随机字节串

示例

lua
local iv = crypto.random_bytes(16)   -- 给 CBC 生成 IV

说明

  • 走硬件 RNG,比 math.random 适合做密钥、IV、nonce、会话 token。

常见用法 ​

CBC 加密上报,IV 随密文一起发出

lua
local KEY = "0123456789abcdef"          -- 16 字节 = AES-128

local function seal(plain)
    local iv  = crypto.random_bytes(16)
    local enc = crypto.aes_cbc_encrypt(plain, KEY, iv)
    return crypto.base64_encode(iv .. enc)   -- IV 不保密,拼在密文前面
end

local function unseal(b64)
    local raw = crypto.base64_decode(b64)
    return crypto.aes_cbc_decrypt(raw:sub(17), KEY, raw:sub(1, 16))
end

mqtt.publish(mqttc, "secure/data", seal(json.encode({ temp = plc.df(100) })), 0, 0)

接口签名

lua
local function sign(body, secret)
    return crypto.md5(body .. secret)
end

注意事项 ​

  • 密钥不要硬编码在 main.lua 里。脚本可被上位机读出,密钥应通过 plc.kv_set、fdb 在部署时写入,或走一次性配网下发。
  • ECB 不做填充且相同明文出相同密文,新设计请用 CBC 或 CTR。
  • CTR 的 nonce 在同一密钥下绝不可重复。
  • 只有 MD5,没有 SHA;有 MODE_GCM 常量但没有 GCM 函数,不要按 GCM 设计协议。
  • AES 是对称加密。需要非对称(RSA/ECC)的场景固件不支持,请在服务端实现。

例程 ​

⚠ 下面示例只在带 crypto 的机型上可跑(见页首的机型清单);不带该模块的机型请退回 plc.aes_encode 写法。统一模板「场景 / 完整代码 / 要点 / 验证方法 / 注意事项」。

例程 1:CBC 加密上报,IV 随密文一起发 ​

适用:带 crypto 的机型(见页首清单) | 依赖模块:crypto json mqtt | 难度:★★☆

场景:把温度 JSON 用 AES-128-CBC 加密后再走 MQTT,明文不出现在线上。

完整代码(可直接下载运行):

lua
local KEY = "0123456789abcdef"          -- 16 字节 = AES-128

local function seal(plain)
    local iv  = crypto.random_bytes(16)
    local enc = crypto.aes_cbc_encrypt(plain, KEY, iv)
    return crypto.base64_encode(iv .. enc)   -- IV 不保密,拼在前面
end

mqtt.publish(mqttc, "secure/data", seal(json.encode({ temp = plc.df(100) })), 0, 0)

要点:aes_cbc_encrypt 自动 PKCS7 填充,明文任意长度;每次用新随机 IV。

验证方法:带 crypto 的机型上能加密,服务端用同 KEY/IV 解密还原。

注意事项:标准固件无此模块(调用即 nil);IV 必须 16 字节;同密钥下每次换 IV。


例程 2:接口签名(MD5) ​

适用:带 crypto 的机型(见页首清单) | 依赖模块:crypto json | 难度:★☆☆

场景:给上报报文加 MD5 签名,服务器校验完整性。

完整代码(可直接下载运行):

lua
local function sign(body, secret)
    return crypto.md5(body .. secret)
end
local payload = json.encode({ temp = plc.df(100) })
mqtt.publish(mqttc, "data/up", payload .. "|" .. sign(payload, "mySecret"), 0, 0)

要点:crypto.md5 返回 32 位十六进制串。

验证方法:服务端用相同 secret 算 MD5 比对一致。

注意事项:MD5 不抗碰撞,别用于密码存储;固件只有 MD5 没有 SHA。


例程 3:注意事项——GCM 只有常量没有函数 ​

适用:带 crypto 的机型(见页首清单) | 依赖模块:crypto | 难度:★☆☆

完整代码(可直接下载运行):

lua
-- ❌ 错误:有 crypto.MODE_GCM 常量,但没有对应的 GCM 加解密函数
local enc = crypto.aes_gcm_encrypt(...)   -- 不存在

-- ✅ 正确:用 CBC 或 CTR
local iv = crypto.random_bytes(16)
local enc = crypto.aes_cbc_encrypt(plain, KEY, iv)

要点:MODE_GCM = 3 只是常量,没有对应函数;对称加密不支持非对称(RSA/ECC)。

验证方法:正确写法能加解密。

注意事项:按 GCM 设计协议会失败;密钥别硬编码在脚本里(可被上位机读出)。

LPPLC 工业物联网云平台 · 软硬件与云平台全栈自研