Unity
C#
1-2 months
Solo
Proggrammer
/// <summary>
/// Author: Jason Shull
/// Description: This is the script used to handle the retrival of data from all active objects in the scene
/// before closing and loading previously saved data into these objects
/// </summary>
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine.SceneManagement;
using System.Diagnostics;
using UnityEngine.Windows;
public class DataPersistanceManager : MonoBehaviour
{
[Header("File Storage Config")]
[SerializeField] private string fileName;
[SerializeField] private string encryptionKeyPlayerPrefs;
private static FileDataHandler dataHandler;
[Tooltip("amount of encryptions which by default will encrypt the data 10 times, Higher number = more secure but more time")]
public FileDataHandler.numberOfRounds numRound;
private GameData gameData;
private List<IDataPersistance> dataPersistanceObjects;
public TaskCompletionSource<bool> tcsSave;
public bool withRounding;
[Range(1,16)]
public int roundHowManyDecimalPlaces = 2;
public static DataPersistanceManager instance { get; protected set; }
private void Awake()
{
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
if (instance != null)
{
UnityEngine.Debug.LogError("Found more than one instance");
}
instance = this;
DontDestroyOnLoad(gameObject);
if (dataHandler == null)
dataHandler = new FileDataHandler(Application.persistentDataPath, fileName, encryptionKeyPlayerPrefs, numRound, withRounding, roundHowManyDecimalPlaces);
//Load any saved data from file
this.gameData = dataHandler.Load<GameData>().Result;
//if no data loaded create new
if (this.gameData == null)
{
print("Making New Game");
NewGame();
}
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
//if (arg0.buildIndex == 1)
//{
this.dataPersistanceObjects = FindAllDataPersistanceObjects();
LoadGame();
//}
}
public GameData GetGameData()
{ return this.gameData; }
public void NewGame()
{
this.gameData = new GameData();
}
public void EraseSave()
{
if (!Application.isPlaying)
{
if (dataHandler == null)
dataHandler = new FileDataHandler(Application.persistentDataPath, fileName, encryptionKeyPlayerPrefs, numRound, withRounding, roundHowManyDecimalPlaces);
}
dataHandler.EraseData();
}
public void LoadGame()
{
//Load any saved data from file
this.gameData = dataHandler.Load<GameData>().Result;
//if no data loaded create new
if (this.gameData == null)
{
print("Making New Game");
NewGame();
}
//push loaded data to other scripts
foreach(IDataPersistance dataPersistantObj in dataPersistanceObjects)
{
dataPersistantObj.LoadData(gameData);
}
}
public void SaveGame()
{
if (tcsSave == null || tcsSave.Task.IsCompleted || tcsSave.Task.IsFaulted || tcsSave.Task.IsCanceled)
{
tcsSave = new TaskCompletionSource<bool>();
}
if (dataPersistanceObjects != null && dataPersistanceObjects.Count > 0)
{
foreach (IDataPersistance dataPersistantObj in dataPersistanceObjects)
{
dataPersistantObj.SaveData(ref gameData);
}
}
//save that data to a file
dataHandler.Save(gameData);
tcsSave.SetResult(true);
}
public void OpenFileLocation()
{
var FilePath = Application.persistentDataPath;
if (Directory.Exists(FilePath))
{
Process.Start(new ProcessStartInfo
{
FileName = FilePath,
UseShellExecute = true,
Verb = "open"
});
}
}
private void OnApplicationQuit()
{
SaveGame();
}
private List<IDataPersistance> FindAllDataPersistanceObjects()
{
IEnumerable <IDataPersistance> dataPersistanceObjects = FindObjectsOfType <MonoBehaviour>()
.OfType <IDataPersistance>();
return new List <IDataPersistance>(dataPersistanceObjects);
}
}
/// <summary>
/// Author: Jason Shull
/// Description: This is a file that saves and encrypts the data that wants
/// to be saved and saves it as a json new file
/// </summary>
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Text;
using Newtonsoft.Json;
public class FileDataHandler
{
/// <summary>
/// directory path where data will be saved
/// </summary>
private string dataDirPath = "";
/// <summary>
/// name of the file that the data will be saved to
/// </summary>
private string dataFileName = "";
/// <summary>
/// if the user decides to change the file
/// name it is saved as this variable and once the
/// user quits game again it will save it as this name
/// </summary>
private string newFileName = "";
/// <summary>
/// array of bytes to be used to generate the key
/// </summary>
public byte[] EncryptionKeyspace;
/// <summary>
/// name of playerpref that the encryption key will be stored
/// </summary>
private string encryptionKeyPlayerPrefs = "";
/// <summary>
/// if the user decides to change the encryptionKey
/// name it is saved as this variable and once the
/// user quits game again it will save it as this name
/// </summary>
private string newEncryptionKey = "";
private bool hasRounding = true;
private int decimalsToRound = 2;
/// <summary>
/// Player Pref string name to get previously assigned number of rounds
/// </summary>
private readonly string previousNumOfRoundPlayerPref = "currentNumRound";
/// <summary>
/// Player Pref string name to get previously assigned file name
/// </summary>
private readonly string previousFileNamePlayerPref = "currentFileName";
/// <summary>
/// Player Pref string name to get previously assigned encryptionKey string
/// </summary>
private readonly string previousEncryptionKeyPlayerPref = "currentEncryptionKey";
#region variables for encryption
[Tooltip("Enum dictating the number of rounds the cipher should encrypt for")]
public enum numberOfRounds
{
Ten,
Twelve,
Fourteen
};
/// <summary>
/// the current dictated number of rounds the cipher should encrypt for
/// </summary>
private numberOfRounds numRound;
/// <summary>
/// if the user decides to change the number of rounds
/// it is saved as this variable and once the
/// user quits game again it will save it with the new value into the
/// player prefs
/// </summary>
private numberOfRounds newNumRound;
private Word[] roundKeys = new Word[44]
{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)
new Word(0x00,0x00,0x00,0x00),};
[Tooltip("Number of Rounds")]
private int Nr = 10;
//Number of columns (32-bit words) comprising the State.
private const int Nb = 4;
//Num of 32-bit words comprising the Cipher Key
private int Nk = 4;
private byte d = 7;
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
private byte[,] lookupTable = new byte[16, 16] {
/* 0 */ { 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76 },
/* 1 */ { 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0 },
/* 2 */ { 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15 },
/* 3 */ { 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75 },
/* 4 */ { 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84 },
/* 5 */ { 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF },
/* 6 */ { 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8 },
/* 7 */ { 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2 },
/* 8 */ { 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73 },
/* 9 */ { 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB },
/* a */ { 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79 },
/* b */ { 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08 },
/* c */ { 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A },
/* d */ { 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E },
/* e */ { 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF },
/* f */ { 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 }};
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
private byte[,] inverseLookupTable = new byte[16, 16] {
/* 0 */ { 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb },
/* 1 */ { 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb },
/* 2 */ { 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e },
/* 3 */ { 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 },
/* 4 */ { 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 },
/* 5 */ { 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 },
/* 6 */ { 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 },
/* 7 */ { 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b },
/* 8 */ { 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 },
/* 9 */ { 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e },
/* a */ { 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b },
/* b */ { 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 },
/* c */ { 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f },
/* d */ { 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef },
/* e */ { 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 },
/* f */ { 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }};
private Word[] Rcon = new Word[10]
{
new Word(0x01,0,0,0),
new Word(0x02,0,0,0),
new Word(0x04,0,0,0),
new Word(0x08,0,0,0),
new Word(0x10,0,0,0),
new Word(0x20,0,0,0),
new Word(0x40,0,0,0),
new Word(0x80,0,0,0),
new Word(0x1b,0,0,0),
new Word(0x36,0,0,0)
};
private Word[] input = new Word[4]
{
new Word(0x00, 0x00, 0x00, 0x00),
new Word(0x00, 0x00, 0x00, 0x00),
new Word(0x00, 0x00, 0x00, 0x00),
new Word(0x00, 0x00, 0x00, 0x00)
};
#endregion
#region Saving Data
/// <summary>
/// Constructor Method of the data handler
/// </summary>
/// <param name="dataDirPath">Directory path where the file will be saved</param>
/// <param name="dataFileName">the name of the file that will be created</param>
/// <param name="encryptionKey">what to save the generated key under via the name</param>
/// <param name="num">number of rounds that will be used to encrypt</param>
public FileDataHandler(string dataDirPath, string dataFileName, string encryptionKey, numberOfRounds num, bool hasRounding, int decimalPlaces)
{
this.dataDirPath = dataDirPath;
newEncryptionKey = encryptionKey;
newFileName = dataFileName;
newNumRound = num;
this.hasRounding = hasRounding;
decimalsToRound = decimalPlaces;
this.dataFileName = PlayerPrefs.GetString(previousFileNamePlayerPref);
encryptionKeyPlayerPrefs = PlayerPrefs.GetString(previousEncryptionKeyPlayerPref);
int enumVal = PlayerPrefs.GetInt(previousNumOfRoundPlayerPref);
switch (enumVal)
{
case 0:
numRound = numberOfRounds.Ten;
break;
case 1:
numRound = numberOfRounds.Twelve;
break;
case 2:
numRound = numberOfRounds.Fourteen;
break;
default:
UnityEngine.Debug.LogError("Enum out of bounds");
break;
}
}
/// <summary>
/// Loads data that was currently saved and decrypts it
/// </summary>
/// <typeparam name="T">dataType</typeparam>
/// <returns>data as an object that was previously saved</returns>
public async Task<T> Load<T>()
{
try
{
await InitalizeKeys();
}
catch (Exception e)
{
UnityEngine.Debug.LogError(e);
return (T)(object)null;
}
//use path.combine to account for different OS's having different path separator
string fullPath = Path.Combine(dataDirPath, dataFileName + ".json");
T loadedData = default;
if (File.Exists(fullPath))
{
string dataToLoad = "";
try
{
//load the serialized data from file
using (FileStream stream = new FileStream(fullPath, FileMode.Open))
{
using (StreamReader reader = new StreamReader(stream))
{
dataToLoad = reader.ReadToEnd();
}
}
if (dataToLoad == "")
return (T)(object)null;
dataToLoad = DecryptData(dataToLoad);
//deseralize data from json back into c# obj
loadedData = JsonConvert.DeserializeObject<T>(dataToLoad,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include,
Converters = new List<JsonConverter>() { new DecimalFormatJsonConverter(2) }
});
}
catch (Exception e)
{
UnityEngine.Debug.LogError("Error when trying to load data from file: " + fullPath + "\n" + e);
}
}
return (T)(object)loadedData;
}
/// <summary>
/// Generates new key to encrypt the data and then encrypts the data
/// and then saves it in addition accounts for new value changes when the manager
/// decides to quickly change the number of rounds or filename or encryption key
/// </summary>
/// <typeparam name="T">dataType</typeparam>
/// <param name="data">data to be saved</param>
public async void Save<T>(T data)
{
numRound = newNumRound;
if(!encryptionKeyPlayerPrefs.Equals(newEncryptionKey))
{
PlayerPrefs.DeleteKey(encryptionKeyPlayerPrefs);
encryptionKeyPlayerPrefs = newEncryptionKey;
PlayerPrefs.SetString(previousEncryptionKeyPlayerPref, newEncryptionKey);
}
PlayerPrefs.SetInt(previousNumOfRoundPlayerPref, (int)newNumRound);
await GenerateKey();
if (!dataFileName.Equals(newFileName))
{
UnityEngine.Debug.Log(dataFileName + " " + newFileName);
string previousPath = Path.Combine(dataDirPath, dataFileName + ".json");
if (File.Exists(previousPath))
{
File.Delete(previousPath);
}
dataFileName = newFileName;
PlayerPrefs.SetString(previousFileNamePlayerPref, newFileName);
}
string fullPath = Path.Combine(dataDirPath, dataFileName + ".json");
//create directory the file will be written if doesnt exist
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
//serilaze the c# game data object into Json
string dataToStore = JsonUtility.ToJson(data, true);
string jsonStr = null;
if(hasRounding)
{
jsonStr = JsonConvert.SerializeObject(data,
Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include,
Converters = new List<JsonConverter>() { new DecimalFormatJsonConverter(decimalsToRound) }
});
}
else
{
jsonStr = JsonConvert.SerializeObject(data,
Formatting.Indented,
new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Include,
});
}
#if UNITY_EDITOR
string tempPath = Path.Combine(dataDirPath, "beforeEncryption.json");
Directory.CreateDirectory(Path.GetDirectoryName(tempPath));
WriteDataToFile(tempPath, jsonStr);
#endif
dataToStore = EncryptData(jsonStr);
WriteDataToFile(fullPath, dataToStore);
}
/// <summary>
/// Erases data that currently exists in the file
/// </summary>
public void EraseData()
{
if (!dataFileName.Equals(newFileName))
{
string previousPath = Path.Combine(dataDirPath, dataFileName + ".json");
if (File.Exists(previousPath))
{
File.Delete(previousPath);
}
dataFileName = newFileName;
PlayerPrefs.SetString(previousFileNamePlayerPref, newFileName);
}
string fullPath = Path.Combine(dataDirPath, dataFileName + ".json");
if(File.Exists(fullPath))
{
File.Delete(fullPath);
}
//WriteDataToFile(fullPath, "");
}
/// <summary>
/// attempts to write serialized data to file
/// </summary>
/// <param name="path">path to save data to</param>
/// <param name="data">data to be added to file</param>
private void WriteDataToFile(string path, string data)
{
try
{
//write serialized data to file
using (FileStream stream = new FileStream(path, FileMode.Create))
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(data);
}
}
}
catch (Exception e)
{
UnityEngine.Debug.LogError("Error occurred when trying to save data to file: " + path + "\n" + e);
}
}
#endregion
}
#region AES
#region Key Generation
public async Task GenerateKey()
{
switch (numRound)
{
case numberOfRounds.Ten:
{
EncryptionKeyspace = new byte[16];
Nr = 10;
Nk = 4;
roundKeys = new Word[44]{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)};
break;
}
case numberOfRounds.Twelve:
{
Nr = 12;
EncryptionKeyspace = new byte[24];
Nk = 6;
roundKeys = new Word[52]{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)};
break;
}
case numberOfRounds.Fourteen:
{
Nr = 14;
EncryptionKeyspace = new byte[32];
Nk = 8;
roundKeys = new Word[60]{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)};
break;
}
default:
break;
}
string s = "";
for (int a = 0; a < EncryptionKeyspace.Length; a++)
{
EncryptionKeyspace[a] = (byte)(UnityEngine.Random.Range(0, 257));
s += EncryptionKeyspace[a].ToString();
if (a + 1 < EncryptionKeyspace.Length)
s += "|";
}
for (int i = 0; i < EncryptionKeyspace.Length / Nb; i++)
{
if (i == 0)
roundKeys[i / Nb] = new Word(EncryptionKeyspace[i], EncryptionKeyspace[i + 1], EncryptionKeyspace[i + 2], EncryptionKeyspace[i + 3]);
else
roundKeys[i / Nb] = new Word(EncryptionKeyspace[(i * 4)], EncryptionKeyspace[(i * 4) + 1], EncryptionKeyspace[(i * 4) + 2], EncryptionKeyspace[(i * 4) + 3]);
}
PlayerPrefs.SetString(encryptionKeyPlayerPrefs, s);
await KeyExpansion(EncryptionKeyspace, roundKeys);
}
public async Task InitalizeKeys()
{
string s = PlayerPrefs.GetString(encryptionKeyPlayerPrefs);
switch (numRound)
{
case numberOfRounds.Ten:
{
EncryptionKeyspace = new byte[16];
Nr = 10;
Nk = 4;
roundKeys = new Word[44]{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)};
break;
}
case numberOfRounds.Twelve:
{
Nr = 12;
EncryptionKeyspace = new byte[24];
Nk = 6;
roundKeys = new Word[52]{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)};
break;
}
case numberOfRounds.Fourteen:
{
Nr = 14;
EncryptionKeyspace = new byte[32];
Nk = 8;
roundKeys = new Word[60]{
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00),
new Word(0x00,0x00,0x00,0x00)};
break;
}
default:
break;
}
int nInd = 0;
int eInd = 0;
for (int i = 0; i < s.Length; i++)
{
if (s[i] == '|')
{
EncryptionKeyspace[eInd] = byte.Parse(s.Substring(nInd, i - nInd));
nInd = i + 1;
eInd++;
}
else if (i + 1 == s.Length)
EncryptionKeyspace[eInd] = byte.Parse(s.Substring(nInd, i - (nInd - 1)));
}
for (int i = 0; i < EncryptionKeyspace.Length / Nb; i++)
{
if (i == 0)
roundKeys[i / Nb] = new Word(EncryptionKeyspace[i], EncryptionKeyspace[i + 1], EncryptionKeyspace[i + 2], EncryptionKeyspace[i + 3]);
else
roundKeys[i / Nb] = new Word(EncryptionKeyspace[(i * 4)], EncryptionKeyspace[(i * 4) + 1], EncryptionKeyspace[(i * 4) + 2], EncryptionKeyspace[(i * 4) + 3]);
}
await KeyExpansion(EncryptionKeyspace, roundKeys);
}
Task KeyExpansion(byte[] key, Word[] rkeys)
{
int i = 0;
while (i < Nk)
{
rkeys[i] = new Word(key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]);
i = i + 1;
}
i = Nk;
do
{
var temp = rkeys[i - 1];
if (i % Nk == 0)
{
Word t = RotWord(temp);
Word k = SubWord(t);
temp = XOR(SubWord(RotWord(temp)), Rcon[(i / Nk) - 1]);
}
else if (Nk > 6 && i % Nk == 4)
{
temp = SubWord(temp);
}
rkeys[i] = XOR(rkeys[i - Nk], temp);
i = i + 1;
} while (i < Nb * (Nr + 1));
roundKeys = rkeys;
return Task.CompletedTask;
}
#endregion
#region Encryption
private Word[] MixTemp(Word[] w)
{ // 's' is the main State matrix, 'ss' is a temp matrix of the same dimensions as 's'.
Word[] output = new Word[4] {
new Word(0,0,0,0),
new Word(0, 0, 0, 0) ,
new Word(0, 0, 0, 0) ,
new Word(0, 0, 0, 0) };
for (int c = 0; c < 4; c++)
{
output[c].W[0] = (byte)(GMul(0x02, w[c].W[0]) ^ GMul(0x03, w[c].W[1]) ^ w[c].W[2] ^ w[c].W[3]);
output[c].W[1] = (byte)(w[c].W[0] ^ GMul(0x02, w[c].W[1]) ^ GMul(0x03, w[c].W[2]) ^ w[c].W[3]);
output[c].W[2] = (byte)(w[c].W[0] ^ w[c].W[1] ^ GMul(0x02, w[c].W[2]) ^ GMul(0x03, w[c].W[3]));
output[c].W[3] = (byte)(GMul(0x03, w[c].W[0]) ^ w[c].W[1] ^ w[c].W[2] ^ GMul(0x02, w[c].W[3]));
}
return output;
}
private Word[] ShiftRows(Word[] w)
{
for (int r = 1; r < Nb; r++)
{
for (int shitfs = 0; shitfs < r; shitfs++)
{
for (int c = 0; c < 3; c++)
{
byte temp = w[c].W[r];
w[c].W[r] = w[c + 1].W[r];
w[c + 1].W[r] = temp;
}
}
}
return w;
}
private Word[] SubBytes(Word[] w)
{
string hRep = "";
for (int r = 0; r < Nb; r++)
{
for (int c = 0; c < Nb; c++)
{
hRep = ByteToHex(w[c].W[r]);
w[c].W[r] = lookupTable[HexConverter(hRep[0]), HexConverter(hRep[1])];
}
}
return w;
}
private Word[] Function(Word[] input)
{
Word[] state = input;
state = AddRoundKey(state, 0);
for (int round = 1; round < Nr; round++)
{
state = SubBytes(state);
state = ShiftRows(state);
state = MixTemp(state);
state = AddRoundKey(state, round);
}
state = SubBytes(state);
state = ShiftRows(state);
state = AddRoundKey(state, Nr);
return state;
}
/// <summary>
/// Encrypts input data in sections based off
/// of the size of the plaintext and combines it all back together
/// </summary>
/// <param name="plainText">string to be encrypted</param>
/// <returns>encrypted result of input string</returns>
public string EncryptData(string plainText)
{
byte[] plainTxtAsBytes = Encoding.ASCII.GetBytes(plainText);
int numOfEncryptions = 0;
string cipherTxt = "";
if (plainTxtAsBytes.Length % 16 == 0)
{
numOfEncryptions = (plainTxtAsBytes.Length / 16);
}
else
{
numOfEncryptions = (int)((plainTxtAsBytes.Length - (plainTxtAsBytes.Length % 16.0f)) / 16.0f) + 1;
Array.Resize<byte>(ref plainTxtAsBytes, 16 * numOfEncryptions);
}
for (int encryptions = 0; encryptions < numOfEncryptions; encryptions++)
{
string t = "";
for (int i = 0; i < plainTxtAsBytes.Length; i++)
{
t += Convert.ToString(plainTxtAsBytes[i], 16) + " | ";
}
for (int i = 0; i < 4; i++)
{
if (i == 0)
{
input[i % Nk] = new Word(plainTxtAsBytes[(encryptions * 16) + i], plainTxtAsBytes[(encryptions * 16) + i + 1], plainTxtAsBytes[(encryptions * 16) + i + 2], plainTxtAsBytes[(encryptions * 16) + i + 3]);
}
else
{
input[i % Nk] = new Word(plainTxtAsBytes[(encryptions * 16) + (i * 4)], plainTxtAsBytes[(encryptions * 16) + (i * 4) + 1], plainTxtAsBytes[(encryptions * 16) + (i * 4) + 2], plainTxtAsBytes[(encryptions * 16) + (i * 4) + 3]);
}
}
input = Function(input);
for (int j = 0; j < input.Length; j++)
{
if (j + 1 == input.Length && encryptions + 1 == numOfEncryptions)
cipherTxt += BitConverter.ToString(input[j].W);
else
cipherTxt += BitConverter.ToString(input[j].W) + "-";
}
}
return cipherTxt;
}
#endregion
#region Decryption
private Word[] InvMixColumns(Word[] w)
{
Word[] output = new Word[4] {
new Word(0,0,0,0),
new Word(0, 0, 0, 0) ,
new Word(0, 0, 0, 0) ,
new Word(0, 0, 0, 0) };
for (int c = 0; c < 4; c++)
{
output[c].W[0] = (byte)(GMul(0x0E, w[c].W[0]) ^ GMul(0x0B, w[c].W[1]) ^ GMul(0x0D, w[c].W[2]) ^ GMul(0x09, w[c].W[3]));
output[c].W[1] = (byte)(GMul(0x09, w[c].W[0]) ^ GMul(0x0E, w[c].W[1]) ^ GMul(0x0B, w[c].W[2]) ^ GMul(0x0D, w[c].W[3]));
output[c].W[2] = (byte)(GMul(0x0D, w[c].W[0]) ^ GMul(0x09, w[c].W[1]) ^ GMul(0x0E, w[c].W[2]) ^ GMul(0x0B, w[c].W[3]));
output[c].W[3] = (byte)(GMul(0x0B, w[c].W[0]) ^ GMul(0x0D, w[c].W[1]) ^ GMul(0x09, w[c].W[2]) ^ GMul(0x0E, w[c].W[3]));
}
return output;
}
private Word[] InvShiftRows(Word[] w)
{
for (int r = 1; r < Nb; r++)
{
for (int shitfs = 0; shitfs < r; shitfs++)
{
for (int c = 3; c > 0; c--)
{
byte temp = w[c].W[r];
w[c].W[r] = w[c - 1].W[r];
w[c - 1].W[r] = temp;
}
}
}
return w;
}
private Word[] InvSubBytes(Word[] w)
{
for (int r = 0; r < Nb; r++)
{
for (int c = 0; c < Nb; c++)
{
string hRep = ByteToHex(w[c].W[r]);
w[c].W[r] = inverseLookupTable[HexConverter(hRep[0]), HexConverter(hRep[1])];
}
}
return w;
}
private Word[] InvFunction(Word[] input)
{
Word[] state = input;
state = AddRoundKey(state, Nr);
for (int round = Nr - 1; round > 0; round--)
{
state = InvShiftRows(state);
state = InvSubBytes(state);
state = AddRoundKey(state, round);
state = InvMixColumns(state);
}
InvShiftRows(state);
InvSubBytes(state);
state = AddRoundKey(state, 0);
return state;
}
/// <summary>
/// Converts the ciphertext to byte array to then be formated
/// as an array of words so it can be parsed through the inverse function
/// to decrypt the text
/// </summary>
/// <param name="cipherText">Encrypted data as string</param>
/// <returns>Decrypted data</returns>
public string DecryptData(string cipherText)
{
int keyIndex = 0;
string plaintxt = "";
string subString = "";
int ind = 0;
byte[] bytes = new byte[((cipherText.Length + 1) / 3)];
for (int a = 0; a < cipherText.Length; a++)
{
if (cipherText[a] != '-')
{
subString += cipherText[a];
}
if (a + 1 == cipherText.Length || cipherText[a] == '-')
{
int num = (Convert.ToByte(subString, 16));
bytes[ind] = (byte)num;
keyIndex = (keyIndex + 1) % (16);
ind++;
subString = "";
}
}
int numOfEncryptions = bytes.Length / 16;
for (int encryptions = 0; encryptions < numOfEncryptions; encryptions++)
{
for (int i = 0; i < Nb; i++)
{
if (i == 0)
{
input[i % Nk] = new Word(bytes[(encryptions * 16) + i], bytes[(encryptions * 16) + i + 1], bytes[(encryptions * 16) + i + 2], bytes[(encryptions * 16) + i + 3]);
}
else
{
input[i % Nk] = new Word(bytes[(encryptions * 16) + (i * 4)], bytes[(encryptions * 16) + (i * 4) + 1], bytes[(encryptions * 16) + (i * 4) + 2], bytes[(encryptions * 16) + (i * 4) + 3]);
}
}
input = InvFunction(input);
for (int j = 0; j < input.Length; j++)
{
plaintxt += Encoding.ASCII.GetString(input[j].W);
}
}
return plaintxt;
}
#endregion
#region Other AES Methods
/// <summary>
/// takes a word[a0, a1, a2, a3] as input, performs a cyclic permutation,
/// and returns the word[a1, a2, a3, a0]
/// </summary>
/// <param name="input"></param>
/// <param name="output"></param>
private Word RotWord(Word input)
{
Word output = new Word(0, 0, 0, 0);
for (int i = 0; i < input.W.Length; i++)
{
output.W[i] = input.W[(i + 1) % input.W.Length];
}
return output;
}
/// <summary>
/// is a function that takes a four-byte input word and applies the S-box
/// to each of the four bytes to produce an output word
/// </summary>
/// <param name="input"></param>
/// <param name="output"></param>
private Word SubWord(Word input)
{
Word output = new Word(0, 0, 0, 0);
string g = BitConverter.ToString(input.W);
string temp = "";
int j = 0;
for (int i = 0; i < g.Length; i++)
{
if (g[i] != '-')
{
temp += g[i];
}
if (i + 1 == g.Length || g[i] == '-')
{
int h1 = HexConverter(temp[0]);
int h2 = HexConverter(temp[1]);
output.W[j] = lookupTable[h1, h2];
temp = "";
j++;
}
}
return output;
}
/// <summary>
/// Round Key is added to the State by a simple bitwise XOR operation
/// Each Round Key consists of Nb words from the key schedule
/// </summary>
/// <param name="state">Array of words which is the key</param>
/// <param name="round">current round</param>
private Word[] AddRoundKey(Word[] state, int round)
{
for (int colum = 0; colum < Nb; colum++)
{
state[colum] = XOR(state[colum], roundKeys[(round * Nb) + colum]);
}
return state;
}
/// <summary>
/// exclusive or operation between two bytes and returns the resulting value
/// </summary>
/// <param name="b1"></param>
/// <param name="b2"></param>
/// <returns></returns>
private byte XOR(byte b1, byte b2)
{
string temp1 = ByteToHex(b1);
string temp2 = ByteToHex(b2);
string b1C = Convert.ToString(b1, 2);
string b2C = Convert.ToString(b2, 2);
b1C = ExpandTo8(b1C);
if (b2C.Length != 8)
{
string temp = "";
for (int c = 0; c < 8 - b2C.Length; c++)
{
temp += "0";
}
b2C = temp + b2C;
}
string b3 = "";
for (int i = 0; i < b1C.Length; i++)
{
if (b1C[i] != b2C[i])
{
b3 += "1";
}
else
b3 += "0";
}
return ConvertToByte(b3);
}
private Word XOR(Word b1, Word b2)
{
byte w1 = XOR(b1.W[0], b2.W[0]);
byte w2 = XOR(b1.W[1], b2.W[1]);
byte w3 = XOR(b1.W[2], b2.W[2]);
byte w4 = XOR(b1.W[3], b2.W[3]);
return new Word(w1, w2, w3, w4);
}
/// <summary>
/// Galois Field (256) Multiplication of two Bytes
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
private byte GMul(byte a, byte b)
{
byte p = 0;
for (int counter = 0; counter < 8; counter++)
{
if ((b & 1) != 0)
{
p ^= a;
}
bool hi_bit_set = (a & 0x80) != 0;
a <<= 1;
if (hi_bit_set)
{
a ^= 0x1B;
}
b >>= 1;
}
return p;
}
#endregion
#region Helpful Conversion Stuff
/// <summary>
/// Converts hex value as number so can go through the values in the lookup table
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
private int HexConverter(char hex)
{
switch (hex)
{
case ('a'):
case ('A'):
return 10;
case ('b'):
case ('B'):
return 11;
case ('c'):
case ('C'):
return 12;
case ('d'):
case ('D'):
return 13;
case ('e'):
case ('E'):
return 14;
case ('f'):
case ('F'):
return 15;
default:
return int.Parse(hex.ToString());
}
}
/// <summary>
/// Converts hex value as number so can go through the values in the lookup table
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
private int HexConverter(string hex)
{
int output = 0;
if (hex[0] == 'A' || hex[0] == 'a')
output += 160;
else if (hex[0] == 'B' || hex[0] == 'b')
output += 176;
else if (hex[0] == 'C' || hex[0] == 'c')
output += 192;
else if (hex[0] == 'D' || hex[0] == 'd')
output += 208;
else if (hex[0] == 'E' || hex[0] == 'e')
output += 224;
else if (hex[0] == 'F' || hex[0] == 'f')
output += 240;
else
output += int.Parse(hex.ToString()) * 16;
switch (hex[1])
{
case ('a'):
case ('A'):
return output + 10;
case ('b'):
case ('B'):
return output + 11;
case ('c'):
case ('C'):
return output + 12;
case ('d'):
case ('D'):
return output + 13;
case ('e'):
case ('E'):
return output + 14;
case ('f'):
case ('F'):
return output + 15;
default:
return output + int.Parse(hex.ToString());
}
}
private string ByteToHex(byte b)
{
string k = Convert.ToString(b, 16);
if (k.Length == 1)
{
return "0" + k;
}
else if (k.Length == 0)
{
return "00";
}
return k;
}
protected byte ConvertToByte(string cipherText)
{
float byteRepresentation = 0;
for (int i = 0; i < cipherText.Length; i++)
{
if (cipherText[i] == '1')
byteRepresentation += Mathf.Pow(2, 7 - i);
}
byte b = (byte)byteRepresentation;
return b;
}
/// <summary>
/// expands binary number as string to be 8 places
/// for use in XOR operation
/// </summary>
/// <param name="binaryRepresentation"></param>
/// <returns></returns>
private static string ExpandTo8(string binaryRepresentation)
{
if (binaryRepresentation.Length != 8)
{
string temp = "";
for (int c = 0; c < 8 - binaryRepresentation.Length; c++)
{
temp += "0";
}
binaryRepresentation = temp + binaryRepresentation;
}
return binaryRepresentation;
}
#endregion
public string RemoveSpaces(string p)
{
string o = "";
for (int i = 0; i < p.Length; i++)
{
if (p[i] != ' ')
o += p[i];
}
return o;
}
/// <summary>
/// Converts input string as Hexidecimal
/// </summary>
/// <param name="plainText">string to convert</param>
/// <returns>whole string as hex</returns>
public string ConvertToHex(string plainText)
{
byte[] example = Encoding.ASCII.GetBytes(plainText);
string output = BitConverter.ToString(example);
return output;
}
#endregion