XXTEA Encryption in Lua

Hi all,

I have just been told that in order for our Corona game to connect to the required web services we would need to implement the XXTEA encryption algorithm in Lua. I’m quite new with Lua and have not had much experience with encryption earlier so I am a little lost here. As I haven’t found much online I was wondering if any of you talented people knew something about XXTEA encrypting AND Lua and fancied a challenge? :slight_smile:

I have posted the actionscript 3 code that we use for our flash games below so if any of you have a similar code for Lua, please do not hesitate to share it here.

Any help appreciated! :slight_smile:

Many thanks.

/\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\ | | | The implementation of PHPRPC Protocol 3.0 | | | | XXTEA.as | | | | Release 3.0.0 | | Copyright (c) 2005-2007 by Team-PHPRPC | | | | WebSite: http://www.phprpc.org/ | | http://www.phprpc.net/ | | http://www.phprpc.com/ | | http://sourceforge.net/projects/php-rpc/ | | | | Authors: Ma Bingyao <andot> |<br>| |<br>| This file may be distributed and/or modified under the |<br>| terms of the GNU Lesser General Public License (LGPL) |<br>| version 3.0 as published by the Free Software Foundation |<br>| and appearing in the included file LICENSE. |<br>| |<br>\ ********************************************************** /<br>/* XXTEA encryption arithmetic library.<br> *<br> * Copyright (C) 2006-2007 Ma Bingyao <andot><br> * Version: 1.7<br> * LastModified: Nov 5, 2007<br> * This library is free. You can redistribute it and/or modify it.<br> */<br> <br>package org.phprpc.util {<br> import flash.utils.ByteArray;<br> import flash.utils.Endian;<br> public class XXTEA {<br> private static const delta:uint = uint(0x9E3779B9);<br> private static function LongArrayToByteArray(data:Array, includeLength:Boolean):ByteArray {<br> var length:uint = data.length;<br> var n:uint = (length - 1) &lt;&lt; 2;<br> if (includeLength) {<br> var m:uint = data[length - 1];<br> if ((m &lt; n - 3) || (m &gt; n)) {<br> return null;<br> }<br> n = m;<br> }<br> var result:ByteArray = new ByteArray();<br> result.endian = Endian.LITTLE_ENDIAN;<br> for (var i:uint = 0; i &lt; length; i++) {<br> result.writeUnsignedInt(data[i]);<br> }<br> if (includeLength) {<br> result.length = n;<br> return result;<br> }<br> else {<br> return result;<br> }<br> }<br> private static function ByteArrayToLongArray(data:ByteArray, includeLength:Boolean):Array {<br> var length:uint = data.length;<br> var n:uint = length &gt;&gt; 2;<br> if (length % 4 &gt; 0) {<br> n++;<br> data.length += (4 - (length % 4));<br> }<br> data.endian = Endian.LITTLE_ENDIAN;<br> data.position = 0;<br> var result:Array = [];<br> for (var i:uint = 0; i &lt; n; i++) {<br> result[i] = data.readUnsignedInt();<br> }<br> if (includeLength) {<br> result[n] = length;<br> }<br> data.length = length;<br> return result;<br> }<br> public static function encrypt(data:ByteArray, key:ByteArray):ByteArray {<br> if (data.length == 0) {<br> return new ByteArray();<br> }<br> var v:Array = ByteArrayToLongArray(data, true);<br> var k:Array = ByteArrayToLongArray(key, false);<br> if (k.length &lt; 4) {<br> k.length = 4;<br> }<br> var n:uint = v.length - 1;<br> var z:uint = v[n];<br> var y:uint = v[0];<br> var mx:uint;<br> var e:uint;<br> var p:uint;<br> var q:uint = uint(6 + 52 / (n + 1));<br> var sum:uint = 0;<br> while (0 &lt; q--) {<br> sum = sum + delta;<br> e = sum &gt;&gt;&gt; 2 &amp; 3;<br> for (p = 0; p &lt; n; p++) {<br> y = v[p + 1];<br> mx = (z &gt;&gt;&gt; 5 ^ y &lt;&lt; 2) + (y &gt;&gt;&gt; 3 ^ z &lt;&lt; 4) ^ (sum ^ y) + (k[p &amp; 3 ^ e] ^ z);<br> z = v[p] = v[p] + mx;<br> }<br> y = v[0];<br> mx = (z &gt;&gt;&gt; 5 ^ y &lt;&lt; 2) + (y &gt;&gt;&gt; 3 ^ z &lt;&lt; 4) ^ (sum ^ y) + (k[p &amp; 3 ^ e] ^ z);<br> z = v[n] = v[n] + mx;<br> }<br> return LongArrayToByteArray(v, false);<br> }<br> public static function decrypt(data:ByteArray, key:ByteArray):ByteArray {<br> if (data.length == 0) {<br> return new ByteArray();<br> }<br> var v:Array = ByteArrayToLongArray(data, false);<br> var k:Array = ByteArrayToLongArray(key, false);<br> if (k.length &lt; 4) {<br> k.length = 4;<br> }<br> var n:uint = v.length - 1;<br> var z:uint = v[n - 1];<br> var y:uint = v[0];<br> var mx:uint;<br> var e:uint;<br> var p:uint;<br> var q:uint = uint(6 + 52 / (n + 1));<br> var sum:uint = q * delta;<br> while (sum != 0) {<br> e = sum &gt;&gt;&gt; 2 &amp; 3;<br> for (p = n; p &gt; 0; p--) {<br> z = v[p - 1];<br> mx = (z &gt;&gt;&gt; 5 ^ y &lt;&lt; 2) + (y &gt;&gt;&gt; 3 ^ z &lt;&lt; 4) ^ (sum ^ y) + (k[p &amp; 3 ^ e] ^ z);<br> y = v[p] = v[p] - mx;<br> }<br> z = v[n];<br> mx = (z &gt;&gt;&gt; 5 ^ y &lt;&lt; 2) + (y &gt;&gt;&gt; 3 ^ z &lt;&lt; 4) ^ (sum ^ y) + (k[p &amp; 3 ^ e] ^ z);<br> y = v[0] = v[0] - mx;<br> sum = sum - delta;<br> }<br> return LongArrayToByteArray(v, true);<br> }<br> }<br>}<br> [import]uid: 18203 topic_id: 6291 reply_id: 306291[/import]

Hi,

Does anyone have any leads concerning the above post? It would help me loads!

Many thanks.

[import]uid: 18203 topic_id: 6291 reply_id: 23194[/import]

Hi all,

A very talented colleague of mine has now come up with a lua script that handles the XXTEA encryption algorithm and it works a treat for us! I thought that I could post the code here in case someone else has a similar need. I hope it will help someone as much as it helped us.

There are three files needed (code below):

  • xxtea.lua
  • bit.lua
  • hex.lua
    xxtea.lua:
---------------------------------------------------------------------  
-- Author: Jørn Skaarud Karlsen  
---------------------------------------------------------------------  
  
require("bit")  
require("hex")  
  
---------------------------------------------------------------------  
-- Constants  
---------------------------------------------------------------------  
  
delta = 0x9E3779B9  
  
---------------------------------------------------------------------  
-- Conversion  
---------------------------------------------------------------------  
  
function convertStringToBytes(str)  
 local bytes = {}  
 local strLength = string.len(str)  
 for i=1,strLength do  
 table.insert(bytes, string.byte(str, i))  
 end  
  
 return bytes  
end  
  
function convertBytesToString(bytes)  
 local bytesLength = table.getn(bytes)  
 local str = ""  
 for i=1,bytesLength do  
 str = str .. string.char(bytes[i])  
 end  
  
 return str  
end  
  
function convertHexStringToBytes(str)  
 local bytes = {}  
 local strLength = string.len(str)  
 for k=2,strLength,2 do  
 local hexString = "0x" .. string.sub(str, (k - 1), k)  
 table.insert(bytes, hex.to\_dec(hexString))  
 end  
  
 return bytes  
end  
  
function convertBytesToHexString(bytes)  
 local str = ""  
 local bytesLength = table.getn(bytes)  
 for i=1,bytesLength do  
 local hexString = string.sub(hex.to\_hex(bytes[i]), 3)  
 if string.len(hexString) == 1 then  
 hexString = "0" .. hexString  
 end  
 str = str .. hexString  
 end  
  
 return str  
end  
  
function convertBytesToUIntArray(bytes, includeLength)  
 local bytesLength = table.getn(bytes)  
 local result = {}  
  
 if includeLength then  
 local n = bit.brshift(bytesLength, 2) + 1  
 if bit.band(bytesLength, 3) ~= 0 then  
 n = n + 1  
 end  
  
 result[n] = bytesLength;  
 end  
  
 for i=0,(bytesLength - 1) do  
 local resultIndex = bit.brshift(i, 2) + 1  
 if result[resultIndex] == nil then  
 result[resultIndex] = 0  
 end  
  
 local resultValue = bit.blshift(bit.band(0x000000ff, bytes[i+1]), bit.blshift(bit.band(i, 3), 3))  
 result[resultIndex] = bit.bor(result[resultIndex], resultValue);  
 end  
  
 return result  
end  
  
function convertUIntArrayToBytes(data, includeLength)  
 local dataLength = table.getn(data)  
 local n = bit.blshift(dataLength, 2)  
 local result = {}  
  
 if includeLength then  
 local m = data[dataLength]  
 if m \> n then  
 return nil  
 end  
  
 n = m  
 end  
  
 for i=0,(n-1) do  
 local value = bit.band(bit.brshift(data[bit.brshift(i, 2) + 1], (bit.blshift(bit.band(i, 3), 3))), 0xff)  
 table.insert(result, value)  
 end  
  
 return result  
end  
  
function convertToUInt32(value)  
 if value \< 0 then  
 local absValue = math.abs(value)  
 local a = math.floor(absValue / 0xFFFFFFFF)  
 local b = value + a \* 0xFFFFFFFF  
 local c = 0xFFFFFFFF + b + 1  
 return c  
 end  
  
 return math.mod(value, 0xFFFFFFFF) - math.floor(value / 0xFFFFFFFF)  
end  
  
---------------------------------------------------------------------  
-- Encryption/decryption common  
---------------------------------------------------------------------  
  
function mx(sum, y, z, p, e, k)  
 local aa = bit.brshift(z, 5)  
 local ab = convertToUInt32(bit.blshift(y, 2))  
 local ac = bit.bxor(aa, ab)  
  
 local ba = bit.brshift(y, 3)  
 local bb = convertToUInt32(bit.blshift(z, 4))  
 local bc = bit.bxor(ba, bb)  
 local ca = bit.bxor(sum, y)  
  
 local dia = bit.band(p, 3)  
 local dib = bit.bxor(dia, e)  
 local da = k[dib + 1]  
 local db = bit.bxor(da, z)  
  
 local ea = convertToUInt32(ca + db)  
 local fa = convertToUInt32(ac + bc)  
 local ga = bit.bxor(fa, ea)  
  
 return ga  
end  
  
---------------------------------------------------------------------  
-- Decryption  
---------------------------------------------------------------------  
  
function decryptIntArray(v, k)  
 local n = table.getn(v)  
 local z = v[n]  
 local y = v[1]  
 local e = 0  
 local p = 0  
  
 local q = 6 + math.floor(52 / n)  
 local sum = convertToUInt32(q \* delta)  
 while sum ~= 0 do  
 e = bit.band(bit.brshift(sum, 2), 3)  
 for p=n,2,-1 do  
 z = v[p - 1]  
 v[p] = convertToUInt32(v[p] - mx(sum, y, z, (p-1), e, k))  
 y = v[p]  
 end  
  
 z = v[n]  
 v[1] = convertToUInt32(v[1] - mx(sum, y, z, p, e, k))  
 y = v[1]  
  
 local sumBefore = sum  
 sum = convertToUInt32(sum - delta)  
 end  
  
 return v  
end  
  
function decrypt(data, key)  
 local dataLength = string.len(data)  
 local keyLength = string.len(key)  
  
 if keyLength == 0 then  
 return data  
 end  
  
 local keyBytes = convertStringToBytes(key)  
 local encryptedBytes = convertHexStringToBytes(data)  
  
 local dataIntArray = convertBytesToUIntArray(encryptedBytes, false)  
 local keyIntArray = convertBytesToUIntArray(keyBytes, false)  
  
 local decryptedIntArray = decryptIntArray(dataIntArray, keyIntArray)  
 local decryptedBytes = convertUIntArrayToBytes(decryptedIntArray, true)  
 local decryptedString = convertBytesToString(decryptedBytes)  
  
 return decryptedString  
end  
  
---------------------------------------------------------------------  
-- Encryption  
---------------------------------------------------------------------  
  
function encryptIntArray(v, k)  
  
 n = table.getn(v)  
 if n \< 2 then  
 return v  
 end  
  
 local z = v[n]  
 local y = v[1]  
 local sum = 0  
 local e = 0  
 local p = 0  
 local initQ = 6 + math.floor(52 / n)  
  
 for q=initQ,1,-1 do  
 sum = convertToUInt32(sum + delta);  
 e = bit.band(bit.brshift(sum, 2), 3);  
 for p=1,(n-1) do  
 y = v[p + 1];  
 v[p] = convertToUInt32(v[p] + mx(sum, y, z, (p-1), e, k));  
 z = v[p]  
 end  
 y = v[1];  
 v[n] = convertToUInt32(v[n] + mx(sum, y, z, (n-1), e, k));  
 z = v[n]  
 end  
  
 return v;  
  
end  
  
function encrypt(data, key)  
 local dataLength = string.len(data)  
 local keyLength = string.len(key)  
  
 if (keyLength == 0) then  
 return data  
 end  
  
 local dataBytes = convertStringToBytes(data)  
 local keyBytes = convertStringToBytes(key)  
  
 local dataIntArray = convertBytesToUIntArray(dataBytes, true)  
 local keyIntArray = convertBytesToUIntArray(keyBytes, false)  
  
 local encryptedIntArray = encryptIntArray(dataIntArray, keyIntArray)  
 local encryptedBytes = convertUIntArrayToBytes(encryptedIntArray, false)  
 local encryptedString = convertBytesToHexString(encryptedBytes)  
  
 return encryptedString  
end  
  
---------------------------------------------------------------------  
-- Program: replace  
---------------------------------------------------------------------  
  
unencryptedInput = "This is a test"  
encryptionKeyInput = "xxxxxxxxxxxxxxxxxxxx" -- Place your encryption key here  
  
encryptedOutput = encrypt(unencryptedInput, encryptionKeyInput)  
print(encryptedOutput)  
  
decryptedEncryptedOutput = decrypt(encryptedOutput, encryptionKeyInput)  
print(decryptedEncryptedOutput)  
  

bit.lua:

--[[---------------  
LuaBit v0.4  
-------------------  
a bitwise operation lib for lua.  
  
http://luaforge.net/projects/bit/  
  
How to use:  
-------------------  
 bit.bnot(n) -- bitwise not (~n)  
 bit.band(m, n) -- bitwise and (m & n)  
 bit.bor(m, n) -- bitwise or (m | n)  
 bit.bxor(m, n) -- bitwise xor (m ^ n)  
 bit.brshift(n, bits) -- right shift (n \>\> bits)  
 bit.blshift(n, bits) -- left shift (n \<\< bits)  
 bit.blogic\_rshift(n, bits) -- logic right shift(zero fill \>\>\>)  
   
Please note that bit.brshift and bit.blshift only support number within  
32 bits.  
  
2 utility functions are provided too:  
 bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence)  
 -- high bits first  
 bit.tonumb(bit\_tbl) -- convert a bit table into a number   
-------------------  
  
Under the MIT license.  
  
copyright(c) 2006~2007 hanzhao (abrash\_han@hotmail.com)  
--]]---------------  
  
do  
  
------------------------  
-- bit lib implementions  
  
local function check\_int(n)  
 -- checking not float  
 if(n - math.floor(n) \> 0) then  
 error("trying to use bitwise operation on non-integer!")  
 end  
end  
  
local function to\_bits(n)  
 check\_int(n)  
 if(n \< 0) then  
 -- negative  
 return to\_bits(bit.bnot(math.abs(n)) + 1)  
 end  
 -- to bits table  
 local tbl = {}  
 local cnt = 1  
 while (n \> 0) do  
 local last = math.mod(n,2)  
 if(last == 1) then  
 tbl[cnt] = 1  
 else  
 tbl[cnt] = 0  
 end  
 n = (n-last)/2  
 cnt = cnt + 1  
 end  
  
 return tbl  
end  
  
local function tbl\_to\_number(tbl)  
 local n = table.getn(tbl)  
  
 local rslt = 0  
 local power = 1  
 for i = 1, n do  
 rslt = rslt + tbl[i]\*power  
 power = power\*2  
 end  
   
 return rslt  
end  
  
local function expand(tbl\_m, tbl\_n)  
 local big = {}  
 local small = {}  
 if(table.getn(tbl\_m) \> table.getn(tbl\_n)) then  
 big = tbl\_m  
 small = tbl\_n  
 else  
 big = tbl\_n  
 small = tbl\_m  
 end  
 -- expand small  
 for i = table.getn(small) + 1, table.getn(big) do  
 small[i] = 0  
 end  
  
end  
  
local function bit\_or(m, n)  
 local tbl\_m = to\_bits(m)  
 local tbl\_n = to\_bits(n)  
 expand(tbl\_m, tbl\_n)  
  
 local tbl = {}  
 local rslt = math.max(table.getn(tbl\_m), table.getn(tbl\_n))  
 for i = 1, rslt do  
 if(tbl\_m[i]== 0 and tbl\_n[i] == 0) then  
 tbl[i] = 0  
 else  
 tbl[i] = 1  
 end  
 end  
   
 return tbl\_to\_number(tbl)  
end  
  
local function bit\_and(m, n)  
 local tbl\_m = to\_bits(m)  
 local tbl\_n = to\_bits(n)  
 expand(tbl\_m, tbl\_n)   
  
 local tbl = {}  
 local rslt = math.max(table.getn(tbl\_m), table.getn(tbl\_n))  
 for i = 1, rslt do  
 if(tbl\_m[i]== 0 or tbl\_n[i] == 0) then  
 tbl[i] = 0  
 else  
 tbl[i] = 1  
 end  
 end  
  
 return tbl\_to\_number(tbl)  
end  
  
local function bit\_not(n)  
   
 local tbl = to\_bits(n)  
 local size = math.max(table.getn(tbl), 32)  
 for i = 1, size do  
 if(tbl[i] == 1) then   
 tbl[i] = 0  
 else  
 tbl[i] = 1  
 end  
 end  
 return tbl\_to\_number(tbl)  
end  
  
local function bit\_xor(m, n)  
 local tbl\_m = to\_bits(m)  
 local tbl\_n = to\_bits(n)  
 expand(tbl\_m, tbl\_n)   
  
 local tbl = {}  
 local rslt = math.max(table.getn(tbl\_m), table.getn(tbl\_n))  
 for i = 1, rslt do  
 if(tbl\_m[i] ~= tbl\_n[i]) then  
 tbl[i] = 1  
 else  
 tbl[i] = 0  
 end  
 end  
   
 --table.foreach(tbl, print)  
  
 return tbl\_to\_number(tbl)  
end  
  
local function bit\_rshift(n, bits)  
 check\_int(n)  
   
 local high\_bit = 0  
 if(n \< 0) then  
 -- negative  
 n = bit\_not(math.abs(n)) + 1  
 high\_bit = 2147483648 -- 0x80000000  
 end  
  
 for i=1, bits do  
 n = n/2  
 n = bit\_or(math.floor(n), high\_bit)  
 end  
 return math.floor(n)  
end  
  
-- logic rightshift assures zero filling shift  
local function bit\_logic\_rshift(n, bits)  
 check\_int(n)  
 if(n \< 0) then  
 -- negative  
 n = bit\_not(math.abs(n)) + 1  
 end  
 for i=1, bits do  
 n = n/2  
 end  
 return math.floor(n)  
end  
  
local function bit\_lshift(n, bits)  
 check\_int(n)  
   
 if(n \< 0) then  
 -- negative  
 n = bit\_not(math.abs(n)) + 1  
 end  
  
 for i=1, bits do  
 n = n\*2  
 end  
 return bit\_and(n, 4294967295) -- 0xFFFFFFFF  
end  
  
local function bit\_xor2(m, n)  
 local rhs = bit\_or(bit\_not(m), bit\_not(n))  
 local lhs = bit\_or(m, n)  
 local rslt = bit\_and(lhs, rhs)  
 return rslt  
end  
  
--------------------  
-- bit lib interface  
  
bit = {  
 -- bit operations  
 bnot = bit\_not,  
 band = bit\_and,  
 bor = bit\_or,  
 bxor = bit\_xor,  
 brshift = bit\_rshift,  
 blshift = bit\_lshift,  
 bxor2 = bit\_xor2,  
 blogic\_rshift = bit\_logic\_rshift,  
  
 -- utility func  
 tobits = to\_bits,  
 tonumb = tbl\_to\_number,  
}  
  
end  
  
--[[  
for i = 1, 100 do  
 for j = 1, 100 do  
 if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then  
 error("bit.xor failed.")  
 end  
 end  
end  
--]]  

hex.lua:

--[[---------------  
Hex v0.4  
-------------------  
Hex conversion lib for lua.  
  
How to use:  
 hex.to\_hex(n) -- convert a number to a hex string  
 hex.to\_dec(hex) -- convert a hex string(prefix with '0x' or '0X') to number  
   
Part of LuaBit(http://luaforge.net/projects/bit/).  
  
Under the MIT license.  
  
copyright(c) 2006~2007 hanzhao (abrash\_han@hotmail.com)  
--]]---------------  
  
require 'bit'  
  
do   
  
local function to\_hex(n)  
 if(type(n) ~= "number") then  
 error("non-number type passed in.")  
 end  
  
 -- checking not float  
 if(n - math.floor(n) \> 0) then  
 error("trying to apply bitwise operation on non-integer!")  
 end  
  
 if(n \< 0) then  
 -- negative  
 n = bit.tobits(bit.bnot(math.abs(n)) + 1)  
 n = bit.tonumb(n)  
 end  
  
 hex\_tbl = {'A', 'B', 'C', 'D', 'E', 'F'}  
 hex\_str = ""  
  
 while(n ~= 0) do  
 last = math.mod(n, 16)  
 if(last \< 10) then  
 hex\_str = tostring(last) .. hex\_str  
 else  
 hex\_str = hex\_tbl[last-10+1] .. hex\_str  
 end  
 n = math.floor(n/16)  
 end  
 if(hex\_str == "") then  
 hex\_str = "0"  
 end  
 return "0x" .. hex\_str  
end  
  
local function to\_dec(hex)  
 if(type(hex) ~= "string") then  
 error("non-string type passed in.")  
 end  
  
 head = string.sub(hex, 1, 2)  
   
 if( head ~= "0x" and head ~= "0X") then  
 error("wrong hex format, should lead by 0x or 0X.")  
 end  
  
 v = tonumber(string.sub(hex, 3), 16)  
  
 return v;  
end  
  
--------------------  
-- hex lib interface  
hex = {  
 to\_dec = to\_dec,  
 to\_hex = to\_hex,  
}  
  
end  
  
--[[  
-- test  
d = 4341688  
h = to\_hex(d)  
print(h)  
print(to\_dec(h))  
for i = 1, 100000 do  
 h = hex.to\_hex(i)  
 d = hex.to\_dec(h)  
 if(d ~= i) then   
 error("failed " .. i .. ", " .. h)  
 end  
end  
--]]  

[import]uid: 18203 topic_id: 6291 reply_id: 24664[/import]

nice, thanks for sharing! [import]uid: 6645 topic_id: 6291 reply_id: 24772[/import]

hi,

i’m tryng to mime encode an image to send to facebook graph api. any idea how this would be achieved? was wondering if it needs byte arrays etc like this does

thanks
j [import]uid: 6645 topic_id: 6291 reply_id: 29094[/import]