I was wondering if there is an easy way to perform MD5 or a comparable form of encryption on a string in Unity. I know it’s as simple as a function call in Java Script, but Unity doesn’t seem to have it. If there’s nothing built in, is Unity Script versatile enough for me to be able to recreate the MD5 algorithm on my own?
I’m very interested in the answer to this, not for MD5 per se, but on the extensibility side of things. While I’m sure you could write your own MD5 implementation in Javascript or C#, does Unity support reusing .Net libraries?
You can import System.Security.Cryptography. And then use MD5CryptoServiceProvider(), ComputeHash() and so on.
It’s Turing-complete, so therefore it’s versatile enough to do literally everything. Theoretically.
–Eric
Sweet, the wiki link worked. Thanks!
Except for the pesky infinite memory thing which folks always forget
So, what about going back again? Within Unity?
I want to hash some text, save it in a text file, and then read it back in and de-hash it…
Any ideas?
The whole point of hashing something is that it’s one-way. There’s no de-hash.
Cheers,
-Jon
Of course there is… at the server end in PHP for instance…
I understand that on a PHP server it would use the data HASHED without exposing the de-hashed data to the world, but it still have to be de-hashed somewhere to be able to know what’s inside.
Thats the whole point of encrypting something isn’t it? to be able to de-crypt it somewhere else to use the information?
Uhm, no… there is no de-hash.
Edit: this might help: Hash function - Wikipedia
ok, so, no de-hash… which leaves me at…
I am looking to encrypt some text that I will save in a txt file and then read it back in and decrypt it, using Javascript in Unity…
Any suggestions on where to start?
We’ll do a Blurst technology post about this sometime, but here’s one option. We never moved this out of a technical test into deployment, but it’ll get you started (we actually went with a much simpler–and faster–solution).
import System.Security.Cryptography;
import System.IO;
import System.Text;
import System.Globalization.CultureInfo;
/*
* This isn't the proper way to do it--we should be generating salted keys from Rfc2898DeriveBytes, and the IV from the Encryptor
* However, Unity's Mono has a bug preventing us from doing this: [url]https://bugzilla.novell.com/show_bug.cgi?id=364360[/url]
*
* But whatever, this is fine for obscuring scores and things (it's not like we're storing credit card info or anything)
*/
// make up keys for our encryption (k1 = 16-byte IV, k2 = 32-byte key)
static private var k1:String = "1234567812345678";
static private var k2:String = "12345678123456781234567812345678";
// store floats/ints as encrypted strings
var hashFloats:Hashtable = new Hashtable();
var hashInts:Hashtable = new Hashtable();
// our singleton instance
static var instance:Protected;
/**
* Singleton logic
*/
function Awake()
{
if(instance != null)
{
Destroy(gameObject);
return;
}
instance = this;
transform.parent = null;
DontDestroyOnLoad(gameObject);
}
/**
* Add to a protected float value
*/
function AddToFloat(name:String, value:float):float
{
// if it doesn't exist, do a set
if(!hashFloats[name])
{
return SetFloat(name, value);
}
else
{
var currentString:String = Decrypt(hashFloats[name]);
var currentValue:float = float.Parse(currentString, InvariantCulture.NumberFormat);
return SetFloat(name, currentValue + value);
}
}
/**
* Set a protected float value
*/
function SetFloat(name:String, value:float):float
{
hashFloats[name] = Encrypt(value.ToString(InvariantCulture.NumberFormat));
return value;
}
/**
* Get a protected float
*/
function GetFloat(name:String):float
{
var encrypted:String = hashFloats[name];
if(encrypted)
{
return float.Parse(Decrypt(encrypted), InvariantCulture.NumberFormat);
}
else
return 0.0;
}
/**
* Add to a protected float value
*/
function AddToInt(name:String, value:int):int
{
// if it doesn't exist, do a set
if(!hashInts[name])
{
return SetInt(name, value);
}
else
{
var currentString:String = Decrypt(hashInts[name]);
var currentValue:int = int.Parse(currentString, InvariantCulture.NumberFormat);
return SetInt(name, currentValue + value);
}
}
/**
* Set a protected float value
*/
function SetInt(name:String, value:int):int
{
hashInts[name] = Encrypt(value.ToString(InvariantCulture.NumberFormat));
return value;
}
/**
* Get a protected int
*/
function GetInt(name:String):int
{
var encrypted:String = hashInts[name];
if(encrypted)
{
return int.Parse(Decrypt(encrypted), InvariantCulture.NumberFormat);
}
else
return 0;
}
/**
* Encrypt a string with AES (Rijndael)
*/
static function Encrypt(data:String):String
{
if(String.IsNullOrEmpty(data))
{
return "";
}
// plug in the keys
var aes:Rijndael = Rijndael.Create();
aes.Padding = PaddingMode.PKCS7;
aes.IV = Encoding.ASCII.GetBytes(k1);
aes.Key = Encoding.ASCII.GetBytes(k2);
// create our streams
var rawData:byte[] = Encoding.Unicode.GetBytes(data);
var memoryStream:MemoryStream = new MemoryStream();
var cryptoStream:CryptoStream = new CryptoStream(memoryStream, aes.CreateEncryptor(), CryptoStreamMode.Write);
// perform the encryption
cryptoStream.Write(rawData, 0, rawData.Length);
cryptoStream.FlushFinalBlock();
cryptoStream.Close();
// pull our encrypted bytes out
var encrypted:byte[] = memoryStream.ToArray();
memoryStream.Close();
// convert to string, return
return System.Convert.ToBase64String(encrypted);
}
/**
* Decrypt a string using AES
*/
static function Decrypt(data:String):String
{
if (String.IsNullOrEmpty(data))
{
return "";
}
var rawData:byte[] = System.Convert.FromBase64String(data);
if (rawData.Length < 8)
{
throw new System.ArgumentException("Invalid input data");
}
// plug in the keys
var aes:Rijndael = Rijndael.Create();
aes.Padding = PaddingMode.PKCS7;
aes.IV = Encoding.ASCII.GetBytes(k1);
aes.Key = Encoding.ASCII.GetBytes(k2);
// decrypt the data
var memoryStream:MemoryStream = new MemoryStream();
var cryptoStream:CryptoStream = new CryptoStream(memoryStream, aes.CreateDecryptor(), CryptoStreamMode.Write);
cryptoStream.Write(rawData, 0, rawData.Length);
cryptoStream.FlushFinalBlock();
cryptoStream.Close();
var decrypted:byte[] = memoryStream.ToArray();
memoryStream.Close();
return Encoding.Unicode.GetString(decrypted);
}
Hey Matthew, Thanks, but this is WAY overkill for what I need. I don’t even need strong encryption… just something to obscure the data I am storing and a way to read it back un-obscured.
There is another thread here about XORing the data, but there is no included solution to get the data back to a readable state.
That is a pretty generic challenge – the best algorithm is going to depend on the nature of the data and hat kind of tradeoffs you are looking at between security and performance, for example. As a generic (but relatively solid) option, I might try a symmetric key algorithm (Rijndael/AES) to encrypt and decrypt data. If you are rolling your own solution (even if you are using some pre-built .NET cryptography stuff) you are going to probably have to think about salt and other things, so you might want to do a bit more digging on the subject.
As to the specifics of a .NET implementation that will work in Unity, I don’t know – maybe someone else can speak from experience.
EDIT: And Matthew beat me to it while I was posting – you guys are fast. (or maybe I need to not reply while distracted by the TV). In any case, disregard my suggestion.
The relevant bits for a simple obfuscation are:
System.Convert.ToInt32()
- Turn a character into its ASCII representation. IE, B is 66 in decimal. Use http://www.asciitable.com/ for reference.
System.Convert.ToChar()
- And back again (ie 66 to ‘B’).
Basically, iterate a string, shift its characters to decimal, do some sort of simple math on them, and then reverse the process. Be careful you don’t move outside of the ASCII table (or even outside of printable characters, depending on how you’re storing them).
For something that won’t be happening multiple times a second I would honestly just use the Encrypt/Decrypt functions above. The Mono cryptography stuff is there and available; you may as well use it.
Ok, I am going to use your encrypt/decrypt Matthew… thanks! I guess this is all a bit above my head so I you are right… using the in built mono stuff is my best option. Thanks again for providing source
As the previous post said, what you chose to do depends on the nature of the data. If you put some forethought into the data you want to protect you can come up with some simple and quick solutions using bit manipulation.
For example scores, this came up in another thread where I showed someone how easy it is to use a memory editor to top his high score list. If you design your game so that the scores are never negatively signed and incremented by 10 (so the right most bit is always 0), you can easily just shift the bits right and left to obscure the value.
temp = hidden_score << 1
temp += 10
hidden_score = temp >> 1
temp = 0
displayed_score = hidden_score << 1
Something like the above is simple, cheap and effective.
If you want to encrypt actual text or keep the sign, then things become a bit more complicated. But some simple searches on the web and you can come up with a lightweight solution.
With XOR “encryption”, the encryption function is the same as the decryption function. Simply XOR the encrypted data with the key and you will get the unscrambled data back.
Heck, you can even get the encryption key by XORing the encrypted data with the unencrypted data.
@ Matthew -
Thanks for posting this. I put in my own 16 32 byte keys and it writes and reads to a file flawlessly. Thanks again!
Can I use this with Stripping enabled? I got bad access runtime exceptions when I used MD5 and had either Assembly or ByteCode stripping enabled on my iPhone build.
I’ve got the same problem here.
When I use stripping, the application crashes on iOS when using the PasswordDeriveBytes class, which uses SHA1 internally.
Looking at the assembly file (mscorlib.dll.s) all needed classes seems to be there, but the code just crashes with EXC_BAD_ACCESS when calling the GetBytes function, when it in turn calls the Reset function.
I also use the SHA256Managed class in another place, but this one does not crash once stripped.