Config: Make DPAPI support optional
This commit is contained in:
parent
550fa92a53
commit
d1cb7e08f2
@ -9,6 +9,8 @@ namespace Pal.Client.Configuration
|
|||||||
{
|
{
|
||||||
public class AccountConfigurationV7 : IAccountConfiguration
|
public class AccountConfigurationV7 : IAccountConfiguration
|
||||||
{
|
{
|
||||||
|
private const int EntropyLength = 16;
|
||||||
|
|
||||||
[JsonConstructor]
|
[JsonConstructor]
|
||||||
public AccountConfigurationV7()
|
public AccountConfigurationV7()
|
||||||
{
|
{
|
||||||
@ -30,11 +32,7 @@ namespace Pal.Client.Configuration
|
|||||||
EncryptedId = accountId.Substring(2);
|
EncryptedId = accountId.Substring(2);
|
||||||
Entropy = ConfigurationData.FixedV1Entropy;
|
Entropy = ConfigurationData.FixedV1Entropy;
|
||||||
Format = EFormat.UseProtectedData;
|
Format = EFormat.UseProtectedData;
|
||||||
|
EncryptIfNeeded();
|
||||||
// try to migrate away from v1 entropy if possible
|
|
||||||
Guid? decryptedId = DecryptAccountId();
|
|
||||||
if (decryptedId != null)
|
|
||||||
(EncryptedId, Entropy, Format) = EncryptAccountId(decryptedId.Value);
|
|
||||||
}
|
}
|
||||||
else if (Guid.TryParse(accountId, out Guid guid))
|
else if (Guid.TryParse(accountId, out Guid guid))
|
||||||
(EncryptedId, Entropy, Format) = EncryptAccountId(guid);
|
(EncryptedId, Entropy, Format) = EncryptAccountId(guid);
|
||||||
@ -65,7 +63,7 @@ namespace Pal.Client.Configuration
|
|||||||
|
|
||||||
private Guid? DecryptAccountId()
|
private Guid? DecryptAccountId()
|
||||||
{
|
{
|
||||||
if (Format == EFormat.UseProtectedData)
|
if (Format == EFormat.UseProtectedData && ConfigurationData.SupportsDpapi)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -80,24 +78,31 @@ namespace Pal.Client.Configuration
|
|||||||
}
|
}
|
||||||
else if (Format == EFormat.Unencrypted)
|
else if (Format == EFormat.Unencrypted)
|
||||||
return Guid.Parse(EncryptedId);
|
return Guid.Parse(EncryptedId);
|
||||||
|
else if (Format == EFormat.ProtectedDataUnsupported && !ConfigurationData.SupportsDpapi)
|
||||||
|
return Guid.Parse(EncryptedId);
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private (string encryptedId, byte[]? entropy, EFormat format) EncryptAccountId(Guid g)
|
private (string encryptedId, byte[]? entropy, EFormat format) EncryptAccountId(Guid g)
|
||||||
{
|
{
|
||||||
try
|
if (!ConfigurationData.SupportsDpapi)
|
||||||
|
return (g.ToString(), null, EFormat.ProtectedDataUnsupported);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
byte[] entropy = RandomNumberGenerator.GetBytes(16);
|
try
|
||||||
byte[] guidBytes = ProtectedData.Protect(g.ToByteArray(), entropy, DataProtectionScope.CurrentUser);
|
{
|
||||||
return (Convert.ToBase64String(guidBytes), entropy, EFormat.UseProtectedData);
|
byte[] entropy = RandomNumberGenerator.GetBytes(EntropyLength);
|
||||||
}
|
byte[] guidBytes = ProtectedData.Protect(g.ToByteArray(), entropy, DataProtectionScope.CurrentUser);
|
||||||
catch (Exception)
|
return (Convert.ToBase64String(guidBytes), entropy, EFormat.UseProtectedData);
|
||||||
{
|
}
|
||||||
return (g.ToString(), null, EFormat.Unencrypted);
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return (g.ToString(), null, EFormat.Unencrypted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EncryptIfNeeded()
|
public bool EncryptIfNeeded()
|
||||||
{
|
{
|
||||||
if (Format == EFormat.Unencrypted)
|
if (Format == EFormat.Unencrypted)
|
||||||
@ -111,9 +116,7 @@ namespace Pal.Client.Configuration
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Format == EFormat.UseProtectedData && Entropy is { Length: < EntropyLength })
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
if (Format == EFormat.UseProtectedData && ConfigurationData.FixedV1Entropy.SequenceEqual(Entropy ?? Array.Empty<byte>()))
|
|
||||||
{
|
{
|
||||||
Guid? g = DecryptAccountId();
|
Guid? g = DecryptAccountId();
|
||||||
if (g != null)
|
if (g != null)
|
||||||
@ -122,7 +125,6 @@ namespace Pal.Client.Configuration
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -131,6 +133,12 @@ namespace Pal.Client.Configuration
|
|||||||
{
|
{
|
||||||
Unencrypted = 1,
|
Unencrypted = 1,
|
||||||
UseProtectedData = 2,
|
UseProtectedData = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for filtering: We don't want to overwrite any entries of this value using DPAPI, ever.
|
||||||
|
/// This is mostly a wine fallback.
|
||||||
|
/// </summary>
|
||||||
|
ProtectedDataUnsupported = 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using System;
|
using Dalamud.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
namespace Pal.Client.Configuration
|
namespace Pal.Client.Configuration
|
||||||
{
|
{
|
||||||
@ -6,5 +9,31 @@ namespace Pal.Client.Configuration
|
|||||||
{
|
{
|
||||||
[Obsolete("for V1 import")]
|
[Obsolete("for V1 import")]
|
||||||
internal static readonly byte[] FixedV1Entropy = { 0x22, 0x4b, 0xe7, 0x21, 0x44, 0x83, 0x69, 0x55, 0x80, 0x38 };
|
internal static readonly byte[] FixedV1Entropy = { 0x22, 0x4b, 0xe7, 0x21, 0x44, 0x83, 0x69, 0x55, 0x80, 0x38 };
|
||||||
|
|
||||||
|
private static bool? _supportsDpapi = null;
|
||||||
|
public static bool SupportsDpapi
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_supportsDpapi == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] input = RandomNumberGenerator.GetBytes(32);
|
||||||
|
byte[] entropy = RandomNumberGenerator.GetBytes(16);
|
||||||
|
byte[] temp = ProtectedData.Protect(input, entropy, DataProtectionScope.CurrentUser);
|
||||||
|
byte[] output = ProtectedData.Unprotect(temp, entropy, DataProtectionScope.CurrentUser);
|
||||||
|
_supportsDpapi = input.SequenceEqual(output);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
_supportsDpapi = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PluginLog.Verbose($"DPAPI support: {_supportsDpapi}");
|
||||||
|
}
|
||||||
|
return _supportsDpapi.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user