XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

HaloPixelToolBox

【WinUI3】基于USB HID通讯的花再音响工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace HaloPixelToolBox.Backend.Utilities;

/// <summary>
/// 使用 Windows DPAPI 加密仅允许当前 Windows 用户解密的本地数据。
/// </summary>
internal static partial class WindowsDataProtection
{
    private const int CryptProtectUiForbidden = 0x1;

    public static byte[] Protect(byte[] data, byte[] entropy) => Transform(data, entropy, protect: true);

    public static byte[] Unprotect(byte[] data, byte[] entropy) => Transform(data, entropy, protect: false);

    private static byte[] Transform(byte[] data, byte[] entropy, bool protect)
    {
        var inputBlob = CreateBlob(data);
        var entropyBlob = CreateBlob(entropy);
        DataBlob outputBlob = default;
        try
        {
            var succeeded = protect
                ? CryptProtectData(ref inputBlob, null, ref entropyBlob, IntPtr.Zero, IntPtr.Zero, CryptProtectUiForbidden, out outputBlob)
                : CryptUnprotectData(ref inputBlob, IntPtr.Zero, ref entropyBlob, IntPtr.Zero, IntPtr.Zero, CryptProtectUiForbidden, out outputBlob);

            if (!succeeded)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            var output = new byte[outputBlob.Size];
            Marshal.Copy(outputBlob.Data, output, 0, output.Length);
            return output;
        }
        finally
        {
            FreeInputBlob(ref inputBlob);
            FreeInputBlob(ref entropyBlob);
            if (outputBlob.Data != IntPtr.Zero)
                LocalFree(outputBlob.Data);
        }
    }

    private static DataBlob CreateBlob(byte[] data)
    {
        var blob = new DataBlob { Size = data.Length };
        if (data.Length == 0)
            return blob;

        blob.Data = Marshal.AllocHGlobal(data.Length);
        Marshal.Copy(data, 0, blob.Data, data.Length);
        return blob;
    }

    private static void FreeInputBlob(ref DataBlob blob)
    {
        if (blob.Data == IntPtr.Zero)
            return;

        Marshal.FreeHGlobal(blob.Data);
        blob = default;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct DataBlob
    {
        public int Size;
        public IntPtr Data;
    }

    [LibraryImport("Crypt32.dll", EntryPoint = "CryptProtectData", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static partial bool CryptProtectData(
        ref DataBlob dataIn,
        string? description,
        ref DataBlob optionalEntropy,
        IntPtr reserved,
        IntPtr promptStruct,
        int flags,
        out DataBlob dataOut);

    [LibraryImport("Crypt32.dll", EntryPoint = "CryptUnprotectData", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static partial bool CryptUnprotectData(
        ref DataBlob dataIn,
        IntPtr description,
        ref DataBlob optionalEntropy,
        IntPtr reserved,
        IntPtr promptStruct,
        int flags,
        out DataBlob dataOut);

    [LibraryImport("Kernel32.dll", EntryPoint = "LocalFree")]
    private static partial IntPtr LocalFree(IntPtr memory);
}