Skip to content

Instantly share code, notes, and snippets.

@LolloDev5123
Forked from afonya2/AES.lua
Created May 29, 2024 12:56
Show Gist options
  • Save LolloDev5123/9409c11864934a7f08c7a7d65295ddad to your computer and use it in GitHub Desktop.
Save LolloDev5123/9409c11864934a7f08c7a7d65295ddad to your computer and use it in GitHub Desktop.
an AES implementation for CC: Tweaked
-- Advanced Encryption Standard (AES) libary for CC: Tweaked
-- ©️ 2024 afonya All rights reserved.
-- MIT License
-- Link: https://gist.github.com/afonya2/489c3306a7d85f8f9512df321d904dbb
-- Documentation: https://gist.github.com/afonya2/489c3306a7d85f8f9512df321d904dbb#file-docs-md
-- Last updated: February 25 2024
local SBox = {[0]=99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118,
202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8,
186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,
112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,
225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22}
local InvSBox = {[0]=82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251,
124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203,
84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78,
8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37,
114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146,
108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132,
144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6,
208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107,
58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115,
150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110,
71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27,
252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244,
31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95,
96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239,
160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97,
23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125}
local mul_2 = {[0]=0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,
32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,
64,66,68,70,72,74,76,78,80,82,84,86,88,90,92,94,
96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126,
128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,
160,162,164,166,168,170,172,174,176,178,180,182,184,186,188,190,
192,194,196,198,200,202,204,206,208,210,212,214,216,218,220,222,
224,226,228,230,232,234,236,238,240,242,244,246,248,250,252,254,
27,25,31,29,19,17,23,21,11,9,15,13,3,1,7,5,
59,57,63,61,51,49,55,53,43,41,47,45,35,33,39,37,
91,89,95,93,83,81,87,85,75,73,79,77,67,65,71,69,
123,121,127,125,115,113,119,117,107,105,111,109,99,97,103,101,
155,153,159,157,147,145,151,149,139,137,143,141,131,129,135,133,
187,185,191,189,179,177,183,181,171,169,175,173,163,161,167,165,
219,217,223,221,211,209,215,213,203,201,207,205,195,193,199,197,
251,249,255,253,243,241,247,245,235,233,239,237,227,225,231,229}
local mul_3 = {[0]=0,3,6,5,12,15,10,9,24,27,30,29,20,23,18,17,
48,51,54,53,60,63,58,57,40,43,46,45,36,39,34,33,
96,99,102,101,108,111,106,105,120,123,126,125,116,119,114,113,
80,83,86,85,92,95,90,89,72,75,78,77,68,71,66,65,
192,195,198,197,204,207,202,201,216,219,222,221,212,215,210,209,
240,243,246,245,252,255,250,249,232,235,238,237,228,231,226,225,
160,163,166,165,172,175,170,169,184,187,190,189,180,183,178,177,
144,147,150,149,156,159,154,153,136,139,142,141,132,135,130,129,
155,152,157,158,151,148,145,146,131,128,133,134,143,140,137,138,
171,168,173,174,167,164,161,162,179,176,181,182,191,188,185,186,
251,248,253,254,247,244,241,242,227,224,229,230,239,236,233,234,
203,200,205,206,199,196,193,194,211,208,213,214,223,220,217,218,
91,88,93,94,87,84,81,82,67,64,69,70,79,76,73,74,
107,104,109,110,103,100,97,98,115,112,117,118,127,124,121,122,
59,56,61,62,55,52,49,50,35,32,37,38,47,44,41,42,
11,8,13,14,7,4,1,2,19,16,21,22,31,28,25,26}
local mul_9 = {[0]=0,9,18,27,36,45,54,63,72,65,90,83,108,101,126,119,
144,153,130,139,180,189,166,175,216,209,202,195,252,245,238,231,
59,50,41,32,31,22,13,4,115,122,97,104,87,94,69,76,
171,162,185,176,143,134,157,148,227,234,241,248,199,206,213,220,
118,127,100,109,82,91,64,73,62,55,44,37,26,19,8,1,
230,239,244,253,194,203,208,217,174,167,188,181,138,131,152,145,
77,68,95,86,105,96,123,114,5,12,23,30,33,40,51,58,
221,212,207,198,249,240,235,226,149,156,135,142,177,184,163,170,
236,229,254,247,200,193,218,211,164,173,182,191,128,137,146,155,
124,117,110,103,88,81,74,67,52,61,38,47,16,25,2,11,
215,222,197,204,243,250,225,232,159,150,141,132,187,178,169,160,
71,78,85,92,99,106,113,120,15,6,29,20,43,34,57,48,
154,147,136,129,190,183,172,165,210,219,192,201,246,255,228,237,
10,3,24,17,46,39,60,53,66,75,80,89,102,111,116,125,
161,168,179,186,133,140,151,158,233,224,251,242,205,196,223,214,
49,56,35,42,21,28,7,14,121,112,107,98,93,84,79,70}
local mul_11 = {[0]=0,11,22,29,44,39,58,49,88,83,78,69,116,127,98,105,
176,187,166,173,156,151,138,129,232,227,254,245,196,207,210,217,
123,112,109,102,87,92,65,74,35,40,53,62,15,4,25,18,
203,192,221,214,231,236,241,250,147,152,133,142,191,180,169,162,
246,253,224,235,218,209,204,199,174,165,184,179,130,137,148,159,
70,77,80,91,106,97,124,119,30,21,8,3,50,57,36,47,
141,134,155,144,161,170,183,188,213,222,195,200,249,242,239,228,
61,54,43,32,17,26,7,12,101,110,115,120,73,66,95,84,
247,252,225,234,219,208,205,198,175,164,185,178,131,136,149,158,
71,76,81,90,107,96,125,118,31,20,9,2,51,56,37,46,
140,135,154,145,160,171,182,189,212,223,194,201,248,243,238,229,
60,55,42,33,16,27,6,13,100,111,114,121,72,67,94,85,
1,10,23,28,45,38,59,48,89,82,79,68,117,126,99,104,
177,186,167,172,157,150,139,128,233,226,255,244,197,206,211,216,
122,113,108,103,86,93,64,75,34,41,52,63,14,5,24,19,
202,193,220,215,230,237,240,251,146,153,132,143,190,181,168,163}
local mul_13 = {[0]=0,13,26,23,52,57,46,35,104,101,114,127,92,81,70,75,
208,221,202,199,228,233,254,243,184,181,162,175,140,129,150,155,
187,182,161,172,143,130,149,152,211,222,201,196,231,234,253,240,
107,102,113,124,95,82,69,72,3,14,25,20,55,58,45,32,
109,96,119,122,89,84,67,78,5,8,31,18,49,60,43,38,
189,176,167,170,137,132,147,158,213,216,207,194,225,236,251,246,
214,219,204,193,226,239,248,245,190,179,164,169,138,135,144,157,
6,11,28,17,50,63,40,37,110,99,116,121,90,87,64,77,
218,215,192,205,238,227,244,249,178,191,168,165,134,139,156,145,
10,7,16,29,62,51,36,41,98,111,120,117,86,91,76,65,
97,108,123,118,85,88,79,66,9,4,19,30,61,48,39,42,
177,188,171,166,133,136,159,146,217,212,195,206,237,224,247,250,
183,186,173,160,131,142,153,148,223,210,197,200,235,230,241,252,
103,106,125,112,83,94,73,68,15,2,21,24,59,54,33,44,
12,1,22,27,56,53,34,47,100,105,126,115,80,93,74,71,
220,209,198,203,232,229,242,255,180,185,174,163,128,141,154,151}
local mul_14 = {[0]=0,14,28,18,56,54,36,42,112,126,108,98,72,70,84,90,
224,238,252,242,216,214,196,202,144,158,140,130,168,166,180,186,
219,213,199,201,227,237,255,241,171,165,183,185,147,157,143,129,
59,53,39,41,3,13,31,17,75,69,87,89,115,125,111,97,
173,163,177,191,149,155,137,135,221,211,193,207,229,235,249,247,
77,67,81,95,117,123,105,103,61,51,33,47,5,11,25,23,
118,120,106,100,78,64,82,92,6,8,26,20,62,48,34,44,
150,152,138,132,174,160,178,188,230,232,250,244,222,208,194,204,
65,79,93,83,121,119,101,107,49,63,45,35,9,7,21,27,
161,175,189,179,153,151,133,139,209,223,205,195,233,231,245,251,
154,148,134,136,162,172,190,176,234,228,246,248,210,220,206,192,
122,116,102,104,66,76,94,80,10,4,22,24,50,60,46,32,
236,226,240,254,212,218,200,198,156,146,128,142,164,170,184,182,
12,2,16,30,52,58,40,38,124,114,96,110,68,74,88,86,
55,57,43,37,15,1,19,29,71,73,91,85,127,113,99,109,
215,217,203,197,239,225,243,253,167,169,187,181,159,145,131,141}
local Rcon = {{0x01, 0x00, 0x00, 0x00}, {0x02, 0x00, 0x00, 0x00}, {0x04, 0x00, 0x00, 0x00}, {0x08, 0x00, 0x00, 0x00},
{0x10, 0x00, 0x00, 0x00}, {0x20, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x00}, {0x80, 0x00, 0x00, 0x00},
{0x1b, 0x00, 0x00, 0x00}, {0x36, 0x00, 0x00, 0x00}}
function mysplit (inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, str)
end
return t
end
local Nkt = {
[16] = 4,
[24] = 6,
[32] = 8
}
local Nb = 4
local Nrt = {
[16] = 10,
[24] = 12,
[32] = 14
}
function base16ToBase10(n)
local convo = {["0"]=0,["1"]=1,["2"]=2,["3"]=3,["4"]=4,["5"]=5,["6"]=6,["7"]=7,["8"]=8,["9"]=9,["a"]=10,["b"]=11,["c"]=12,["d"]=13,["e"]=14,["f"]=15}
local out = 0
for i = 1, #n do
out = out + (convo[n:sub(i,i)] * 16 ^ (#n-i))
end
return out
end
function base10ToBase16(n)
local convo = {[0]="0",[1]="1",[2]="2",[3]="3",[4]="4",[5]="5",[6]="6",[7]="7",[8]="8",[9]="9",[10]="a",[11]="b",[12]="c",[13]="d",[14]="e",[15]="f"}
local out = ""
while n > 0 do
out = convo[n%16] .. out
n = math.floor(n/16)
end
return out
end
function StringToTable(str)
local out = {}
for i=1,#str do
table.insert(out, str:sub(i,i):byte())
end
return out
end
function TableToString(tbl)
local out = ""
for i=1,#tbl do
out = out .. string.char(tbl[i])
end
return out
end
function SubTable(tbl, start, endd)
local out = {}
for i=start,endd do
table.insert(out, tbl[i])
end
return out
end
function SubWord(word)
local out = {SBox[word[1]], SBox[word[2]], SBox[word[3]], SBox[word[4]]}
return out
end
function RotWord(word)
local rotatedWord = {word[2], word[3], word[4], word[1]}
return rotatedWord
end
function XorWord(word, word2)
local out = {bit32.bxor(word[1], word2[1]), bit32.bxor(word[2], word2[2]), bit32.bxor(word[3], word2[3]), bit32.bxor(word[4], word2[4])}
return out
end
function KeyExpansion(key, Nk, Nr)
local temp
local words = {}
for i=1, Nk do
words[i] = {key[4*(i-1)+1], key[4*(i-1)+2], key[4*(i-1)+3], key[4*(i-1)+4]}
end
for i=Nk+1, Nb * (Nr+1) do
temp = words[i-1]
if (i-1) % Nk == 0 then
temp = XorWord(SubWord(RotWord(temp)), Rcon[(i-1)/Nk])
elseif (Nk > 6) and ((i-1) % Nk == 4) then
temp = SubWord(temp)
end
words[i] = XorWord(words[i-Nk], temp)
end
return words
end
function copyBlock(block)
local out = {}
for k,v in ipairs(block) do
out[k] = v
end
return out
end
function SubBytes(block)
local newBlock = {
{SBox[block[1][1]], SBox[block[1][2]], SBox[block[1][3]], SBox[block[1][4]]},
{SBox[block[2][1]], SBox[block[2][2]], SBox[block[2][3]], SBox[block[2][4]]},
{SBox[block[3][1]], SBox[block[3][2]], SBox[block[3][3]], SBox[block[3][4]]},
{SBox[block[4][1]], SBox[block[4][2]], SBox[block[4][3]], SBox[block[4][4]]}
}
return newBlock
end
function ShiftRows(block)
local newBlock = {
{block[1][1], block[1][2], block[1][3], block[1][4]},
{block[2][2], block[2][3], block[2][4], block[2][1]},
{block[3][3], block[3][4], block[3][1], block[3][2]},
{block[4][4], block[4][1], block[4][2], block[4][3]}
}
return newBlock
end
local function Xor(...)
local args = {...}
local result = args[1]
table.remove(args, 1)
for k, v in ipairs(args) do
result = bit32.bxor(result, v)
end
return result
end
function MixColumns(block)
local cols = {
{block[1][1], block[2][1], block[3][1], block[4][1]},
{block[1][2], block[2][2], block[3][2], block[4][2]},
{block[1][3], block[2][3], block[3][3], block[4][3]},
{block[1][4], block[2][4], block[3][4], block[4][4]}
}
local newCols = {}
for k, v in ipairs(cols) do
table.insert(newCols, {})
newCols[k][1] = Xor(mul_2[v[1]], mul_3[v[2]], v[3], v[4])
newCols[k][2] = Xor(v[1], mul_2[v[2]], mul_3[v[3]], v[4])
newCols[k][3] = Xor(v[1], v[2], mul_2[v[3]], mul_3[v[4]])
newCols[k][4] = Xor(mul_3[v[1]], v[2], v[3], mul_2[v[4]])
end
local newBlock = {
{newCols[1][1], newCols[2][1], newCols[3][1], newCols[4][1]},
{newCols[1][2], newCols[2][2], newCols[3][2], newCols[4][2]},
{newCols[1][3], newCols[2][3], newCols[3][3], newCols[4][3]},
{newCols[1][4], newCols[2][4], newCols[3][4], newCols[4][4]}
}
return newBlock
end
function AddRoundKey(block, words, keyId)
local cols = {
{block[1][1], block[2][1], block[3][1], block[4][1]},
{block[1][2], block[2][2], block[3][2], block[4][2]},
{block[1][3], block[2][3], block[3][3], block[4][3]},
{block[1][4], block[2][4], block[3][4], block[4][4]}
}
local roundKey = {}
for i=4*(keyId-1)+1, 4*keyId do
table.insert(roundKey, words[i])
end
for k, v in ipairs(cols) do
cols[k] = XorWord(v, roundKey[k])
end
local newBlock = {
{cols[1][1], cols[2][1], cols[3][1], cols[4][1]},
{cols[1][2], cols[2][2], cols[3][2], cols[4][2]},
{cols[1][3], cols[2][3], cols[3][3], cols[4][3]},
{cols[1][4], cols[2][4], cols[3][4], cols[4][4]}
}
return newBlock
end
function EncryptBlock(block, words, Nr)
local state = copyBlock(block)
state = AddRoundKey(state, words, 1)
for round=1, Nr-1 do
state = SubBytes(state)
state = ShiftRows(state)
state = MixColumns(state)
state = AddRoundKey(state, words, round+1)
end
state = SubBytes(state)
state = ShiftRows(state)
state = AddRoundKey(state, words, Nr+1)
return state
end
function AES_Block(inp)
local out = {
{},
{},
{},
{}
}
local i = 1
for x = 1, 4 do
for y = 1, 4 do
table.insert(out[y], inp[i] ~= nil and inp[i] or 0)
i = i + 1
end
end
return out
end
function AES_Encrypt(plaintext, key)
if type(plaintext) ~= "table" then
error("AES_Encrypt: bad argument #1 (expected table, got "..type(plaintext)..")")
end
for k,v in ipairs(plaintext) do
if type(v) ~= "number" then
error("AES_Encrypt: Invalid plaintext at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_Encrypt: Invalid plaintext at "..k.." (expected number (0-255), got "..v..")")
end
end
if type(key) ~= "table" then
error("AES_Encrypt: bad argument #2 (expected table, got "..type(key)..")")
end
for k,v in ipairs(key) do
if type(v) ~= "number" then
error("AES_Encrypt: Invalid key at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_Encrypt: Invalid key at "..k.." (expected number (0-255), got "..v..")")
end
end
if #key > 32 then
key = SubTable(key, 1, 32)
end
if Nkt[#key] == nil then
error("AES_Encrypt: Key must be 16, 24, 32 characters long")
end
local blocks = {}
for i=1, math.ceil(#plaintext/16) do
table.insert(blocks, AES_Block(SubTable(plaintext, 16*(i-1)+1, 16*(i-1)+1+16)))
end
local words = KeyExpansion(key, Nkt[#key], Nrt[#key])
local s = os.clock()
for k,v in ipairs(blocks) do
blocks[k] = EncryptBlock(v, words, Nrt[#key])
if os.clock() - s >= 2.5 then
os.queueEvent("")
os.pullEvent("")
s = os.clock()
end
end
local out = {}
for k,v in ipairs(blocks) do
for x = 1, 4 do
for y = 1, 4 do
table.insert(out, v[y][x])
end
end
end
return out
end
function InvShiftRows(block)
local newBlock = {
{block[1][1], block[1][2], block[1][3], block[1][4]},
{block[2][4], block[2][1], block[2][2], block[2][3]},
{block[3][3], block[3][4], block[3][1], block[3][2]},
{block[4][2], block[4][3], block[4][4], block[4][1]}
}
return newBlock
end
function InvSubBytes(block)
local newBlock = {
{InvSBox[block[1][1]], InvSBox[block[1][2]], InvSBox[block[1][3]], InvSBox[block[1][4]]},
{InvSBox[block[2][1]], InvSBox[block[2][2]], InvSBox[block[2][3]], InvSBox[block[2][4]]},
{InvSBox[block[3][1]], InvSBox[block[3][2]], InvSBox[block[3][3]], InvSBox[block[3][4]]},
{InvSBox[block[4][1]], InvSBox[block[4][2]], InvSBox[block[4][3]], InvSBox[block[4][4]]}
}
return newBlock
end
function InvMixColumns(block)
local cols = {
{block[1][1], block[2][1], block[3][1], block[4][1]},
{block[1][2], block[2][2], block[3][2], block[4][2]},
{block[1][3], block[2][3], block[3][3], block[4][3]},
{block[1][4], block[2][4], block[3][4], block[4][4]}
}
local newCols = {}
for k, v in ipairs(cols) do
table.insert(newCols, {})
newCols[k][1] = Xor(mul_14[v[1]], mul_11[v[2]], mul_13[v[3]], mul_9[v[4]])
newCols[k][2] = Xor(mul_9[v[1]], mul_14[v[2]], mul_11[v[3]], mul_13[v[4]])
newCols[k][3] = Xor(mul_13[v[1]], mul_9[v[2]], mul_14[v[3]], mul_11[v[4]])
newCols[k][4] = Xor(mul_11[v[1]], mul_13[v[2]], mul_9[v[3]], mul_14[v[4]])
end
local newBlock = {
{newCols[1][1], newCols[2][1], newCols[3][1], newCols[4][1]},
{newCols[1][2], newCols[2][2], newCols[3][2], newCols[4][2]},
{newCols[1][3], newCols[2][3], newCols[3][3], newCols[4][3]},
{newCols[1][4], newCols[2][4], newCols[3][4], newCols[4][4]}
}
return newBlock
end
function DecryptBlock(block, words, Nr)
local state = copyBlock(block)
state = AddRoundKey(state, words, Nr+1)
for round=Nr-1, 1, -1 do
state = InvShiftRows(state)
state = InvSubBytes(state)
state = AddRoundKey(state, words, round+1)
state = InvMixColumns(state)
end
state = InvShiftRows(state)
state = InvSubBytes(state)
state = AddRoundKey(state, words, 1)
return state
end
function AES_Decrypt(ciphertext, key)
if type(ciphertext) ~= "table" then
error("AES_Decrypt: bad argument #1 (expected table, got "..type(ciphertext)..")")
end
for k,v in ipairs(ciphertext) do
if type(v) ~= "number" then
error("AES_Decrypt: Invalid ciphertext at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_Decrypt: Invalid ciphertext at "..k.." (expected number (0-255), got "..v..")")
end
end
if type(key) ~= "table" then
error("AES_Decrypt: bad argument #2 (expected table, got "..type(key)..")")
end
for k,v in ipairs(key) do
if type(v) ~= "number" then
error("AES_Decrypt: Invalid key at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_Decrypt: Invalid key at "..k.." (expected number (0-255), got "..v..")")
end
end
if #key > 32 then
key = SubTable(key, 1, 32)
end
if Nkt[#key] == nil then
error("AES_Decrypt: Key must be 16, 24, 32 characters long")
end
local blocks = {}
for i=1, math.ceil(#ciphertext/16) do
table.insert(blocks, AES_Block(SubTable(ciphertext, 16*(i-1)+1, 16*(i-1)+1+16)))
end
local words = KeyExpansion(key, Nkt[#key], Nrt[#key])
local s = os.clock()
for k,v in ipairs(blocks) do
blocks[k] = DecryptBlock(v, words, Nrt[#key])
if os.clock() - s >= 2.5 then
os.queueEvent("")
os.pullEvent("")
s = os.clock()
end
end
local out = {}
for k,v in ipairs(blocks) do
for x = 1, 4 do
for y = 1, 4 do
table.insert(out, v[y][x])
end
end
end
return out
end
function StringToBytes(str)
local out = {}
for i=1,#str do
table.insert(out, str:sub(i,i):byte())
end
return out
end
function InvertString(str)
local function rawInvert(iStr)
local out = ""
for i=#iStr, 1, -1 do
out = out .. iStr:sub(i,i)
end
return out
end
if str:match("e") == nil then
return rawInvert(str)
else
local firstp = "[" .. mysplit(str, ".")[1] .. ".]"
str = str:gsub(firstp, "")
local secondp = "[e" .. mysplit(str, "e")[2] .. "]"
str = str:gsub(secondp, "")
str = rawInvert(str)
str = firstp:gsub("[[]", ""):gsub("[]]", "") .. str .. secondp:gsub("[[]", ""):gsub("[]]", "")
return str
end
end
function BytesToHexString(tbl)
local out = ""
for i=1,#tbl do
out = out .. base10ToBase16(tbl[i])
end
return out
end
function ASH_Hash(plaintext, rounds, salt, len)
plaintext = StringToBytes(plaintext)
local s = os.clock()
for round=0, rounds-1 do
if round % 2 ~= 0 then
for i=1, #plaintext do
plaintext[i] = plaintext[i] + (plaintext[i+1] ~= nil and plaintext[i+1] or plaintext[1]) + salt
end
else
for i=1, #plaintext do
plaintext[i] = math.floor(tonumber(InvertString(tostring(plaintext[i]))))
end
for i=#plaintext, 1, -1 do
plaintext[i] = plaintext[i] + (plaintext[i-1] ~= nil and plaintext[i-1] or plaintext[#plaintext]) + salt
end
end
if os.clock() - s >= 2.5 then
os.queueEvent("")
os.pullEvent("")
s = os.clock()
end
end
local out = BytesToHexString(plaintext)
while #out < len do
out = out .. out
end
if #out > len then
out = out:sub(1, len)
end
return out
end
function StringToKey(str, salt)
if type(str) ~= "string" then
error("StringToKey: bad argument #1 (expected string, got "..type(str)..")")
end
if type(salt) ~= "number" then
error("StringToKey: bad argument #2 (expected number, got "..type(salt)..")")
end
local hash = ASH_Hash(str, 32, salt, 32)
local key = {}
for i=1,#hash do
table.insert(key, hash:sub(i,i):byte())
end
return key
end
function GenerateRandomKey()
local keyCache = {}
local key = {}
for i=1, 32 do
local rand = 0
while (rand == 0) or (keyCache[rand] ~= nil) do
rand = math.random(1, 255)
end
keyCache[rand] = true
table.insert(key, rand)
end
return key
end
function XorBlock(block1, block2)
local out = copyBlock(block1)
for y=1,4 do
for x=1,4 do
out[y][x] = Xor(out[y][x], block2[y][x])
end
end
return out
end
function AES_EncryptCBC(plaintext, key, iv)
if type(plaintext) ~= "table" then
error("AES_EncryptCBC: bad argument #1 (expected table, got "..type(plaintext)..")")
end
for k,v in ipairs(plaintext) do
if type(v) ~= "number" then
error("AES_EncryptCBC: Invalid plaintext at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_EncryptCBC: Invalid plaintext at "..k.." (expected number (0-255), got "..v..")")
end
end
if type(key) ~= "table" then
error("AES_EncryptCBC: bad argument #2 (expected table, got "..type(key)..")")
end
for k,v in ipairs(key) do
if type(v) ~= "number" then
error("AES_EncryptCBC: Invalid key at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_EncryptCBC: Invalid key at "..k.." (expected number (0-255), got "..v..")")
end
end
if #key > 32 then
key = SubTable(key, 1, 32)
end
if Nkt[#key] == nil then
error("AES_EncryptCBC: Key must be 16, 24, 32 characters long")
end
if type(iv) ~= "table" then
error("AES_EncryptCBC: bad argument #3 (expected table, got "..type(iv)..")")
end
for k,v in ipairs(iv) do
if type(v) ~= "number" then
error("AES_EncryptCBC: Invalid iv at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_EncryptCBC: Invalid iv at "..k.." (expected number (0-255), got "..v..")")
end
end
if #iv > 16 then
iv = SubTable(iv, 1, 16)
end
if #iv ~= 16 then
error("AES_EncryptCBC: Iv must be 16 characters long")
end
local blocks = {}
table.insert(blocks, AES_Block(iv))
for i=1, math.ceil(#plaintext/16) do
table.insert(blocks, AES_Block(SubTable(plaintext, 16*(i-1)+1, 16*(i-1)+1+16)))
end
local words = KeyExpansion(key, Nkt[#key], Nrt[#key])
local s = os.clock()
for i=2,#blocks do
blocks[i] = XorBlock(blocks[i], blocks[i-1])
blocks[i] = EncryptBlock(blocks[i], words, Nrt[#key])
if os.clock() - s >= 2.5 then
os.queueEvent("")
os.pullEvent("")
s = os.clock()
end
end
local out = {}
table.remove(blocks, 1)
for k,v in ipairs(blocks) do
for x = 1, 4 do
for y = 1, 4 do
table.insert(out, v[y][x])
end
end
end
return out
end
function AES_DecryptCBC(ciphertext, key, iv)
if type(ciphertext) ~= "table" then
error("AES_DecryptCBC: bad argument #1 (expected table, got "..type(ciphertext)..")")
end
for k,v in ipairs(ciphertext) do
if type(v) ~= "number" then
error("AES_DecryptCBC: Invalid ciphertext at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_DecryptCBC: Invalid ciphertext at "..k.." (expected number (0-255), got "..v..")")
end
end
if type(key) ~= "table" then
error("AES_DecryptCBC: bad argument #2 (expected table, got "..type(key)..")")
end
for k,v in ipairs(key) do
if type(v) ~= "number" then
error("AES_DecryptCBC: Invalid key at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_DecryptCBC: Invalid key at "..k.." (expected number (0-255), got "..v..")")
end
end
if #key > 32 then
key = SubTable(key, 1, 32)
end
if Nkt[#key] == nil then
error("AES_DecryptCBC: Key must be 16, 24, 32 characters long")
end
if type(iv) ~= "table" then
error("AES_DecryptCBC: bad argument #3 (expected table, got "..type(iv)..")")
end
for k,v in ipairs(iv) do
if type(v) ~= "number" then
error("AES_DecryptCBC: Invalid iv at "..k.." (expected number (0-255), got "..type(v)..")")
end
if (v < 0) or (v > 255) then
error("AES_DecryptCBC: Invalid iv at "..k.." (expected number (0-255), got "..v..")")
end
end
if #iv > 16 then
iv = SubTable(iv, 1, 16)
end
if #iv ~= 16 then
error("AES_DecryptCBC: Iv must be 16 characters long")
end
local pblocks = {}
local blocks = {}
table.insert(pblocks, AES_Block(iv))
table.insert(blocks, AES_Block(iv))
for i=1, math.ceil(#ciphertext/16) do
table.insert(blocks, AES_Block(SubTable(ciphertext, 16*(i-1)+1, 16*(i-1)+1+16)))
table.insert(pblocks, AES_Block(SubTable(ciphertext, 16*(i-1)+1, 16*(i-1)+1+16)))
end
local words = KeyExpansion(key, Nkt[#key], Nrt[#key])
local s = os.clock()
for i=2,#blocks do
blocks[i] = DecryptBlock(blocks[i], words, Nrt[#key])
blocks[i] = XorBlock(blocks[i], pblocks[i-1])
if os.clock() - s >= 2.5 then
os.queueEvent("")
os.pullEvent("")
s = os.clock()
end
end
local out = {}
table.remove(blocks, 1)
for k,v in ipairs(blocks) do
for x = 1, 4 do
for y = 1, 4 do
table.insert(out, v[y][x])
end
end
end
return out
end
return {
Encrypt = AES_Encrypt,
Decrypt = AES_Decrypt,
EncryptCBC = AES_EncryptCBC,
DecryptCBC = AES_DecryptCBC,
StringToKey = StringToKey,
GenerateRandomKey = GenerateRandomKey,
StringToTable = StringToTable,
TableToString = TableToString
}

Advanced Encryption Standard (AES) library for CC: Tweaked

an AES implementation for CC: Tweaked
based on: wikipedia, FIPS 197

How to install

run: wget https://gist.githubusercontent.com/afonya2/489c3306a7d85f8f9512df321d904dbb/raw/AES.lua AES.lua in a CC: Tweaked computer (pretty much every lua emulator should be able to run it)

Examples

Simple encryption and decryption:

local AES = require("AES")
local key = AES.StringToKey("This is an example key", 1024)
local encryptedText = AES.EncryptCBC(AES.StringToTable("This is an example message"), key)
local decryptedText = AES.TableToString(AES.DecryptCBC(encryptedText, key))
-- If you want to remove the "\0" (nothing) characters from the end of the decryptedText you need to remove the comment ("--") below.
-- It adds this character because your message length was not divisible by 16
-- decryptedText = decryptedText:gsub("\0","")
print(AES.TableToString(encryptedText))
print(decryptedText)

Random key encryption:

local AES = require("AES")
local key = AES.GenerateRandomKey()
local encryptedText = AES.EncryptCBC(AES.StringToTable("This is an example message"), key)
local decryptedText = AES.TableToString(AES.DecryptCBC(encryptedText, key))
-- If you want to remove the "\0" (nothing) characters from the end of the decryptedText you need to remove the comment ("--") below.
-- It adds this character because your message length was not divisible by 16
-- decryptedText = decryptedText:gsub("\0","")
print(AES.TableToString(encryptedText))
print(decryptedText)

Benchmark script:

local AES = require("AES")

local blockCount = 1000
local key = AES.GenerateRandomKey()
local iv = AES.GenerateRandomKey()
local inp = {}
for i=1,blockCount*16 do
    table.insert(inp, math.random(0,255))
end

print("AES Encrypt test ("..blockCount.." blocks, 256 bit key):")
local start = os.epoch("utc")
AES.Encrypt(inp, key)
local endd = os.epoch("utc")
local dist = endd-start
print("Time took: "..tostring(dist).."ms")
print("Block/sec: "..tostring(math.floor(blockCount/(dist/1000)*100)/100))
print("sec/Block: "..tostring(math.floor((dist/1000)/blockCount*100)/100))
print("----------------------------------------")

print("AES CBC Encrypt test ("..blockCount.." blocks, 256 bit key):")
local start = os.epoch("utc")
AES.EncryptCBC(inp, key, iv)
local endd = os.epoch("utc")
local dist = endd-start
print("Time took: "..tostring(dist).."ms")
print("Block/sec: "..tostring(math.floor(blockCount/(dist/1000)*100)/100))
print("sec/Block: "..tostring(math.floor((dist/1000)/blockCount*100)/100))

Disk encryption (like in bitlocker): (soon™️)

Documentation

AES.Encrypt

Encrypts the plaintext with the given key AES.Encrypt(table: plaintext, table: key):table, Note that the key must be 16, 24 or 32 characters long. It uses ECB (Electronic codebook) mode, it is not recommended to use it in production.

AES.Decrypt

Decrypts the ciphertext with the given key AES.Decrypt(table: ciphertext, table: key):table, Note that the key must be 16, 24 or 32 characters long. It uses ECB (Electronic codebook) mode, it is not recommended to use it in production.

AES.EncryptCBC

Encrypts the plaintext with the given key AES.EncryptCBC(table: plaintext, table: key, table:iv):table, Note that the key must be 16, 24 or 32 characters long. Also note that iv (initialization vector) must be 16 characters long. It uses CBC (Cipher block chaining) mode.

AES.DecryptCBC

Decrypts the ciphertext with the given key AES.DecryptCBC(table: ciphertext, table: key, table:iv):table, Note that the key must be 16, 24 or 32 characters long. Also note that iv (initialization vector) must be 16 characters long. It uses CBC (Cipher block chaining) mode.

AES.StringToKey

Converts a string into a key table AES.StringToKey(string: key, number: salt):table, you can feed this directly as the key to the encrypt/decrypt functions.
Note that this is using a simple "hashing" algorithm to generate the key. I recommend using an sha-256 library instead of this. It's good if you just want to test out some things. Also note that the salt can be 0.

AES.GenerateRandomKey

Generates a random key (32 bytes long) AES.GenerateRandomKey():table, you can feed this directly as the key to the encrypt/decrypt functions.

AES.StringToTable

Converts a string into a table of bytes AES.StringToTable(string: input):table, you can feed this directly as the plaintext/ciphertext to the encrypt/decrypt functions.

AES.TableToString

Converts a table of bytes into a string AES.TableToString(table: input):table, you can use this to convert the output of the encrypt/decrypt functions to a string.

Benchmark (1000 blocks, 256 bit key) (CraftOS-PC, Lua 5.2, CC 1.109.3)

  • Script aviable above
  • ECB Encrypt
    • Time took: ~1000ms
    • Block/Sec: ~1000
  • CBC Encrypt
    • Time took: ~1000ms
    • Block/Sec: ~1000

Last updates

February 25 2024

  • Added CBC mode for encryption/decryption
  • Made it much more faster
  • The encrypt/decrypt functions now require a table of bytes as ciphertext/plaintext, and they return a table of bytes too

License (MIT)

Copyright (c) 2024 afonya

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment