[Lua] 纯文本查看 复制代码
-- ============================================================
-- AES-128 ECB 纯 Lua 实现
-- ============================================================
local AES = {}
-- S盒
local S_BOX = {
0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16
}
-- 逆S盒
local INV_S_BOX = {}
for i = 0, 255 do
for j = 0, 255 do
if S_BOX[j+1] == i then
INV_S_BOX[i+1] = j
break
end
end
end
-- 轮常数
local RCON = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36}
-- GF(2^8)乘法
local function gmul(a, b)
local p = 0
for i = 1, 8 do
if (b & 1) ~= 0 then p = p ~ a end
local hi = (a & 0x80) ~= 0
a = (a << 1) & 0xff
if hi then a = a ~ 0x1b end
b = b >> 1
end
return p
end
-- 密钥扩展 (AES-128: 16字节密钥 → 176字节扩展密钥)
local function key_expansion(key)
local w = {}
for i = 0, 3 do
w[i] = {key[4*i+1], key[4*i+2], key[4*i+3], key[4*i+4]}
end
for i = 4, 43 do
local temp = {w[i-1][1], w[i-1][2], w[i-1][3], w[i-1][4]}
if i % 4 == 0 then
temp = {S_BOX[temp[2]+1], S_BOX[temp[3]+1], S_BOX[temp[4]+1], S_BOX[temp[1]+1]}
temp[1] = temp[1] ~ RCON[i/4]
end
w[i] = {
w[i-4][1] ~ temp[1],
w[i-4][2] ~ temp[2],
w[i-4][3] ~ temp[3],
w[i-4][4] ~ temp[4]
}
end
-- 展开为字节数组
local exp_key = {}
for i = 0, 43 do
exp_key[4*i+1] = w[i][1]
exp_key[4*i+2] = w[i][2]
exp_key[4*i+3] = w[i][3]
exp_key[4*i+4] = w[i][4]
end
return exp_key
end
-- 将16字节state转为4x4列主序矩阵
local function bytes_to_state(data, offset)
local state = {}
for r = 0, 3 do
state[r] = {}
for c = 0, 3 do
state[r][c] = data[offset + c*4 + r + 1]
end
end
return state
end
local function state_to_bytes(state)
local out = {}
for c = 0, 3 do
for r = 0, 3 do
out[c*4 + r + 1] = state[r][c]
end
end
return out
end
-- AES加密一轮的四个操作
local function sub_bytes(state)
for r = 0, 3 do
for c = 0, 3 do
state[r][c] = S_BOX[state[r][c]+1]
end
end
end
local function shift_rows(state)
-- 第1行左移1,第2行左移2,第3行左移3
local t = state[1][0]; state[1][0]=state[1][1]; state[1][1]=state[1][2]; state[1][2]=state[1][3]; state[1][3]=t
t = state[2][0]; state[2][0]=state[2][2]; state[2][2]=t; t=state[2][1]; state[2][1]=state[2][3]; state[2][3]=t
t = state[3][0]; state[3][0]=state[3][3]; state[3][3]=state[3][2]; state[3][2]=state[3][1]; state[3][1]=t
end
local function mix_columns(state)
for c = 0, 3 do
local a0,a1,a2,a3 = state[0][c], state[1][c], state[2][c], state[3][c]
state[0][c] = gmul(2,a0) ~ gmul(3,a1) ~ a2 ~ a3
state[1][c] = a0 ~ gmul(2,a1) ~ gmul(3,a2) ~ a3
state[2][c] = a0 ~ a1 ~ gmul(2,a2) ~ gmul(3,a3)
state[3][c] = gmul(3,a0) ~ a1 ~ a2 ~ gmul(2,a3)
end
end
local function add_round_key(state, round_key, offset)
for r = 0, 3 do
for c = 0, 3 do
state[r][c] = state[r][c] ~ round_key[offset + c*4 + r + 1]
end
end
end
-- AES-128加密单个16字节块
local function encrypt_block(plaintext, ek, offset)
local state = bytes_to_state(plaintext, offset)
add_round_key(state, ek, 0)
for round = 1, 9 do
sub_bytes(state)
shift_rows(state)
mix_columns(state)
add_round_key(state, ek, round * 16)
end
sub_bytes(state)
shift_rows(state)
add_round_key(state, ek, 160) -- 第10轮
return state_to_bytes(state)
end
-- PKCS#7 填充
local function pkcs7_pad(data, block_size)
local pad_len = block_size - (#data % block_size)
if pad_len == 0 then pad_len = block_size end
local padded = data
for i = 1, pad_len do
padded = padded .. string.char(pad_len)
end
return padded
end
-- PKCS#7 去填充
local function pkcs7_unpad(data)
local pad_len = data:byte(#data)
if pad_len > 0 and pad_len <= 16 then
return data:sub(1, #data - pad_len)
end
return data
end
-- ============================================================
-- 公开API
-- ============================================================
-- key: 16字节字符串
-- plaintext: 明文字符串
-- 返回: 密文(二进制字符串)
function AES.encrypt_ecb(key_str, plaintext)
assert(#key_str == 16, "AES-128 密钥必须为16字节")
local key = {key_str:byte(1, 16)}
local ek = key_expansion(key)
local padded = pkcs7_pad(plaintext, 16)
local ciphertext = ""
for i = 1, #padded, 16 do
local block = {padded:byte(i, i+15)}
local enc = encrypt_block(block, ek, 0)
for j = 1, 16 do
ciphertext = ciphertext .. string.char(enc[j])
end
end
return ciphertext
end
-- 解密(附带,方便测试)
local function inv_sub_bytes(state)
for r = 0, 3 do
for c = 0, 3 do
state[r][c] = INV_S_BOX[state[r][c]+1]
end
end
end
local function inv_shift_rows(state)
local t = state[1][3]; state[1][3]=state[1][2]; state[1][2]=state[1][1]; state[1][1]=state[1][0]; state[1][0]=t
t = state[2][0]; state[2][0]=state[2][2]; state[2][2]=t; t=state[2][1]; state[2][1]=state[2][3]; state[2][3]=t
t = state[3][3]; state[3][3]=state[3][0]; state[3][0]=state[3][1]; state[3][1]=state[3][2]; state[3][2]=t
end
local function inv_mix_columns(state)
for c = 0, 3 do
local a0,a1,a2,a3 = state[0][c], state[1][c], state[2][c], state[3][c]
state[0][c] = gmul(14,a0) ~ gmul(11,a1) ~ gmul(13,a2) ~ gmul(9,a3)
state[1][c] = gmul(9,a0) ~ gmul(14,a1) ~ gmul(11,a2) ~ gmul(13,a3)
state[2][c] = gmul(13,a0) ~ gmul(9,a1) ~ gmul(14,a2) ~ gmul(11,a3)
state[3][c] = gmul(11,a0) ~ gmul(13,a1) ~ gmul(9,a2) ~ gmul(14,a3)
end
end
local function decrypt_block(ciphertext, ek, offset)
local state = bytes_to_state(ciphertext, offset)
add_round_key(state, ek, 160)
for round = 9, 1, -1 do
inv_shift_rows(state)
inv_sub_bytes(state)
add_round_key(state, ek, round * 16)
inv_mix_columns(state)
end
inv_shift_rows(state)
inv_sub_bytes(state)
add_round_key(state, ek, 0)
return state_to_bytes(state)
end
function AES.decrypt_ecb(key_str, ciphertext)
assert(#key_str == 16, "AES-128 密钥必须为16字节")
local key = {key_str:byte(1, 16)}
local ek = key_expansion(key)
local plaintext = ""
for i = 1, #ciphertext, 16 do
local block = {ciphertext:byte(i, i+15)}
local dec = decrypt_block(block, ek, 0)
for j = 1, 16 do
plaintext = plaintext .. string.char(dec[j])
end
end
return pkcs7_unpad(plaintext)
end
-- ============================================================
-- 测试
-- ============================================================
-- NIST 标准测试向量
local key = "0123456789abcdef" -- 16字节
local pt = "0123456789abcdef" -- 恰好16字节(无填充)
local ct = AES.encrypt_ecb(key, pt)
local pt2 = AES.decrypt_ecb(key, ct)
print("密钥: " .. key)
print("明文: " .. pt)
print("解密还原: " .. pt2)
print("加解密一致: " .. tostring(pt == pt2))
-- 打印密文(hex格式)
print("密文(hex): ")
for i = 1, #ct do
io.write(string.format("%02X ", ct:byte(i)))
if i % 16 == 0 then print() end
end