ldd-CC/API/aeslua.lua

1199 lines
27 KiB
Lua

local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
local bit=_W(function(_ENV, ...)
--[[
This bit API is designed to cope with unsigned integers instead of normal integers
To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
These are written in long form because no constant folding.
]]
local floor = math.floor
local lshift, rshift
rshift = function(a,disp)
return floor(a % 4294967296 / 2^disp)
end
lshift = function(a,disp)
return (a * 2^disp) % 4294967296
end
return {
-- bit operations
bnot = bit.bnot,
band = bit.band,
bor = bit.bor,
bxor = bit.bxor,
rshift = rshift,
lshift = lshift,
}
end)
local gf=_W(function(_ENV, ...)
-- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
local bxor = bit.bxor
local lshift = bit.lshift
-- private data of gf
local n = 0x100
local ord = 0xff
local irrPolynom = 0x11b
local exp = {}
local log = {}
--
-- add two polynoms (its simply xor)
--
local function add(operand1, operand2)
return bxor(operand1,operand2)
end
--
-- subtract two polynoms (same as addition)
--
local function sub(operand1, operand2)
return bxor(operand1,operand2)
end
--
-- inverts element
-- a^(-1) = g^(order - log(a))
--
local function invert(operand)
-- special case for 1
if (operand == 1) then
return 1
end
-- normal invert
local exponent = ord - log[operand]
return exp[exponent]
end
--
-- multiply two elements using a logarithm table
-- a*b = g^(log(a)+log(b))
--
local function mul(operand1, operand2)
if (operand1 == 0 or operand2 == 0) then
return 0
end
local exponent = log[operand1] + log[operand2]
if (exponent >= ord) then
exponent = exponent - ord
end
return exp[exponent]
end
--
-- divide two elements
-- a/b = g^(log(a)-log(b))
--
local function div(operand1, operand2)
if (operand1 == 0) then
return 0
end
-- TODO: exception if operand2 == 0
local exponent = log[operand1] - log[operand2]
if (exponent < 0) then
exponent = exponent + ord
end
return exp[exponent]
end
--
-- print logarithmic table
--
local function printLog()
for i = 1, n do
print("log(", i-1, ")=", log[i-1])
end
end
--
-- print exponentiation table
--
local function printExp()
for i = 1, n do
print("exp(", i-1, ")=", exp[i-1])
end
end
--
-- calculate logarithmic and exponentiation table
--
local function initMulTable()
local a = 1
for i = 0,ord-1 do
exp[i] = a
log[a] = i
-- multiply with generator x+1 -> left shift + 1
a = bxor(lshift(a, 1), a)
-- if a gets larger than order, reduce modulo irreducible polynom
if a > ord then
a = sub(a, irrPolynom)
end
end
end
initMulTable()
return {
add = add,
sub = sub,
invert = invert,
mul = mul,
div = dib,
printLog = printLog,
printExp = printExp,
}
end)
util=_W(function(_ENV, ...)
-- Cache some bit operators
local bxor = bit.bxor
local rshift = bit.rshift
local band = bit.band
local lshift = bit.lshift
local sleepCheckIn
--
-- calculate the parity of one byte
--
local function byteParity(byte)
byte = bxor(byte, rshift(byte, 4))
byte = bxor(byte, rshift(byte, 2))
byte = bxor(byte, rshift(byte, 1))
return band(byte, 1)
end
--
-- get byte at position index
--
local function getByte(number, index)
if (index == 0) then
return band(number,0xff)
else
return band(rshift(number, index*8),0xff)
end
end
--
-- put number into int at position index
--
local function putByte(number, index)
if (index == 0) then
return band(number,0xff)
else
return lshift(band(number,0xff),index*8)
end
end
--
-- convert byte array to int array
--
local function bytesToInts(bytes, start, n)
local ints = {}
for i = 0, n - 1 do
ints[i + 1] =
putByte(bytes[start + (i*4)], 3) +
putByte(bytes[start + (i*4) + 1], 2) +
putByte(bytes[start + (i*4) + 2], 1) +
putByte(bytes[start + (i*4) + 3], 0)
if n % 10000 == 0 then sleepCheckIn() end
end
return ints
end
--
-- convert int array to byte array
--
local function intsToBytes(ints, output, outputOffset, n)
n = n or #ints
for i = 0, n - 1 do
for j = 0,3 do
output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
end
if n % 10000 == 0 then sleepCheckIn() end
end
return output
end
--
-- convert bytes to hexString
--
local function bytesToHex(bytes)
local hexBytes = ""
for i,byte in ipairs(bytes) do
hexBytes = hexBytes .. string.format("%02x ", byte)
end
return hexBytes
end
local function hexToBytes(bytes)
local out = {}
for i = 1, #bytes, 2 do
out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
end
return out
end
--
-- convert data to hex string
--
local function toHexString(data)
local type = type(data)
if (type == "number") then
return string.format("%08x",data)
elseif (type == "table") then
return bytesToHex(data)
elseif (type == "string") then
local bytes = {string.byte(data, 1, #data)}
return bytesToHex(bytes)
else
return data
end
end
local function padByteString(data)
local dataLength = #data
local random1 = math.random(0,255)
local random2 = math.random(0,255)
local prefix = string.char(random1,
random2,
random1,
random2,
getByte(dataLength, 3),
getByte(dataLength, 2),
getByte(dataLength, 1),
getByte(dataLength, 0)
)
data = prefix .. data
local paddingLength = math.ceil(#data/16)*16 - #data
local padding = ""
for i=1,paddingLength do
padding = padding .. string.char(math.random(0,255))
end
return data .. padding
end
local function properlyDecrypted(data)
local random = {string.byte(data,1,4)}
if (random[1] == random[3] and random[2] == random[4]) then
return true
end
return false
end
local function unpadByteString(data)
if (not properlyDecrypted(data)) then
return nil
end
local dataLength = putByte(string.byte(data,5), 3)
+ putByte(string.byte(data,6), 2)
+ putByte(string.byte(data,7), 1)
+ putByte(string.byte(data,8), 0)
return string.sub(data,9,8+dataLength)
end
local function xorIV(data, iv)
for i = 1,16 do
data[i] = bxor(data[i], iv[i])
end
end
local function increment(data)
local i = 16
while true do
local value = data[i] + 1
if value >= 256 then
data[i] = value - 256
i = (i - 2) % 16 + 1
else
data[i] = value
break
end
end
end
-- Called every encryption cycle
local push, pull, time = os.queueEvent, coroutine.yield, os.time
local oldTime = time()
local function sleepCheckIn()
local newTime = time()
if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
oldTime = newTime
push("sleep")
pull("sleep")
end
end
local function getRandomData(bytes)
local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
local result = {}
for i=1,bytes do
insert(result, random(0,255))
if i % 10240 == 0 then sleep() end
end
return result
end
local function getRandomString(bytes)
local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
local result = {}
for i=1,bytes do
insert(result, char(random(0,255)))
if i % 10240 == 0 then sleep() end
end
return table.concat(result)
end
return {
byteParity = byteParity,
getByte = getByte,
putByte = putByte,
bytesToInts = bytesToInts,
intsToBytes = intsToBytes,
bytesToHex = bytesToHex,
hexToBytes = hexToBytes,
toHexString = toHexString,
padByteString = padByteString,
properlyDecrypted = properlyDecrypted,
unpadByteString = unpadByteString,
xorIV = xorIV,
increment = increment,
sleepCheckIn = sleepCheckIn,
getRandomData = getRandomData,
getRandomString = getRandomString,
}
end)
aes=_W(function(_ENV, ...)
-- Implementation of AES with nearly pure lua
-- AES with lua is slow, really slow :-)
local putByte = util.putByte
local getByte = util.getByte
-- some constants
local ROUNDS = 'rounds'
local KEY_TYPE = "type"
local ENCRYPTION_KEY=1
local DECRYPTION_KEY=2
-- aes SBOX
local SBox = {}
local iSBox = {}
-- aes tables
local table0 = {}
local table1 = {}
local table2 = {}
local table3 = {}
local tableInv0 = {}
local tableInv1 = {}
local tableInv2 = {}
local tableInv3 = {}
-- round constants
local rCon = {
0x01000000,
0x02000000,
0x04000000,
0x08000000,
0x10000000,
0x20000000,
0x40000000,
0x80000000,
0x1b000000,
0x36000000,
0x6c000000,
0xd8000000,
0xab000000,
0x4d000000,
0x9a000000,
0x2f000000,
}
--
-- affine transformation for calculating the S-Box of AES
--
local function affinMap(byte)
mask = 0xf8
result = 0
for i = 1,8 do
result = bit.lshift(result,1)
parity = util.byteParity(bit.band(byte,mask))
result = result + parity
-- simulate roll
lastbit = bit.band(mask, 1)
mask = bit.band(bit.rshift(mask, 1),0xff)
if (lastbit ~= 0) then
mask = bit.bor(mask, 0x80)
else
mask = bit.band(mask, 0x7f)
end
end
return bit.bxor(result, 0x63)
end
--
-- calculate S-Box and inverse S-Box of AES
-- apply affine transformation to inverse in finite field 2^8
--
local function calcSBox()
for i = 0, 255 do
if (i ~= 0) then
inverse = gf.invert(i)
else
inverse = i
end
mapped = affinMap(inverse)
SBox[i] = mapped
iSBox[mapped] = i
end
end
--
-- Calculate round tables
-- round tables are used to calculate shiftRow, MixColumn and SubBytes
-- with 4 table lookups and 4 xor operations.
--
local function calcRoundTables()
for x = 0,255 do
byte = SBox[x]
table0[x] = putByte(gf.mul(0x03, byte), 0)
+ putByte( byte , 1)
+ putByte( byte , 2)
+ putByte(gf.mul(0x02, byte), 3)
table1[x] = putByte( byte , 0)
+ putByte( byte , 1)
+ putByte(gf.mul(0x02, byte), 2)
+ putByte(gf.mul(0x03, byte), 3)
table2[x] = putByte( byte , 0)
+ putByte(gf.mul(0x02, byte), 1)
+ putByte(gf.mul(0x03, byte), 2)
+ putByte( byte , 3)
table3[x] = putByte(gf.mul(0x02, byte), 0)
+ putByte(gf.mul(0x03, byte), 1)
+ putByte( byte , 2)
+ putByte( byte , 3)
end
end
--
-- Calculate inverse round tables
-- does the inverse of the normal roundtables for the equivalent
-- decryption algorithm.
--
local function calcInvRoundTables()
for x = 0,255 do
byte = iSBox[x]
tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
+ putByte(gf.mul(0x0d, byte), 1)
+ putByte(gf.mul(0x09, byte), 2)
+ putByte(gf.mul(0x0e, byte), 3)
tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
+ putByte(gf.mul(0x09, byte), 1)
+ putByte(gf.mul(0x0e, byte), 2)
+ putByte(gf.mul(0x0b, byte), 3)
tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
+ putByte(gf.mul(0x0e, byte), 1)
+ putByte(gf.mul(0x0b, byte), 2)
+ putByte(gf.mul(0x0d, byte), 3)
tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
+ putByte(gf.mul(0x0b, byte), 1)
+ putByte(gf.mul(0x0d, byte), 2)
+ putByte(gf.mul(0x09, byte), 3)
end
end
--
-- rotate word: 0xaabbccdd gets 0xbbccddaa
-- used for key schedule
--
local function rotWord(word)
local tmp = bit.band(word,0xff000000)
return (bit.lshift(word,8) + bit.rshift(tmp,24))
end
--
-- replace all bytes in a word with the SBox.
-- used for key schedule
--
local function subWord(word)
return putByte(SBox[getByte(word,0)],0)
+ putByte(SBox[getByte(word,1)],1)
+ putByte(SBox[getByte(word,2)],2)
+ putByte(SBox[getByte(word,3)],3)
end
--
-- generate key schedule for aes encryption
--
-- returns table with all round keys and
-- the necessary number of rounds saved in [ROUNDS]
--
local function expandEncryptionKey(key)
local keySchedule = {}
local keyWords = math.floor(#key / 4)
if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
error("Invalid key size: " .. tostring(keyWords))
return nil
end
keySchedule[ROUNDS] = keyWords + 6
keySchedule[KEY_TYPE] = ENCRYPTION_KEY
for i = 0,keyWords - 1 do
keySchedule[i] = putByte(key[i*4+1], 3)
+ putByte(key[i*4+2], 2)
+ putByte(key[i*4+3], 1)
+ putByte(key[i*4+4], 0)
end
for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
local tmp = keySchedule[i-1]
if ( i % keyWords == 0) then
tmp = rotWord(tmp)
tmp = subWord(tmp)
local index = math.floor(i/keyWords)
tmp = bit.bxor(tmp,rCon[index])
elseif (keyWords > 6 and i % keyWords == 4) then
tmp = subWord(tmp)
end
keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
end
return keySchedule
end
--
-- Inverse mix column
-- used for key schedule of decryption key
--
local function invMixColumnOld(word)
local b0 = getByte(word,3)
local b1 = getByte(word,2)
local b2 = getByte(word,1)
local b3 = getByte(word,0)
return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
gf.mul(0x0d, b2)),
gf.mul(0x09, b3)),
gf.mul(0x0e, b0)),3)
+ putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
gf.mul(0x0d, b3)),
gf.mul(0x09, b0)),
gf.mul(0x0e, b1)),2)
+ putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
gf.mul(0x0d, b0)),
gf.mul(0x09, b1)),
gf.mul(0x0e, b2)),1)
+ putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
gf.mul(0x0d, b1)),
gf.mul(0x09, b2)),
gf.mul(0x0e, b3)),0)
end
--
-- Optimized inverse mix column
-- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
-- TODO: make it work
--
local function invMixColumn(word)
local b0 = getByte(word,3)
local b1 = getByte(word,2)
local b2 = getByte(word,1)
local b3 = getByte(word,0)
local t = bit.bxor(b3,b2)
local u = bit.bxor(b1,b0)
local v = bit.bxor(t,u)
v = bit.bxor(v,gf.mul(0x08,v))
w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
+ putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t )), 1)
+ putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
+ putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u )), 3)
end
--
-- generate key schedule for aes decryption
--
-- uses key schedule for aes encryption and transforms each
-- key by inverse mix column.
--
local function expandDecryptionKey(key)
local keySchedule = expandEncryptionKey(key)
if (keySchedule == nil) then
return nil
end
keySchedule[KEY_TYPE] = DECRYPTION_KEY
for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
keySchedule[i] = invMixColumnOld(keySchedule[i])
end
return keySchedule
end
--
-- xor round key to state
--
local function addRoundKey(state, key, round)
for i = 0, 3 do
state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
end
end
--
-- do encryption round (ShiftRow, SubBytes, MixColumn together)
--
local function doRound(origState, dstState)
dstState[1] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[1],3)],
table1[getByte(origState[2],2)]),
table2[getByte(origState[3],1)]),
table3[getByte(origState[4],0)])
dstState[2] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[2],3)],
table1[getByte(origState[3],2)]),
table2[getByte(origState[4],1)]),
table3[getByte(origState[1],0)])
dstState[3] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[3],3)],
table1[getByte(origState[4],2)]),
table2[getByte(origState[1],1)]),
table3[getByte(origState[2],0)])
dstState[4] = bit.bxor(bit.bxor(bit.bxor(
table0[getByte(origState[4],3)],
table1[getByte(origState[1],2)]),
table2[getByte(origState[2],1)]),
table3[getByte(origState[3],0)])
end
--
-- do last encryption round (ShiftRow and SubBytes)
--
local function doLastRound(origState, dstState)
dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
+ putByte(SBox[getByte(origState[2],2)], 2)
+ putByte(SBox[getByte(origState[3],1)], 1)
+ putByte(SBox[getByte(origState[4],0)], 0)
dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
+ putByte(SBox[getByte(origState[3],2)], 2)
+ putByte(SBox[getByte(origState[4],1)], 1)
+ putByte(SBox[getByte(origState[1],0)], 0)
dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
+ putByte(SBox[getByte(origState[4],2)], 2)
+ putByte(SBox[getByte(origState[1],1)], 1)
+ putByte(SBox[getByte(origState[2],0)], 0)
dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
+ putByte(SBox[getByte(origState[1],2)], 2)
+ putByte(SBox[getByte(origState[2],1)], 1)
+ putByte(SBox[getByte(origState[3],0)], 0)
end
--
-- do decryption round
--
local function doInvRound(origState, dstState)
dstState[1] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[1],3)],
tableInv1[getByte(origState[4],2)]),
tableInv2[getByte(origState[3],1)]),
tableInv3[getByte(origState[2],0)])
dstState[2] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[2],3)],
tableInv1[getByte(origState[1],2)]),
tableInv2[getByte(origState[4],1)]),
tableInv3[getByte(origState[3],0)])
dstState[3] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[3],3)],
tableInv1[getByte(origState[2],2)]),
tableInv2[getByte(origState[1],1)]),
tableInv3[getByte(origState[4],0)])
dstState[4] = bit.bxor(bit.bxor(bit.bxor(
tableInv0[getByte(origState[4],3)],
tableInv1[getByte(origState[3],2)]),
tableInv2[getByte(origState[2],1)]),
tableInv3[getByte(origState[1],0)])
end
--
-- do last decryption round
--
local function doInvLastRound(origState, dstState)
dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
+ putByte(iSBox[getByte(origState[4],2)], 2)
+ putByte(iSBox[getByte(origState[3],1)], 1)
+ putByte(iSBox[getByte(origState[2],0)], 0)
dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
+ putByte(iSBox[getByte(origState[1],2)], 2)
+ putByte(iSBox[getByte(origState[4],1)], 1)
+ putByte(iSBox[getByte(origState[3],0)], 0)
dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
+ putByte(iSBox[getByte(origState[2],2)], 2)
+ putByte(iSBox[getByte(origState[1],1)], 1)
+ putByte(iSBox[getByte(origState[4],0)], 0)
dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
+ putByte(iSBox[getByte(origState[3],2)], 2)
+ putByte(iSBox[getByte(origState[2],1)], 1)
+ putByte(iSBox[getByte(origState[1],0)], 0)
end
--
-- encrypts 16 Bytes
-- key encryption key schedule
-- input array with input data
-- inputOffset start index for input
-- output array for encrypted data
-- outputOffset start index for output
--
local function encrypt(key, input, inputOffset, output, outputOffset)
--default parameters
inputOffset = inputOffset or 1
output = output or {}
outputOffset = outputOffset or 1
local state = {}
local tmpState = {}
if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
return
end
state = util.bytesToInts(input, inputOffset, 4)
addRoundKey(state, key, 0)
local round = 1
while (round < key[ROUNDS] - 1) do
-- do a double round to save temporary assignments
doRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round + 1
doRound(tmpState, state)
addRoundKey(state, key, round)
round = round + 1
end
doRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round +1
doLastRound(tmpState, state)
addRoundKey(state, key, round)
util.sleepCheckIn()
return util.intsToBytes(state, output, outputOffset)
end
--
-- decrypt 16 bytes
-- key decryption key schedule
-- input array with input data
-- inputOffset start index for input
-- output array for decrypted data
-- outputOffset start index for output
---
local function decrypt(key, input, inputOffset, output, outputOffset)
-- default arguments
inputOffset = inputOffset or 1
output = output or {}
outputOffset = outputOffset or 1
local state = {}
local tmpState = {}
if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
error("No decryption key: " .. tostring(key[KEY_TYPE]))
return
end
state = util.bytesToInts(input, inputOffset, 4)
addRoundKey(state, key, key[ROUNDS])
local round = key[ROUNDS] - 1
while (round > 2) do
-- do a double round to save temporary assignments
doInvRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round - 1
doInvRound(tmpState, state)
addRoundKey(state, key, round)
round = round - 1
end
doInvRound(state, tmpState)
addRoundKey(tmpState, key, round)
round = round - 1
doInvLastRound(tmpState, state)
addRoundKey(state, key, round)
util.sleepCheckIn()
return util.intsToBytes(state, output, outputOffset)
end
-- calculate all tables when loading this file
calcSBox()
calcRoundTables()
calcInvRoundTables()
return {
ROUNDS = ROUNDS,
KEY_TYPE = KEY_TYPE,
ENCRYPTION_KEY = ENCRYPTION_KEY,
DECRYPTION_KEY = DECRYPTION_KEY,
expandEncryptionKey = expandEncryptionKey,
expandDecryptionKey = expandDecryptionKey,
encrypt = encrypt,
decrypt = decrypt,
}
end)
local buffer=_W(function(_ENV, ...)
local function new ()
return {}
end
local function addString (stack, s)
table.insert(stack, s)
end
local function toString (stack)
return table.concat(stack)
end
return {
new = new,
addString = addString,
toString = toString,
}
end)
ciphermode=_W(function(_ENV, ...)
local public = {}
--
-- Encrypt strings
-- key - byte array with key
-- string - string to encrypt
-- modefunction - function for cipher mode to use
--
local random = math.random
function public.encryptString(key, data, modeFunction, iv)
if iv then
local ivCopy = {}
for i = 1, 16 do ivCopy[i] = iv[i] end
iv = ivCopy
else
iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
end
local keySched = aes.expandEncryptionKey(key)
local encryptedData = buffer.new()
for i = 1, #data/16 do
local offset = (i-1)*16 + 1
local byteData = {string.byte(data,offset,offset +15)}
iv = modeFunction(keySched, byteData, iv)
buffer.addString(encryptedData, string.char(unpack(byteData)))
end
return buffer.toString(encryptedData)
end
--
-- the following 4 functions can be used as
-- modefunction for encryptString
--
-- Electronic code book mode encrypt function
function public.encryptECB(keySched, byteData, iv)
aes.encrypt(keySched, byteData, 1, byteData, 1)
end
-- Cipher block chaining mode encrypt function
function public.encryptCBC(keySched, byteData, iv)
util.xorIV(byteData, iv)
aes.encrypt(keySched, byteData, 1, byteData, 1)
return byteData
end
-- Output feedback mode encrypt function
function public.encryptOFB(keySched, byteData, iv)
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return iv
end
-- Cipher feedback mode encrypt function
function public.encryptCFB(keySched, byteData, iv)
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return byteData
end
function public.encryptCTR(keySched, byteData, iv)
local nextIV = {}
for j = 1, 16 do nextIV[j] = iv[j] end
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
util.increment(nextIV)
return nextIV
end
--
-- Decrypt strings
-- key - byte array with key
-- string - string to decrypt
-- modefunction - function for cipher mode to use
--
function public.decryptString(key, data, modeFunction, iv)
if iv then
local ivCopy = {}
for i = 1, 16 do ivCopy[i] = iv[i] end
iv = ivCopy
else
iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
end
local keySched
if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
keySched = aes.expandEncryptionKey(key)
else
keySched = aes.expandDecryptionKey(key)
end
local decryptedData = buffer.new()
for i = 1, #data/16 do
local offset = (i-1)*16 + 1
local byteData = {string.byte(data,offset,offset +15)}
iv = modeFunction(keySched, byteData, iv)
buffer.addString(decryptedData, string.char(unpack(byteData)))
end
return buffer.toString(decryptedData)
end
--
-- the following 4 functions can be used as
-- modefunction for decryptString
--
-- Electronic code book mode decrypt function
function public.decryptECB(keySched, byteData, iv)
aes.decrypt(keySched, byteData, 1, byteData, 1)
return iv
end
-- Cipher block chaining mode decrypt function
function public.decryptCBC(keySched, byteData, iv)
local nextIV = {}
for j = 1, 16 do nextIV[j] = byteData[j] end
aes.decrypt(keySched, byteData, 1, byteData, 1)
util.xorIV(byteData, iv)
return nextIV
end
-- Output feedback mode decrypt function
function public.decryptOFB(keySched, byteData, iv)
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return iv
end
-- Cipher feedback mode decrypt function
function public.decryptCFB(keySched, byteData, iv)
local nextIV = {}
for j = 1, 16 do nextIV[j] = byteData[j] end
aes.encrypt(keySched, iv, 1, iv, 1)
util.xorIV(byteData, iv)
return nextIV
end
public.decryptCTR = public.encryptCTR
return public
end)
-- Simple API for encrypting strings.
--
AES128 = 16
AES192 = 24
AES256 = 32
ECBMODE = 1
CBCMODE = 2
OFBMODE = 3
CFBMODE = 4
CTRMODE = 4
local function pwToKey(password, keyLength, iv)
local padLength = keyLength
if (keyLength == AES192) then
padLength = 32
end
if (padLength > #password) then
local postfix = ""
for i = 1,padLength - #password do
postfix = postfix .. string.char(0)
end
password = password .. postfix
else
password = string.sub(password, 1, padLength)
end
local pwBytes = {string.byte(password,1,#password)}
password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
password = string.sub(password, 1, keyLength)
return {string.byte(password,1,#password)}
end
--
-- Encrypts string data with password password.
-- password - the encryption key is generated from this string
-- data - string to encrypt (must not be too large)
-- keyLength - length of aes key: 128(default), 192 or 256 Bit
-- mode - mode of encryption: ecb, cbc(default), ofb, cfb
--
-- mode and keyLength must be the same for encryption and decryption.
--
function encrypt(password, data, keyLength, mode, iv)
assert(password ~= nil, "Empty password.")
assert(password ~= nil, "Empty data.")
local mode = mode or CBCMODE
local keyLength = keyLength or AES128
local key = pwToKey(password, keyLength, iv)
local paddedData = util.padByteString(data)
if mode == ECBMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
elseif mode == CBCMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
elseif mode == OFBMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
elseif mode == CFBMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
elseif mode == CTRMODE then
return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
else
error("Unknown mode", 2)
end
end
--
-- Decrypts string data with password password.
-- password - the decryption key is generated from this string
-- data - string to encrypt
-- keyLength - length of aes key: 128(default), 192 or 256 Bit
-- mode - mode of decryption: ecb, cbc(default), ofb, cfb
--
-- mode and keyLength must be the same for encryption and decryption.
--
function decrypt(password, data, keyLength, mode, iv)
local mode = mode or CBCMODE
local keyLength = keyLength or AES128
local key = pwToKey(password, keyLength, iv)
local plain
if mode == ECBMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
elseif mode == CBCMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
elseif mode == OFBMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
elseif mode == CFBMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
elseif mode == CTRMODE then
plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
else
error("Unknown mode", 2)
end
result = util.unpadByteString(plain)
if (result == nil) then
return nil
end
return result
end