misc/Create-DPAPI-DLL.ps1

# May need to adjust paths when versions change, and this may need to be pasted into an interactive window.
# This will generate a DLL that can be used instead of a code block.


# download https://www.nuget.org/packages/Microsoft.CodeDom.Providers.DotNetCompilerPlatform/ and extract with 7-zip to a location, enter that location on the next line
$DotNetCodeDomLocation = 'C:\Utils\microsoft.codedom.providers.dotnetcompilerplatform'

# Next load it.
Add-Type -Path "$DotNetCodeDomLocation\lib\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll"

# using Invoke-Expression moves this class definition to runtime, so it will work after the add-type and the ps5 class interface implementation will succeed
# This uses the public interface ICompilerSettings instead of the private class CompilerSettings
Invoke-Expression -Command @"
class RoslynCompilerSettings : Microsoft.CodeDom.Providers.DotNetCompilerPlatform.ICompilerSettings
{
    [string] get_CompilerFullPath()
    {
        return "$DotNetCodeDomLocation\tools\Roslyn-4.1.0\csc.exe"
    }
    [int] get_CompilerServerTimeToLive()
    {
        return 10
    }
}
"@

$DotNetCodeDomProvider = [Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider]::new([RoslynCompilerSettings]::new())
$Code=@"
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
 
public static class DpapiNgUtil
{
    public static string ProtectBase64(string protectionDescriptor, string input)
    {
        byte[] output = Protect(protectionDescriptor, Encoding.UTF8.GetBytes(input));
        return Convert.ToBase64String(output);
    }
 
    public static string UnprotectBase64(string input)
    {
        byte[] bytes = Convert.FromBase64String(input);
        byte[] output = Unprotect(bytes);
        return Encoding.UTF8.GetString(output);
    }
     
    public static byte[] Protect(string protectionDescriptor, byte[] data)
    {
        using (NCryptProtectionDescriptorHandle handle = NCryptProtectionDescriptorHandle.Create(protectionDescriptor))
        {
            return Protect(handle, data);
        }
    }
 
    internal static byte[] Protect(NCryptProtectionDescriptorHandle descriptor, byte[] data)
    {
        uint cbProtectedBlob;
        LocalAllocHandle protectedBlobHandle;
        int status = NativeMethods.NCryptProtectSecret(descriptor, NativeMethods.NCRYPT_SILENT_FLAG, data, (uint)data.Length, IntPtr.Zero, IntPtr.Zero, out protectedBlobHandle, out cbProtectedBlob);
        if(status != 0)
        {
            throw new CryptographicException(status);
        }
 
        using (protectedBlobHandle)
        {
            byte[] retVal = new byte[cbProtectedBlob];
            Marshal.Copy(protectedBlobHandle.DangerousGetHandle(), retVal, 0, retVal.Length);
            return retVal;
        }
    }
 
    public static byte[] Unprotect(byte[] protectedData)
    {
        uint cbData;
        LocalAllocHandle dataHandle;
        int status = NativeMethods.NCryptUnprotectSecret(IntPtr.Zero, NativeMethods.NCRYPT_SILENT_FLAG, protectedData, (uint)protectedData.Length, IntPtr.Zero, IntPtr.Zero, out dataHandle, out cbData);
        if (status != 0)
        {
            throw new CryptographicException(status);
        }
 
        using (dataHandle)
        {
            byte[] retVal = new byte[cbData];
            Marshal.Copy(dataHandle.DangerousGetHandle(), retVal, 0, retVal.Length);
            return retVal;
        }
    }
}
 
internal class LocalAllocHandle : SafeHandle
{
    // Called by P/Invoke when returning SafeHandles
    private LocalAllocHandle() : base(IntPtr.Zero, ownsHandle: true) { }
 
    // Do not provide a finalizer - SafeHandle's critical finalizer will
    // call ReleaseHandle for you.
 
    public override bool IsInvalid
    {
        get { return handle == IntPtr.Zero; }
    }
 
    protected override bool ReleaseHandle()
    {
        IntPtr retVal = NativeMethods.LocalFree(handle);
        return (retVal == IntPtr.Zero);
    }
}
 
internal class NCryptProtectionDescriptorHandle : SafeHandle
{
    // Called by P/Invoke when returning SafeHandles
    private NCryptProtectionDescriptorHandle() : base(IntPtr.Zero, ownsHandle: true) { }
 
    // Do not provide a finalizer - SafeHandle's critical finalizer will
    // call ReleaseHandle for you.
 
    public override bool IsInvalid
    {
        get { return handle == IntPtr.Zero; }
    }
 
    public static NCryptProtectionDescriptorHandle Create(string protectionDescriptor)
    {
        NCryptProtectionDescriptorHandle descriptorHandle;
        int status = NativeMethods.NCryptCreateProtectionDescriptor(protectionDescriptor, 0, out descriptorHandle);
        if (status != 0) {
            throw new CryptographicException(status);
        }
        return descriptorHandle;
    }
 
    protected override bool ReleaseHandle()
    {
        int retVal = NativeMethods.NCryptCloseProtectionDescriptor(handle);
        return (retVal == 0);
    }
}
 
internal static class NativeMethods
{
    private const string KERNEL32LIB = "kernel32.dll";
    private const string NCRYPTLIB = "ncrypt.dll";
 
    internal const uint NCRYPT_SILENT_FLAG = 0x00000040;
     
 
    // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366730(v=vs.85).aspx
    [DllImport(KERNEL32LIB, SetLastError = true)]
    internal static extern IntPtr LocalFree(
        [In] IntPtr handle);
 
    // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706799(v=vs.85).aspx
    [DllImport(NCRYPTLIB)]
    internal extern static int NCryptCloseProtectionDescriptor(
        [In] IntPtr hDescriptor);
 
    // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706800(v=vs.85).aspx
    [DllImport(NCRYPTLIB, CharSet = CharSet.Unicode)]
    internal extern static int NCryptCreateProtectionDescriptor(
        [In] string pwszDescriptorString,
        [In] uint dwFlags,
        [Out] out NCryptProtectionDescriptorHandle phDescriptor);
 
    // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706802(v=vs.85).aspx
    [DllImport(NCRYPTLIB)]
    internal extern static int NCryptProtectSecret(
        [In] NCryptProtectionDescriptorHandle hDescriptor,
        [In] uint dwFlags,
        [In] byte[] pbData,
        [In] uint cbData,
        [In] IntPtr pMemPara,
        [In] IntPtr hWnd,
        [Out] out LocalAllocHandle ppbProtectedBlob,
        [Out] out uint pcbProtectedBlob);
 
    // http://msdn.microsoft.com/en-us/library/windows/desktop/hh706811(v=vs.85).aspx
    [DllImport(NCRYPTLIB)]
    internal extern static int NCryptUnprotectSecret(
        [In] IntPtr phDescriptor,
        [In] uint dwFlags,
        [In] byte[] pbProtectedBlob,
        [In] uint cbProtectedBlob,
        [In] IntPtr pMemPara,
        [In] IntPtr hWnd,
        [Out] out LocalAllocHandle ppbData,
        [Out] out uint pcbData);
}
"@



Add-Type -CodeDomProvider $DotNetCodeDomProvider -TypeDefinition $Code -ReferencedAssemblies @([System.Reflection.Assembly]::GetAssembly([hashtable]).Location)
$DotNetAssemblyParameters = [System.CodeDom.Compiler.CompilerParameters]::New(
    @([System.reflection.assembly]::GetAssembly([hashtable]).Location),
    ".\dpapi-ng.dll", 
    $false
)
$compilationResults = $DotNetCodeDomProvider.CompileAssemblyFromSource(
    $DotNetAssemblyParameters,
    $Code
)