69 lines
2.0 KiB
C#
69 lines
2.0 KiB
C#
using System.Buffers.Binary;
|
|
using System.Security.Cryptography;
|
|
|
|
namespace WxAgent.Core;
|
|
|
|
public static class SqlCipherPageVerifier
|
|
{
|
|
public const int PageSize = 4096;
|
|
public const int SaltSize = 16;
|
|
public const int KeySize = 32;
|
|
public const int HmacSize = 64;
|
|
|
|
public static bool Verify(ReadOnlySpan<byte> page, ReadOnlySpan<byte> key, uint pageNumber = 1)
|
|
{
|
|
if (page.Length != PageSize || key.Length != KeySize || pageNumber == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Span<byte> hmacSalt = stackalloc byte[SaltSize];
|
|
for (var index = 0; index < SaltSize; index++)
|
|
{
|
|
hmacSalt[index] = (byte)(page[index] ^ 0x3a);
|
|
}
|
|
|
|
Span<byte> hmacKey = stackalloc byte[KeySize];
|
|
Rfc2898DeriveBytes.Pbkdf2(key, hmacSalt, hmacKey, 2, HashAlgorithmName.SHA512);
|
|
|
|
var authenticatedLength = PageSize - SaltSize - HmacSize;
|
|
var authenticated = new byte[authenticatedLength + sizeof(uint)];
|
|
page.Slice(SaltSize, authenticatedLength).CopyTo(authenticated);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(authenticated.AsSpan(authenticatedLength), pageNumber);
|
|
|
|
var computed = HMACSHA512.HashData(hmacKey, authenticated);
|
|
var valid = CryptographicOperations.FixedTimeEquals(computed, page[^HmacSize..]);
|
|
CryptographicOperations.ZeroMemory(hmacKey);
|
|
CryptographicOperations.ZeroMemory(authenticated);
|
|
CryptographicOperations.ZeroMemory(computed);
|
|
return valid;
|
|
}
|
|
|
|
public static bool VerifyHexKey(ReadOnlySpan<byte> page, string hexKey, uint pageNumber = 1)
|
|
{
|
|
if (hexKey.Length != KeySize * 2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
byte[] key;
|
|
try
|
|
{
|
|
key = Convert.FromHexString(hexKey);
|
|
}
|
|
catch (FormatException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
return Verify(page, key, pageNumber);
|
|
}
|
|
finally
|
|
{
|
|
CryptographicOperations.ZeroMemory(key);
|
|
}
|
|
}
|
|
}
|