ZUtility is a personal collection of programming concepts that I have found myself re-writing over the years and have now an ambition to create a growing library that can be used where needed in future projects.
The Z in ZUtility stands for Zelenka; my last name.
The Z in ZUtility stands for Zelenka; my last name.
The repository contains both C# generic classes as well as Unity specific classes.
Encryption
The encryption I have gone for here is the XTEA (eXtended Tiny Encryption Algorithm) it is a high efficient symmetric block cipher and is a proven way of encrypting data both for transferring data over networks as well as storing sensitive data locally.
private static uint[] FormatKey(string key)
{
if (key.Length == 0)
{
throw new ArgumentException("Key must be between 1 and 16 characters in length");
}
{
if (key.Length == 0)
{
throw new ArgumentException("Key must be between 1 and 16 characters in length");
}
key = key.PadRight(16,' ').Substring(0, 16);
uint[] formattedKey = new uint[4];
int j = 0;
uint[] formattedKey = new uint[4];
int j = 0;
for (int i = 0; i < key.Length; i += 4)
{
formattedKey[j++] = ConvertStringToUInt(key.Substring(i, 4));
}
{
formattedKey[j++] = ConvertStringToUInt(key.Substring(i, 4));
}
return formattedKey;
}
}
public static uint ConvertStringToUInt(string input)
{
uint output;
output = ((uint)input[0]);
output += ((uint)input[1] << 8);
output += ((uint)input[2] << 16);
output += ((uint)input[3] << 24);
return output;
}
{
uint output;
output = ((uint)input[0]);
output += ((uint)input[1] << 8);
output += ((uint)input[2] << 16);
output += ((uint)input[3] << 24);
return output;
}
Formatting keys
The key format has a strict character count of 16 characters so we start by making sure there's at least 1 character and add empty spaces to any empty characters or if it's longer than 16 characters every character after the 16th are discarded.
The reason for it to be 16 characters is to be able to be divided into sets of 4 characters which fits into a uint. We then create an array of 4 uints where we can store the key.
Then iterate through each key and convert it to uint by assigning. every 4 character to 4 separate bits of the 32-bit uint.
The reason for it to be 16 characters is to be able to be divided into sets of 4 characters which fits into a uint. We then create an array of 4 uints where we can store the key.
Then iterate through each key and convert it to uint by assigning. every 4 character to 4 separate bits of the 32-bit uint.
Encryption
Once we have the formatted key we want to ensure the data has a length of even characters and add an extra character if it's not even. The reason we want an even number of characters in our data is cause we process the characters in pairs for the encryption.
Once ensured the character length is even we convert the string to bytes to conveniently bit shift its values in the coding.
once the cipher and a tempdata holder has been declared we start iterating over the characters in pairs and assign the data values in turn to the temp values.
public static string Encrypt(string data, string key)
{
uint[] formattedKey = FormatKey(key);
{
uint[] formattedKey = FormatKey(key);
if(data.Length % 2 != 0)
{
data += '\0';
}
byte[] dataBytes = ASCIIEncoding.ASCII.GetBytes(data);
{
data += '\0';
}
byte[] dataBytes = ASCIIEncoding.ASCII.GetBytes(data);
string cipher = string.Empty;
uint[] tempData = new uint[2];
for(int i = 0; i < dataBytes.Length; i += 2)
{
tempData[0] = dataBytes[i];
tempData[1] = dataBytes[i+1];
Code(tempData, formattedKey);
cipher += ConvertUIntToString(tempData[0]) + ConvertUIntToString(tempData[1]);
}
uint[] tempData = new uint[2];
for(int i = 0; i < dataBytes.Length; i += 2)
{
tempData[0] = dataBytes[i];
tempData[1] = dataBytes[i+1];
Code(tempData, formattedKey);
cipher += ConvertUIntToString(tempData[0]) + ConvertUIntToString(tempData[1]);
}
return cipher;
}
}
private static void Code(uint[] value, uint[] key)
{
uint firstValue = value[0];
uint secondValue = value[1];
uint sum = 0;
uint delta = 0x9e3779b9;
uint n = 32;
{
uint firstValue = value[0];
uint secondValue = value[1];
uint sum = 0;
uint delta = 0x9e3779b9;
uint n = 32;
while(n-- > 0)
{
firstValue += (secondValue << 4 ^ secondValue >> 5) + secondValue ^ sum + key[sum & 3];
sum += delta;
secondValue += (firstValue << 4 ^ firstValue >> 5) + firstValue ^ sum + key[sum >> 11 & 3];
}
{
firstValue += (secondValue << 4 ^ secondValue >> 5) + secondValue ^ sum + key[sum & 3];
sum += delta;
secondValue += (firstValue << 4 ^ firstValue >> 5) + firstValue ^ sum + key[sum >> 11 & 3];
}
value[0] = firstValue;
value[1] = secondValue;
}
value[1] = secondValue;
}
private static void Decode(uint[] value, uint[] key)
{
uint firstValue = value[0];
uint secondValue = value[1];
uint sum;
uint delta = 0x9e3779b9;
uint n = 32;
{
uint firstValue = value[0];
uint secondValue = value[1];
uint sum;
uint delta = 0x9e3779b9;
uint n = 32;
sum = delta << 5 ;
while(n-- > 0)
{
secondValue -= (firstValue << 4 ^ firstValue >> 5) + firstValue ^ sum + key[sum >> 11 & 3];
sum -= delta;
firstValue -= (secondValue << 4 ^ secondValue >> 5) + secondValue ^ sum + key[sum & 3];
}
{
secondValue -= (firstValue << 4 ^ firstValue >> 5) + firstValue ^ sum + key[sum >> 11 & 3];
sum -= delta;
firstValue -= (secondValue << 4 ^ secondValue >> 5) + secondValue ^ sum + key[sum & 3];
}
value[0] = firstValue;
value[1] = secondValue;
}
value[1] = secondValue;
}
Code
The coding is where there is not as clear to what is going on. I'll do my best to break it down in understandable chunks to the best of my ability.
As mentioned above the algorithm processes the encryption in terms of character pairs which has been formatted into uints. Which is what we see in the first two lines.
As mentioned above the algorithm processes the encryption in terms of character pairs which has been formatted into uints. Which is what we see in the first two lines.
sum is the value used as an offset for the bit shifting to ensure the same characters don't turn out to be copies of each other in the end ciphered string.
delta is a constant value taken from the mathematical golden ratio which is a common constant used for hash scattering to ensure a secure, complex and efficient way of scattering values.
n is keeping track of the current bit in the character being processed (first and second value is being processed simultaneously).
n is keeping track of the current bit in the character being processed (first and second value is being processed simultaneously).
the expression can be broken down into 3 parts.
firstValue += (secondValue << 4 ^ secondValue >> 5) + secondValue ^ sum + key[sum & 3];
firstValue += (secondValue << 4 ^ secondValue >> 5) + secondValue ^ sum + key[sum & 3];
Part 1:
(secondValue << 4 ^ secondValue >> 5)
This expression shifts the bits by 4 bits to the left (multiply by 16) and then shifting by 5 bits to the right (dividing by 32) then applying the XOR operator to get a more unrecognizable value.
Part 2:
secondValue ^ sum
Then we use the XOR between the second value and the sum which was the offset used to ensure values are not repeated.
Part 3:
key[sum & 3]
Finally the key is introduced to the cipher and is clamped to a value between 0 and 7 as sum & 3 is always within one byte.
between the first and second value the sum gets it's golden ratio delta added to once again ensure the same sequence of the cipher is repeated.
Also note how the first value uses the second value and vice versa to add another layer of complexity to the algorithm.
I don't plan on getting into detail on how the decoding works as it is essentially the same procedure in reverse but all code can be found in the github repository for further investigation.
Coming soon to this page:
mySQL Database
ObjectPooling
File SaveSystem
Unity:
GameObjectPool
A* pathfinding
In-game Command console