Skip to content

Commit

Permalink
feat: implement a hash to check for tempered files
Browse files Browse the repository at this point in the history
  • Loading branch information
ManuelRauber committed Mar 11, 2024
1 parent ac9fdc3 commit 80e2d0f
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,31 @@ public class JsonFileManager

public UniTask<bool> ExistsAsync(string key)
{
var result = File.Exists(CreateFilePath(key));
var result = File.Exists(CreatePath(key));

return UniTask.FromResult(result);
}

public async UniTask WriteAsync<T>(string key, T serializable)
public async UniTask<string> WriteAsync<T>(string key, T serializable)
{
var jsonSerialization = JsonUtility.ToJson(serializable);

var path = CreateFilePath(key);
var path = CreatePath(key);
EnsurePath(path);

await File.WriteAllTextAsync(path, jsonSerialization);

return path;
}

public async UniTask<T> ReadAsync<T>(string key)
{
var jsonFromFile = await File.ReadAllTextAsync(CreateFilePath(key));
var jsonFromFile = await File.ReadAllTextAsync(CreatePath(key));

return JsonUtility.FromJson<T>(jsonFromFile);
}

private string CreateFilePath(string key)
{
return Path.Combine(_rootPath, key);
}
public string CreatePath(string key) => Path.Combine(_rootPath, key);

private void EnsurePath(string path)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using BoundfoxStudios.FairyTaleDefender.Infrastructure.ScriptableObjects;
using BoundfoxStudios.FairyTaleDefender.Infrastructure;

namespace BoundfoxStudios.FairyTaleDefender.Systems.SaveGameSystem
{
Expand All @@ -12,6 +12,6 @@ public class SaveGameData
/// <summary>
/// Identifies the last played level.
/// </summary>
public IdentifiableSO? LastLevel;
public ScriptableObjectIdentity? LastLevel;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.IO;
using System.Security.Cryptography;
using UnityEngine;

namespace BoundfoxStudios.FairyTaleDefender.Systems.SaveGameSystem
{
public static class SaveGameHash
{
public static string Create(string saveName, string saveGameFilePath)
{
using var sha512 = SHA512.Create();
using var fileStream = new FileStream(saveGameFilePath, FileMode.Open, FileAccess.Read);
var fileHash = sha512.ComputeHash(fileStream);

var hash = new Hash128();

hash.Append(saveName);
hash.Append(fileHash);

return hash.ToString();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public class SaveGameMeta
{
public string Name = string.Empty;
public DateTime LastPlayedDate;
public string Hash = string.Empty;

public string Directory { get; set; }
public string Directory { get; set; } = string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using BoundfoxStudios.FairyTaleDefender.Common;
using BoundfoxStudios.FairyTaleDefender.Extensions;
using BoundfoxStudios.FairyTaleDefender.Infrastructure.FileManagement;
Expand Down Expand Up @@ -76,8 +77,12 @@ public async UniTask<List<SaveGameMeta>> ListAvailableSaveGamesAsync()

try
{
var saveGameFile = Path.Combine(meta.Directory, Constants.SaveGames.SaveGameFileName);
var saveGameFilePath = await _fileManager.WriteAsync(saveGameFile, data);

meta.Hash = SaveGameHash.Create(meta.Name, saveGameFilePath);

await _fileManager.WriteAsync(Path.Combine(meta.Directory, Constants.SaveGames.MetaFileName), meta);
await _fileManager.WriteAsync(Path.Combine(meta.Directory, Constants.SaveGames.SaveGameFileName), data);
}

catch
Expand All @@ -98,13 +103,29 @@ public async UniTask<bool> SaveGameExistsAsync(string saveName)

private async UniTask<bool> IsValidSaveGameAsync(string directory)
{
var saveGameFile = CreatePath(directory, Constants.SaveGames.SaveGameFileName);

var metaFileExists = await _fileManager.ExistsAsync(CreatePath(directory, Constants.SaveGames.MetaFileName));
var saveGameFileExists =
await _fileManager.ExistsAsync(CreatePath(directory, Constants.SaveGames.SaveGameFileName));
await _fileManager.ExistsAsync(saveGameFile);

return metaFileExists && saveGameFileExists;
var filesExist = metaFileExists && saveGameFileExists;

if (!filesExist)
{
return false;
}

var meta = await LoadMeta(directory);
var hash = SaveGameHash.Create(meta.Name, _fileManager.CreatePath(saveGameFile));

return meta.Hash == hash;
}

private async UniTask<SaveGameMeta> LoadMeta(string directory) =>
await _fileManager.ReadAsync<SaveGameMeta>(CreatePath(directory,
Constants.SaveGames.MetaFileName));

private string CreatePath(string directory, string fileName) => Path.Combine(CreatePath(directory), fileName);

private string CreatePath(string directory) => Path.Combine(Constants.SaveGames.DirectoryName, directory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ private async UniTask<SaveGameMeta> PrepareSaveGameAsync(bool isValid = true, st
name = name.Slugify();

var saveGamePath = Path.Combine(SaveGamesPath, name);
var metaPath = Path.Combine(saveGamePath, Constants.SaveGames.MetaFileName);

var meta = new SaveGameMeta()
{
Expand All @@ -58,18 +59,21 @@ private async UniTask<SaveGameMeta> PrepareSaveGameAsync(bool isValid = true, st
Directory = saveGamePath,
};

await fileManager.WriteAsync(Path.Combine(saveGamePath, Constants.SaveGames.MetaFileName), meta);
await fileManager.WriteAsync(metaPath, meta);

if (!isValid)
{
return meta;
}

await fileManager.WriteAsync(
Path.Combine(saveGamePath, Constants.SaveGames.SaveGameFileName), new SaveGameData()
{
LastLevel = null
});
var saveGameFilePath = Path.Combine(saveGamePath, Constants.SaveGames.SaveGameFileName);
await fileManager.WriteAsync(saveGameFilePath, new SaveGameData()
{
LastLevel = null
});

meta.Hash = SaveGameHash.Create(meta.Name, saveGameFilePath);
await fileManager.WriteAsync(metaPath, meta);

return meta;
}
Expand Down Expand Up @@ -164,5 +168,23 @@ private async UniTask<SaveGameMeta> PrepareSaveGameAsync(bool isValid = true, st
result.Should().BeTrue();
});

[UnityTest]
public IEnumerator LoadSaveGame_CannotLoadWhenFileWasTemperedWith() =>
UniTask.ToCoroutine(async () =>
{
var meta = await PrepareSaveGameAsync(name: "Unit Test");
// Temper with the file
await File.AppendAllLinesAsync(Path.Combine(meta.Directory, Constants.SaveGames.SaveGameFileName), new []{" "});
var sut = ScriptableObject.CreateInstance<SaveGameManagerSO>();
var saveGame = await sut.LoadSaveGameAsync(meta);
saveGame.Should().BeNull();
});


}
}

0 comments on commit 80e2d0f

Please sign in to comment.