客户端rsa,冗余清理,bug修复,安卓build问题
This commit is contained in:
@@ -19,6 +19,7 @@ public static class SecureSaveVault
|
||||
{
|
||||
private const string SecretSeed = "ban_total.secure_save_v2";
|
||||
private const string VaultDirectoryName = ".cache_bridge";
|
||||
private const string RecoveryDirectoryName = ".save_recovery";
|
||||
private static bool s_dpapiInitialized;
|
||||
private static bool s_dpapiSupported;
|
||||
private static MethodInfo s_dpapiProtectMethod;
|
||||
@@ -30,6 +31,11 @@ public static class SecureSaveVault
|
||||
get { return Path.Combine(SaveIdentityUtility.GetCanonicalPersistentRoot(), VaultDirectoryName); }
|
||||
}
|
||||
|
||||
private static string RecoveryDirectoryPath
|
||||
{
|
||||
get { return Path.Combine(SaveIdentityUtility.GetCanonicalPersistentRoot(), RecoveryDirectoryName); }
|
||||
}
|
||||
|
||||
public static bool SaveJson<T>(string category, string key, T data, string legacyPlainPath = null)
|
||||
{
|
||||
if (!typeof(T).IsValueType && (object)data == null)
|
||||
@@ -103,6 +109,7 @@ public static class SecureSaveVault
|
||||
File.Copy(tempPath, mainPath, true);
|
||||
TryHidePath(mainPath);
|
||||
File.Delete(tempPath);
|
||||
TrySaveRecoveryCopy(category, key, json);
|
||||
DeleteLegacyPlainFile(legacyPlainPath);
|
||||
return true;
|
||||
}
|
||||
@@ -126,6 +133,12 @@ public static class SecureSaveVault
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryLoadFromRecoveryCopies(category, key, out json))
|
||||
{
|
||||
SaveRawJson(category, key, json, legacyPlainPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(legacyPlainPath) && File.Exists(legacyPlainPath))
|
||||
{
|
||||
try
|
||||
@@ -236,6 +249,47 @@ public static class SecureSaveVault
|
||||
return Directory.GetFiles(categoryDirectory, "*.dat", SearchOption.TopDirectoryOnly).Length;
|
||||
}
|
||||
|
||||
public static bool HasAnyRecoverableState(string category, string key, string legacyPlainPath = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IReadOnlyList<string> roots = SaveIdentityUtility.GetPersistentRootVariants(legacyPlainPath);
|
||||
IReadOnlyList<string> identifierVariants = SaveIdentityUtility.GetApplicationIdentifierVariants();
|
||||
for (int rootIndex = 0; rootIndex < roots.Count; rootIndex++)
|
||||
{
|
||||
string root = roots[rootIndex];
|
||||
if (string.IsNullOrWhiteSpace(root))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int variantIndex = 0; variantIndex < identifierVariants.Count; variantIndex++)
|
||||
{
|
||||
string applicationIdentifier = identifierVariants[variantIndex];
|
||||
if (File.Exists(GetFilePathForRoot(root, category, key, ".dat", applicationIdentifier))
|
||||
|| File.Exists(GetFilePathForRoot(root, category, key, ".bak", applicationIdentifier)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IReadOnlyList<string> recoveryCandidates = GetRecoveryFilePathVariants(category, key);
|
||||
for (int i = 0; i < recoveryCandidates.Count; i++)
|
||||
{
|
||||
string path = recoveryCandidates[i];
|
||||
if (!string.IsNullOrWhiteSpace(path) && File.Exists(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(legacyPlainPath) && File.Exists(legacyPlainPath);
|
||||
}
|
||||
|
||||
private static bool TryReadEncryptedFile(string category, string key, string filePath, out string json)
|
||||
{
|
||||
json = null;
|
||||
@@ -616,6 +670,117 @@ public static class SecureSaveVault
|
||||
return Path.Combine(categoryDirectory, "." + safeKey + extension);
|
||||
}
|
||||
|
||||
private static bool TryLoadFromRecoveryCopies(string category, string key, out string json)
|
||||
{
|
||||
json = null;
|
||||
IReadOnlyList<string> candidates = GetRecoveryFilePathVariants(category, key);
|
||||
for (int i = 0; i < candidates.Count; i++)
|
||||
{
|
||||
string path = candidates[i];
|
||||
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string loaded = File.ReadAllText(path, Encoding.UTF8);
|
||||
if (string.IsNullOrWhiteSpace(loaded))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
json = loaded;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[SecureSaveVault] Recovery read failed ({path}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void TrySaveRecoveryCopy(string category, string key, string json)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(key) || json == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(RecoveryDirectoryPath);
|
||||
string recoveryPath = GetRecoveryFilePath(category, key);
|
||||
string backupPath = recoveryPath + ".bak";
|
||||
string tempPath = recoveryPath + ".tmp";
|
||||
|
||||
File.WriteAllText(tempPath, json, Encoding.UTF8);
|
||||
if (File.Exists(recoveryPath))
|
||||
{
|
||||
File.Copy(recoveryPath, backupPath, true);
|
||||
}
|
||||
|
||||
File.Copy(tempPath, recoveryPath, true);
|
||||
File.Delete(tempPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"[SecureSaveVault] Recovery mirror save failed ({category}/{key}): {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRecoveryFilePath(string category, string key)
|
||||
{
|
||||
string safeName = ShortHash("recovery|" + category + "|" + key);
|
||||
return Path.Combine(RecoveryDirectoryPath, safeName + ".json");
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> GetRecoveryFilePathVariants(string category, string key)
|
||||
{
|
||||
var result = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(category) || string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
string safeName = ShortHash("recovery|" + category + "|" + key) + ".json";
|
||||
string safeBackupName = safeName + ".bak";
|
||||
IReadOnlyList<string> roots = SaveIdentityUtility.GetPersistentRootVariants();
|
||||
for (int i = 0; i < roots.Count; i++)
|
||||
{
|
||||
string root = roots[i];
|
||||
if (string.IsNullOrWhiteSpace(root))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddDistinctPath(result, Path.Combine(root, RecoveryDirectoryName, safeName));
|
||||
AddDistinctPath(result, Path.Combine(root, RecoveryDirectoryName, safeBackupName));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void AddDistinctPath(List<string> target, string value)
|
||||
{
|
||||
if (target == null || string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < target.Count; i++)
|
||||
{
|
||||
if (string.Equals(target[i], value, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
target.Add(value);
|
||||
}
|
||||
|
||||
private static void DeleteLegacyPlainFile(string legacyPlainPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(legacyPlainPath) || !File.Exists(legacyPlainPath))
|
||||
|
||||
Reference in New Issue
Block a user