Several users have encountered strange issues with my app/game. They reported that their save files have become invalid. I've tried to investigate the problem and haven't found anything.
Here is a pipeline:
- in-game data converts to json file (string)
- json file converts UTF8 bytes
- UTF8 bytes goes thru this encryption method (using AES)
Save file extension is .dat
This is a first game where I decided to use step #3 (save data with encryption) and I didn't found anything strange and like that in testcases and on test stands.
After I've got some save files from involved users, I saw that all of those files has valid non-zero length, but have only zero bytes.
This is an example of one of those files: only zero bytes here
Here is a code of encryption:
public static byte[] Encrypt(byte[] clearBytes, string encyptionKey)
{
using (var algorythm = Aes.Create())
{
algorythm.Mode = CipherMode.CBC;
algorythm.Padding = PaddingMode.PKCS7;
var encryptionBytes = _encoding.GetBytes(encyptionKey);
using (var pdb = new Rfc2898DeriveBytes(encryptionBytes, _salt, _iterations, _hashAlgorithm))
{
algorythm.Key = pdb.GetBytes(32);
algorythm.IV = pdb.GetBytes(16);
using (var encryptor = algorythm.CreateEncryptor())
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
cs.Write(clearBytes, 0, clearBytes.Length);
}
return ms.ToArray();
}
}
}
}
}
Here is a code of save file to file system:
public bool Save(string relativePath, byte[] bytes)
{
var absolutePath = Path.Combine(_applicationDataPath, relativePath);
var fi = new FileInfo(absolutePath);
var di = fi.Directory;
try
{
if (!di.Exists)
di.Create();
var exists = fi.Exists;
using (var fs = exists ? fi.OpenWrite() : fi.Create())
{
fs.SetLength(0);
fs.Seek(0, SeekOrigin.Begin);
using (var ms = new MemoryStream(bytes))
{
ms.CopyTo(fs);
ms.Flush();
return true;
}
}
}
catch (Exception ex)
{
Debug.LogException(ex);
return false;
}
}
Everything is working good if I remove encryption, no reports with broken save files there. The problem is I can't reproduce this bug on my own computer.
ms.Flush
doesn't do what you think it does and is completely unnecessary. You also don't need the memorystream at all, you can justWrite
the bytes. Also why write the encrypted bytes to a memorystream to turn into an array to turn into a memroystream to write to a file, why not just wrap theCryptoStream
directly over the file? And why Base64 the JSON rather than just encrypting the UTF8 JSON directly? I hope the encryption key isn't hard-coded in your app...