ComputerCraft Archive

aeslua

computer utility LDDestroier github

Description

A collection of all my ComputerCraft programs and the APIs they use. This is mostly just to get them the fuck off of pastebin, and also to ensure that API owners don't change things to break my precious programs...!

Installation

Copy one of these commands into your ComputerCraft terminal:

wget:wget https://raw.githubusercontent.com/LDDestroier/CC/master/API/aeslua.lua aeslua
Archive:wget https://cc.shobie.xyz/cc/get/gh-LDDestroier-CC-api-aeslua aeslua
Quick Install: wget https://cc.shobie.xyz/cc/get/gh-LDDestroier-CC-api-aeslua aeslua

Usage

Run: aeslua

Tags

none

Source

View Original Source

Code Preview

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