diff --git a/src/Core/Net/CertificatePinStore.cs b/src/Core/Net/CertificatePinStore.cs index 6fc2f1c..a61baca 100644 --- a/src/Core/Net/CertificatePinStore.cs +++ b/src/Core/Net/CertificatePinStore.cs @@ -58,10 +58,21 @@ public sealed class CertificatePinStore(string filePath) } } - private Dictionary Load() => - File.Exists(filePath) - ? JsonSerializer.Deserialize>(File.ReadAllText(filePath)) ?? [] - : []; + private Dictionary Load() + { + if (!File.Exists(filePath)) + return []; + try + { + return JsonSerializer.Deserialize>(File.ReadAllText(filePath)) ?? []; + } + catch (JsonException) + { + // A corrupt pin file must not make every SSL connection fail forever: + // fall back to first-contact TOFU (same trust level as the bootstrap). + return []; + } + } private void Save(Dictionary pins) { diff --git a/src/Core/Storage/EncryptedFile.cs b/src/Core/Storage/EncryptedFile.cs index f560637..5736b0e 100644 --- a/src/Core/Storage/EncryptedFile.cs +++ b/src/Core/Storage/EncryptedFile.cs @@ -26,13 +26,21 @@ public static class EncryptedFile try { using var doc = JsonDocument.Parse(fileContent); - return doc.RootElement.TryGetProperty("Format", out var f) + return doc.RootElement.ValueKind == JsonValueKind.Object + && doc.RootElement.TryGetProperty("Format", out var f) + && f.ValueKind == JsonValueKind.String && f.GetString() == Format; } catch (JsonException) { return false; } + catch (ArgumentException) + { + // Invalid UTF-16 (lone surrogates) cannot be transcoded for parsing: + // certainly not an encrypted container. + return false; + } } public static string Encrypt(string plaintext, string password)