Managed DPAPI Part II: ProtectedMemory

Last week (ok, really two weeks ago ....), I wrote about using DPAPI with Whidbey.  (You can find that post here: Managed DPAPI Part I: ProtectedData).  In addition to the ProtectedData class, Whidbey will also expose DPAPI through the ProtectedMemory class.  In this post I'll show some samples of using ProtectedMemory and compare it to ProtectedData to help in making decisions as to which one to use in your managed application.

ProtectedMemory is a wrapper around CryptProtectMemory and CryptUnprotectMemory.  The main difference between ProtectedMemory and ProtectedData is that if the computer is rebooted between calls to Protect and Unprotect, you won't be able to decrypt the data.  This means that if you need to encrypt some data and save it to a file or other persistent storage, you'll need to use ProtectedData instead.  However if the data you're encrypting is for use just while the application is running, ProtectedMemory is slightly easier to use, and is a good choice.

Like the ProtectedData class, ProtectedMemory contains two static methods Protect and Unprotect.  Since there's no MSDN docs for Whidbey yet, I'll provide their signatures here:

void Protect(byte[] userData, MemoryProtectionScope scope)
userData memory area to encrypt
scope what applications can decrypt

The corresponding Unprotect method is:

void Unprotect(byte[] encryptedData, MemoryProtectionScope scope)
encryptedData data to decrypt
scope must match scope from call to Protect

As you can see, ProtectedMemory is even simpler than ProtectedData.  The encrypted data overwrites the original data, so you don't have to worry about destroying your original array.  Also, there's no entropy to deal with in these APIs.  The scope parameter has one of the following three values:

MemoryProtectionScope.SameProcess Only the current process will be able to decrypt the data
MemoryProtectionScope.CrossProcess Applications running in different processes can decrypt the data
MemoryProtectionScope.SameLogon Applications running in different processes, but running as the same user, can decrypt the data.

Generally you'll want to use MemoryProtectionScope.SameProcess.  When unprotecting your data, you'll need to provide the same MemoryProtectionScope, otherwise the decryption won't work.  Also, if you're writing a server application that recieves some data from a user application (say encrypted with MemoryProtectionScope.SameLogon), you'll need to impersonate the user that the other process is running as before decrypting.

One slight gotchya that exists with ProtectedMemory is that it will only work on blocks of 16 bytes, so you may have to pad your array before using it.  Here's some sample code:

byte[] sensitiveData = new byte[]
{
    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
    0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10
};

Console.WriteLine("Before protection:");
PrintBytes(sensitiveData); // utility function that simply prints out the byte array -- not part of the framework
        
ProtectedMemory.Protect(sensitiveData, MemoryProtectionScope.SameProcess);

Console.WriteLine("After protection:");
PrintBytes(sensitiveData);

ProtectedMemory.Unprotect(sensitiveData, MemoryProtectionScope.SameProcess);
        
Console.WriteLine("After unprotection:");
PrintBytes(sensitiveData)

The output from this code is:

Before protection:
01, 02, 03, 04, 05, 06, 07, 08, 09, 0A
0B, 0C, 0D, 0E, 0F, 10
After protection:
16, 9A, B1, A7, 3E, 09, 1B, 5F, FF, 30
B7, 64, 90, 4B, 15, FC
After unprotection:
01, 02, 03, 04, 05, 06, 07, 08, 09, 0A
0B, 0C, 0D, 0E, 0F, 10

There are some things to be aware of when using ProtectedMemory. Since your data needs to be decrypted for you to use it, there will be some window of time that it exists in plaintext in memory and can be found with a debugger.  During the time that your array is unencrypted, you're also vulnerable to some other problems.  Since managed arrays are under the control of the garbage collector, if they aren't pinned, they may be moved around memory.  In this case, even though you've protected your copy of the array, other unencrypted “images” of the sensitive data may still exist.  And of course, there's the possibility that Windows swaps your process out of memory into the swap file while your data is unencrypted.  In this case, an attacker could read the swap file and possibly find your unencrypted data.

ProtectedMemory makes it very easy to significantly raise the bar for an attacker to spy on your process and find some sensitive data.  Its use is very easy, and I recommend employing it in your Whidbey applications where you'll be holding information that you want hidden from prying eyes.  It's not perfect, but when used properly, attackers will have a significantly harder time obtaining sensitive information from your application.

As for choosing between ProtectedMemory and ProtectedData:

Data use ProtectedData ProtectedMemory System.Security.Cryptography algorithms
Written to a file x x
Stored in a database x x
Only used inside your application x x
Used in multiple applications, but not stored anywhere x x
Transmitted over the network x