black-box implementation of CryptProtectData/CryptUnprotectData

Kees Cook kees at outflux.net
Sun Apr 3 22:12:05 CDT 2005


This is my first patch to wine, so I hope I've followed all the correct 
conventions, etc.  :)  Please let me know if I should change anything 
for this patch.  It was not clear from the FAQ pages if I should 
actually add lines to ChangeLog or just put a short note in the email.  
I opted to put them in the patch, to make it (hopefully) easier to 
apply.

This patch implements a functional replacement for crypt32.dll's 
CryptProtectData and CryptUnprotectData.  It does _not_ perform any 
encrypt/decryption, but rather tracks the cipher/entropy/plain triplets 
so that programs depending on the calls will operate correctly.

The real Win32 CryptProtectData and CryptUnprotectData functions are
meant to provide a mechanism for encrypting data on a machine where
other users of the system can't be trusted.  It is used in many examples
as a way to store username and password information to the registry, but
store it not in the clear.

The real encryption is symmetric, but the method is non-public. 
However, since it is keyed to the machine and the user, it is unlikely
that the values would be portable from machine to machine, or into Wine
even if the encryption method was known.  Since programs must first call
CryptProtectData to get a cipher text, the underlying system just has to
track the plain/entropy/cipher triplet to be able to return the plain
text on a later call to CryptUnprotectData.

Again, these functions DO NOT encrypt the data, but rather, track the
calls in the registry, so that programs that rely on this mechanism will
still function.  Some programs refuse to run unless they can
successfully call CryptProtectData and CryptUnprotectData, getting the
expected values back.

To store the triplets, these functions use the registry:

Registry Layout:
    HKEY_CURRENT_USER\Software\Wine\Crypt\ProtectData\Map\[index]
        Cipher:          HEX string
        Entropy:         HEX string
        DataDescription: WCHAR
        Plain:           HEX string

If a call is made to CryptUnprotectData for an unknown cipher/entropy
pair, a new registry entry is created with a plain text of "[unknown]".

I've found this implementation quite useful, so I'm happy to make 
whatever changes are needed to get it put into wine officially.  :)

Thanks!

-- 
Kees Cook                                            @outflux.net
-------------- next part --------------
? dlls/crypt32/protectdata.c
Index: ChangeLog
===================================================================
RCS file: /home/wine/wine/ChangeLog,v
retrieving revision 1.92
diff -u -p -u -p -r1.92 ChangeLog
--- ChangeLog	10 Mar 2005 20:10:32 -0000	1.92
+++ ChangeLog	4 Apr 2005 03:00:09 -0000
@@ -1,4 +1,10 @@
 ----------------------------------------------------------------
+2005-04-03  Kees Cook  <kees at outflux.net>
+
+	* dlls/crypt32/Makefile.in, dlls/crypt32/crypt32.spec,
+	  dlls/crypt32/main.c, dlls/crypt32/protectdata.c:
+	Added black-box implementation of CryptProtectData/CryptUnprotectData.
+  
 2005-03-10  Alexandre Julliard  <julliard at winehq.com>
 
 	* dlls/shdocvw/shdocvw_main.c: Juan Lang <juan_lang at yahoo.com>
Index: dlls/crypt32/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/crypt32/Makefile.in,v
retrieving revision 1.8
diff -u -p -u -p -r1.8 Makefile.in
--- dlls/crypt32/Makefile.in	1 Mar 2004 21:19:37 -0000	1.8
+++ dlls/crypt32/Makefile.in	4 Apr 2005 03:00:09 -0000
@@ -8,6 +8,7 @@ IMPORTS   = advapi32 kernel32
 
 C_SRCS = \
 	cert.c \
+	protectdata.c \
 	main.c
 
 @MAKE_DLL_RULES@
Index: dlls/crypt32/crypt32.spec
===================================================================
RCS file: /home/wine/wine/dlls/crypt32/crypt32.spec,v
retrieving revision 1.19
diff -u -p -u -p -r1.19 crypt32.spec
--- dlls/crypt32/crypt32.spec	10 Nov 2004 01:31:50 -0000	1.19
+++ dlls/crypt32/crypt32.spec	4 Apr 2005 03:00:10 -0000
@@ -133,6 +133,7 @@
 @ stub CryptMsgUpdate
 @ stub CryptMsgVerifyCountersignatureEncoded
 @ stdcall CryptProtectData(ptr wstr ptr ptr ptr long ptr)
+@ stdcall CryptUnprotectData(ptr ptr ptr ptr ptr long ptr)
 @ stdcall CryptRegisterDefaultOIDFunction(long str long wstr)
 @ stdcall CryptRegisterOIDFunction(long str str wstr str)
 @ stub CryptRegisterOIDInfo
@@ -149,7 +150,6 @@
 @ stub CryptSignHashU
 @ stub CryptSignMessage
 @ stub CryptSignMessageWithKey
-@ stub CryptUnprotectData
 @ stub CryptUnregisterDefaultOIDFunction
 @ stub CryptUnregisterOIDFunction
 @ stub CryptUnregisterOIDInfo
Index: dlls/crypt32/main.c
===================================================================
RCS file: /home/wine/wine/dlls/crypt32/main.c,v
retrieving revision 1.19
diff -u -p -u -p -r1.19 main.c
--- dlls/crypt32/main.c	30 Nov 2004 21:39:00 -0000	1.19
+++ dlls/crypt32/main.c	4 Apr 2005 03:00:10 -0000
@@ -56,14 +56,6 @@ BOOL WINAPI I_CryptFreeLruCache(DWORD x)
     return FALSE;
 }
 
-BOOL WINAPI CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy,
-                             PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
-                             DWORD dwFlags, DATA_BLOB* pDataOut)
-{
-    FIXME("stub!\n");
-    return FALSE;
-}
-
 BOOL WINAPI CryptSIPRemoveProvider(GUID *pgProv)
 {
     FIXME("stub!\n");
--- /dev/null	2005-02-11 19:52:11.491673144 -0800
+++ dlls/crypt32/protectdata.c	2005-04-03 19:53:11.837031779 -0700
@@ -0,0 +1,614 @@
+/*
+ * Copyright 2005 Kees Cook <kees at outflux.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "config.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wincrypt.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "mssip.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(crypt);
+
+#define CRYPT_MAX_REGISTRY_NAME_LENGTH 255
+static const WCHAR wszProtectDataMap[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','C','r','y','p','t','\\','P','r','o','t','e','c','t','D','a','t','a','\\','M','a','p',0};
+static const WCHAR wszCipher[] = {'C','i','p','h','e','r',0};
+static const WCHAR wszPlain[] = {'P','l','a','i','n',0};
+static const WCHAR wszEntropy[] = {'E','n','t','r','o','p','y',0};
+static const WCHAR wszDataDescr[] = {'D','a','t','a','D','e','s','c','r',0};
+
+/*
+ * The Win32 CryptProtectData and CryptUnprotectData functions are meant
+ * to provide a mechanism for encrypting data on a machine where other users
+ * of the system can't be trusted.  It is used in many examples as a way
+ * to store username and password information to the registry, but store
+ * it not in the clear.
+ *
+ * The encryption is symmetric, but the method is unknown.  However, since
+ * it is keyed to the machine and the user, it is unlikely that the values
+ * would be portable.  Since programs must first call CryptProtectData to
+ * get a cipher text, the underlying system just has to track the
+ * plain/entropy/cipher triplet to be able to return the plain text on a
+ * later call to CryptUnprotectData.
+ *
+ * These functions DO NOT encrypt the data, but rather, track the calls
+ * in the registry, so that programs that rely on this mechanism will
+ * still function.  Some programs refuse to run unless they can successfully
+ * call CryptProtectData and CryptUnprotectData, getting the expected values
+ * back.
+ *
+ * To store the triplets, these functions use the registry:
+ *
+ * Registry Layout:
+ *     HKEY_CURRENT_USER\Software\Wine\Crypt\ProtectData\Map\[index]
+ *         Cipher:          HEX string
+ *         Entropy:         HEX string
+ *         DataDescription: WCHAR
+ *         Plain:           HEX string
+ *
+ * If a call is made to CryptUnprotectData for an unknown cipher/entropy
+ * pair, a new registry entry is created with a plain text of "[unknown]".
+ *
+ */
+
+
+
+/* 
+ * Top-level searching function, which uses a callback to evaluate
+ * if a given registry item matches.  If the callback returns TRUE,
+ * this function stores the dwMaxValueLen & hkey, and returns TRUE.
+ * It is up to the caller to close the open registry item hkey.
+ */
+static BOOL
+crypt_search_registry(HKEY *phkeyOut,
+                      DWORD *pMaxValueLen,
+                      BOOL (*reg_match_callback)(HKEY hkey,
+                                                  DWORD maxValueLen,
+                                                 DATA_BLOB *pDataMatch,
+                                                 DATA_BLOB *pOptionalEntropy),
+                      DATA_BLOB *pDataToMatch,
+                      DATA_BLOB *pOptionalEntropy)
+{
+    HKEY hkeyMap = NULL;
+    HRESULT hr;
+    BOOL bFound = FALSE;
+    DWORD dwIndex;
+
+    TRACE("called\n");
+
+    if (!pDataToMatch || !phkeyOut || !reg_match_callback)
+        return FALSE;
+
+    hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_CURRENT_USER, wszProtectDataMap, 0, KEY_READ, &hkeyMap));
+    if (!SUCCEEDED(hr))
+    {
+        ERR("ProtectData registry not found\n");
+        return FALSE;
+    }        
+
+    for (dwIndex = 0; !bFound; dwIndex++)
+    {
+        HKEY hkeyIndex;
+        WCHAR wszIndexKey[CRYPT_MAX_REGISTRY_NAME_LENGTH];
+        DWORD dwIndexKeyLength=CRYPT_MAX_REGISTRY_NAME_LENGTH;
+        DWORD dwMaxValueLen;
+
+        if (RegEnumKeyExW(hkeyMap, dwIndex, wszIndexKey, &dwIndexKeyLength, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
+            break;
+        if (RegOpenKeyExW(hkeyMap, wszIndexKey, 0, KEY_READ, &hkeyIndex) != ERROR_SUCCESS)
+            break;
+
+        if (WINE_TRACE_ON(crypt))
+            wine_dbg_printf("\tChecking Map Index %s\n", debugstr_w(wszIndexKey));
+
+        if (RegQueryInfoKeyW(hkeyIndex, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwMaxValueLen, NULL, NULL) != ERROR_SUCCESS)
+        {
+            CloseHandle(hkeyIndex);
+            break;
+        }
+
+        if (reg_match_callback(hkeyIndex,dwMaxValueLen,
+                               pDataToMatch,pOptionalEntropy))
+        {
+            bFound=TRUE;
+            if (phkeyOut) *phkeyOut=hkeyIndex;
+            if (pMaxValueLen) *pMaxValueLen=dwMaxValueLen;
+        }
+        else
+        {
+            CloseHandle(hkeyIndex);
+        }
+    }
+
+    if (!bFound) {
+        TRACE("no matches\n");
+    }
+
+    CloseHandle(hkeyMap);
+    return bFound;
+}
+
+
+/*
+ * Utility function for matching Entropy against a given registry item.
+ */
+static BOOL
+crypt_reg_match_entropy(HKEY hkeyIndex,
+                    DWORD maxValueLen,
+                    DATA_BLOB *pOptionalEntropy)
+{
+    /* assume that lack of Entropy means "match" */
+    if (pOptionalEntropy) {
+        BYTE * pbValue;
+        DWORD dwValueLen;
+
+        dwValueLen = maxValueLen;
+        pbValue = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
+
+        if (RegQueryValueExW(hkeyIndex, wszEntropy, NULL, NULL, pbValue, &dwValueLen) != ERROR_SUCCESS)
+        {
+            HeapFree(GetProcessHeap(), 0, pbValue);
+            return FALSE;
+        }
+    
+        /* Does the Entropy match? */
+        if (dwValueLen!=pOptionalEntropy->cbData ||
+            memcmp(pOptionalEntropy->pbData,pbValue,dwValueLen))
+        {
+            HeapFree(GetProcessHeap(), 0, pbValue);
+            return FALSE;
+        }
+    }
+
+    if (WINE_TRACE_ON(crypt))
+        wine_dbg_printf("\tEntropy matched\n");
+    return TRUE;
+}
+
+/*
+ * registry matching callback for plain-text searches.  This is used
+ * to find already "encrypted" items.
+ */
+static BOOL crypt_reg_callback_match_plain(HKEY hkeyIndex,
+                                           DWORD maxValueLen,
+                                            DATA_BLOB *pDataMatch,
+                                            DATA_BLOB *pOptionalEntropy)
+{
+    BYTE * pbValue;
+    DWORD dwValueLen;
+
+    if (!pDataMatch)
+        return FALSE;
+
+    pbValue = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
+
+    dwValueLen = maxValueLen;
+    if (RegQueryValueExW(hkeyIndex, wszPlain, NULL, NULL, pbValue, &dwValueLen) != ERROR_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(), 0, pbValue);
+        return FALSE;
+    }
+
+    /* Does the Plain match? */
+    if (dwValueLen!=pDataMatch->cbData ||
+        memcmp(pDataMatch->pbData,pbValue,dwValueLen))
+    {
+        HeapFree(GetProcessHeap(), 0, pbValue);
+        return FALSE;
+    }
+    if (WINE_TRACE_ON(crypt))
+        wine_dbg_printf("\tPlain matched\n");
+
+    HeapFree(GetProcessHeap(), 0, pbValue);
+
+    if (!crypt_reg_match_entropy(hkeyIndex,maxValueLen,pOptionalEntropy))
+        return FALSE;
+
+    return TRUE;
+}
+
+/*
+ * registry matching callback for cipher-text searches.  This is used
+ * to find "encrypted" items based on their cipher text.
+ * One thing different here is that cipher may sometimes be NULL padded
+ * if they are unexpectedly small (<256 bytes).
+ */
+static BOOL crypt_reg_callback_match_cipher(HKEY hkeyIndex,
+                                            DWORD maxValueLen,
+                                            DATA_BLOB *pDataMatch,
+                                            DATA_BLOB *pOptionalEntropy)
+{
+    BYTE * pbValue;
+    DWORD dwValueLen;
+
+    if (!pDataMatch)
+        return FALSE;
+
+    /* Need to NULL-pad ciphers */
+    if (maxValueLen<pDataMatch->cbData)
+        maxValueLen=pDataMatch->cbData;
+    pbValue = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
+
+    dwValueLen = maxValueLen;
+    if (RegQueryValueExW(hkeyIndex, wszCipher, NULL, NULL, pbValue, &dwValueLen) != ERROR_SUCCESS)
+    {
+        HeapFree(GetProcessHeap(), 0, pbValue);
+        return FALSE;
+    }
+    /* Perform NULL padding */
+    for (; dwValueLen<pDataMatch->cbData; dwValueLen++)
+        pbValue[dwValueLen]=0;
+
+    /* Does the Cipher match? */
+    if (dwValueLen!=pDataMatch->cbData ||
+        memcmp(pDataMatch->pbData,pbValue,dwValueLen))
+    {
+        HeapFree(GetProcessHeap(), 0, pbValue);
+        return FALSE;
+    }
+
+    if (WINE_TRACE_ON(crypt))
+        wine_dbg_printf("\tCipher matched\n");
+
+    HeapFree(GetProcessHeap(), 0, pbValue);
+
+    if (!crypt_reg_match_entropy(hkeyIndex,maxValueLen,pOptionalEntropy))
+        return FALSE;
+
+    return TRUE;
+}
+
+static BOOL
+crypt_find_cipher_by_plain(DATA_BLOB* pDataOut, DATA_BLOB* pDataIn,
+                   DATA_BLOB* pOptionalEntropy, LPCWSTR szDataDescr)
+{
+    HKEY hkeyIndex;
+    DWORD maxValueLen;
+    DWORD dwValueLen;
+    BYTE * pbValue;
+    BOOL result=FALSE;
+
+    TRACE("called\n");
+
+    if (!crypt_search_registry(&hkeyIndex,&maxValueLen,
+                               crypt_reg_callback_match_plain,
+                               pDataIn,pOptionalEntropy))
+    {
+        return result;
+    }
+    /* found the registry entry */
+
+       pbValue = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
+
+    dwValueLen = maxValueLen;
+    if (RegQueryValueExW(hkeyIndex, wszCipher, NULL, NULL, pbValue, &dwValueLen) == ERROR_SUCCESS)
+    {
+        /* make a copy of the cipher text */
+        pDataOut->cbData=dwValueLen;
+        pDataOut->pbData=LocalAlloc(LPTR,pDataOut->cbData);
+        memcpy(pDataOut->pbData,pbValue,pDataOut->cbData);
+
+        result=TRUE;
+    }
+    else {
+        ERR("\tCipher value not found\n");
+    }
+
+    HeapFree(GetProcessHeap(), 0, pbValue);
+    CloseHandle(hkeyIndex);
+    return result;
+}
+
+static BOOL
+crypt_find_plain_by_cipher(DATA_BLOB* pDataOut, LPWSTR *ppszDataDescr,
+                   DATA_BLOB* pDataIn, DATA_BLOB* pOptionalEntropy)
+{
+    HKEY hkeyIndex;
+    DWORD maxValueLen;
+    DWORD dwValueLen;
+    BYTE * pbValue;
+    BOOL result=FALSE;
+
+    TRACE("called\n");
+
+    if (!crypt_search_registry(&hkeyIndex,&maxValueLen,
+                               crypt_reg_callback_match_cipher,
+                               pDataIn,pOptionalEntropy))
+    {
+        return result;
+    }
+    /* found the registry entry */
+
+       pbValue = HeapAlloc(GetProcessHeap(), 0, maxValueLen);
+
+    dwValueLen = maxValueLen;
+    if (RegQueryValueExW(hkeyIndex, wszPlain, NULL, NULL, pbValue, &dwValueLen) == ERROR_SUCCESS)
+    {
+        /* copy the plain text */
+        pDataOut->cbData=dwValueLen;
+        pDataOut->pbData=LocalAlloc(LPTR,pDataOut->cbData);
+        memcpy(pDataOut->pbData,pbValue,pDataOut->cbData);
+
+        /* copy the DataDescription */
+        if (ppszDataDescr)
+        {
+            dwValueLen = maxValueLen;
+            if (RegQueryValueExW(hkeyIndex, wszDataDescr, NULL, NULL, pbValue, &dwValueLen) != ERROR_SUCCESS)
+            {
+                /* if we don't have one stored, just return an empty string */
+                dwValueLen=2;
+                pbValue[0]='\0';
+            }
+
+            *ppszDataDescr=LocalAlloc(LPTR,dwValueLen);
+            memcpy(*ppszDataDescr,pbValue,dwValueLen);
+        }
+
+        result = TRUE;
+    }
+    else
+    {
+        ERR("\tPlain value not found\n");
+    }
+
+    HeapFree(GetProcessHeap(), 0, pbValue);
+    CloseHandle(hkeyIndex);
+    return result;
+}
+
+/*
+ * Creates a registry value, falling back to a string default.
+ * If fallback happens, the stored value is written to pDataOut
+ * if it isn't NULL
+ */
+static BOOL
+crypt_write_registry_value(HKEY hkeyOpen,
+                           const WCHAR * wszName,
+                           DWORD dwType,
+                           DATA_BLOB *pDataIn,
+                           WCHAR * wszDefault,
+                           DATA_BLOB *pDataOut)
+{
+    DATA_BLOB pData;
+
+    if (pDataIn)
+    {
+        pData.pbData=pDataIn->pbData;
+        pData.cbData=pDataIn->cbData;
+    }
+    else
+    {
+        pData.pbData=(void*)wszDefault;
+        pData.cbData=(lstrlenW(wszDefault)+1)*sizeof(WCHAR);
+
+        if (pDataOut)
+        {
+            pDataOut->cbData=pData.cbData;
+            pDataOut->pbData=LocalAlloc(LPTR,pDataOut->cbData);
+            memcpy(pDataOut->pbData,pData.pbData,pData.cbData);
+        }
+    }
+
+    return SUCCEEDED(RegSetValueExW(hkeyOpen,wszName,0,dwType,
+                                    pData.pbData,pData.cbData));
+}
+
+static BOOL
+crypt_create_registry_item(DATA_BLOB *pDataCipher,
+                           DATA_BLOB *pDataPlain,
+                           DATA_BLOB *pOptionalEntropy,
+                           WCHAR * szDataDescr,
+                           DATA_BLOB *pDataOut)
+{
+    HRESULT hr;
+    BOOL okay=TRUE;
+    BOOL bFound=FALSE;
+    HKEY hkeyMap;
+    DWORD dwIndex;
+    HKEY hkeyOpen;
+    WCHAR wszIndexKey[CRYPT_MAX_REGISTRY_NAME_LENGTH];
+    WCHAR wszUnknown[] = {'[','u','n','k','n','o','w','n',']',0};
+
+    hr = HRESULT_FROM_WIN32(RegOpenKeyExW(HKEY_CURRENT_USER, wszProtectDataMap, 0, KEY_ALL_ACCESS, &hkeyMap));
+    if (!SUCCEEDED(hr))
+    {
+        ERR("ProtectData registry not found\n");
+        return FALSE;
+    }        
+
+    /* set our search limit to 1024, arbitrarily */
+    for (dwIndex = 0; !bFound && dwIndex<1024; dwIndex++)
+    {
+        int i, len;
+        char buf[CRYPT_MAX_REGISTRY_NAME_LENGTH];
+        sprintf(buf,"%u",(unsigned int)dwIndex);
+        len=strlen(buf)+1;
+        for (i=0; i<len; i++) {
+            wszIndexKey[i]=buf[i];
+        }
+
+        DWORD dwDisposition;
+        hr = HRESULT_FROM_WIN32(RegCreateKeyExW(hkeyMap, wszIndexKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hkeyOpen, &dwDisposition));
+        if (!SUCCEEDED(hr))
+           continue;
+        if (dwDisposition == REG_OPENED_EXISTING_KEY)
+        {
+            /* already exists, skip */
+            CloseHandle(hkeyOpen);
+            continue;
+        }
+
+        bFound=TRUE;
+    }
+    CloseHandle(hkeyMap);
+
+    if (!bFound)
+    {
+        ERR("Cannot find room for new ProtectData registry item\n");
+        return FALSE;
+    }
+
+    if (!crypt_write_registry_value(hkeyOpen, wszCipher, REG_BINARY,
+                               pDataCipher, wszIndexKey, pDataOut))
+        okay=FALSE;
+    if (!crypt_write_registry_value(hkeyOpen, wszPlain, REG_BINARY,
+                               pDataPlain, wszUnknown, pDataOut))
+        okay=FALSE;
+    if (szDataDescr)
+    {
+        if (!crypt_write_registry_value(hkeyOpen, wszDataDescr, REG_SZ,
+                                   NULL, szDataDescr, NULL))
+            okay=FALSE;
+    }
+    if (pOptionalEntropy)
+    {
+        if (!crypt_write_registry_value(hkeyOpen, wszEntropy, REG_BINARY,
+                                   pOptionalEntropy, NULL, NULL))
+            okay=FALSE;
+    }
+
+    CloseHandle(hkeyOpen);
+    return okay;
+}
+
+
+static BOOL
+crypt_create_cipher_from_plain(DATA_BLOB* pDataOut, DATA_BLOB* pDataIn,
+                       DATA_BLOB* pOptionalEntropy, LPCWSTR szDataDescr)
+{
+    TRACE("called\n");
+
+    return crypt_create_registry_item(NULL,pDataIn,pOptionalEntropy,(WCHAR*)szDataDescr,pDataOut);
+}
+
+static void
+crypt_create_unknown_from_cipher(DATA_BLOB * pDataIn,
+                         DATA_BLOB* pOptionalEntropy)
+{
+    TRACE("called\n");
+
+    crypt_create_registry_item(pDataIn,NULL,pOptionalEntropy,NULL,NULL);
+}
+
+static int
+hexprint(const char *s, unsigned char *p, int n)
+{
+    char report[80];
+    int r=-1;
+    snprintf(report,16,"%14s:", s);
+    while (--n >= 0) {
+        if (r++ % 20 == 19)
+        {
+            wine_dbg_printf("%s\n",report);
+            snprintf(report,16,"%14s ", "");
+        }
+        sprintf(report+strlen(report)," %02x", *p++);
+    }
+    wine_dbg_printf("%s\n",report);
+    return 0;
+}
+
+static void
+crypt_report_func_input(DATA_BLOB* pDataIn,
+            DATA_BLOB* pOptionalEntropy,
+                CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
+            DWORD dwFlags)
+{
+    wine_dbg_printf("\tpPromptStruct: 0x%x\n",(unsigned int)pPromptStruct);
+    if (pPromptStruct)
+    {
+        wine_dbg_printf("\t\tcbSize: 0x%x\n",(unsigned int)pPromptStruct->cbSize);
+        wine_dbg_printf("\t\tdwPromptFlags: 0x%x\n",(unsigned int)pPromptStruct->dwPromptFlags);
+        wine_dbg_printf("\t\thwndApp: 0x%x\n",(unsigned int)pPromptStruct->hwndApp);
+        wine_dbg_printf("\t\tszPrompt: 0x%x %s\n",
+                (unsigned int)pPromptStruct->szPrompt,
+                pPromptStruct->szPrompt ? debugstr_w(pPromptStruct->szPrompt)
+                : "");
+    }
+    wine_dbg_printf("\tdwFlags: 0x%04x\n",(unsigned int)dwFlags);
+    wine_dbg_printf("\tpDataIn->cbData: %u\n",(unsigned int)pDataIn->cbData);
+    hexprint("pbData", pDataIn->pbData, pDataIn->cbData);
+    if (pOptionalEntropy)
+    {
+        wine_dbg_printf("\tpOptionalEntropy->cbData: %u\n",(unsigned int)pOptionalEntropy->cbData);
+        hexprint("pbData", pOptionalEntropy->pbData, pOptionalEntropy->cbData);
+        wine_dbg_printf("\t\t%s\n",debugstr_an(pOptionalEntropy->pbData,pOptionalEntropy->cbData));
+    }
+
+}
+
+BOOL WINAPI CryptProtectData(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy,
+                             PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
+                             DWORD dwFlags, DATA_BLOB* pDataOut)
+{
+    TRACE("called\n");
+
+    if (WINE_TRACE_ON(crypt))
+    {
+        crypt_report_func_input(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
+        wine_dbg_printf("\tszDataDescr: 0x%x %s\n",(unsigned int)szDataDescr,
+                szDataDescr ? debugstr_w(szDataDescr) : "");
+    }
+
+    if (crypt_find_cipher_by_plain(pDataOut, pDataIn, pOptionalEntropy, szDataDescr))
+    {
+        return TRUE;
+    }
+    if (crypt_create_cipher_from_plain(pDataOut, pDataIn, pOptionalEntropy, szDataDescr))
+    {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+BOOL WINAPI CryptUnprotectData(DATA_BLOB* pDataIn, LPWSTR * ppszDataDescr, DATA_BLOB* pOptionalEntropy,
+                             PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
+                             DWORD dwFlags, DATA_BLOB* pDataOut)
+{
+    TRACE("called\n");
+
+    if (WINE_TRACE_ON(crypt)) {
+        crypt_report_func_input(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
+        wine_dbg_printf("\tppszDataDescr: 0x%x\n",(unsigned int)ppszDataDescr);
+    }
+
+    if (crypt_find_plain_by_cipher(pDataOut, ppszDataDescr, pDataIn, pOptionalEntropy))
+    {
+        if (WINE_TRACE_ON(crypt)) {
+            if (ppszDataDescr)
+            {
+                wine_dbg_printf("\tszDataDescr: %s\n",debugstr_w(*ppszDataDescr));
+            }
+            wine_dbg_printf("\tpDataOut->cbData: %u\n",(unsigned int)pDataOut->cbData);
+            hexprint("pbData", pDataOut->pbData, pDataOut->cbData);
+        }
+        return TRUE;
+    }
+    /* Record the cipher and entropy for the future */
+    crypt_create_unknown_from_cipher(pDataIn, pOptionalEntropy);
+
+    return FALSE;
+}
+
+


More information about the wine-patches mailing list