Files

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);
}
}
}