+++ /dev/null
-/*
- * Copyright 2005 Kees Cook <kees@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
- */
-
-
-/*
- * 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 doesn't have to exactly
- * match the real Windows version. However, attempts have been made to
- * at least try to look like the Windows version, including guesses at the
- * purpose of various portions of the "opaque data blob" that is used.
- *
- */
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "wincrypt.h"
-#include "winreg.h"
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(crypt);
-
-#define CRYPT32_PROTECTDATA_PROV PROV_RSA_FULL
-#define CRYPT32_PROTECTDATA_HASH_CALG CALG_MD5
-#define CRYPT32_PROTECTDATA_KEY_CALG CALG_RC2
-#define CRYPT32_PROTECTDATA_SALT_LEN 16
-
-static const BYTE crypt32_protectdata_secret[] = {
- 'I','\'','m',' ','h','u','n','t','i','n','g',' ',
- 'w','a','b','b','i','t','s',0
-};
-
-/*
- * The data format returned by the real Windows CryptProtectData seems
- * to be something like this:
-
- DWORD count0; - how many "info0_*[16]" blocks follow (was always 1)
- BYTE info0_0[16]; - unknown information
- ...
- DWORD count1; - how many "info1_*[16]" blocks follow (was always 1)
- BYTE info1_0[16]; - unknown information
- ...
- DWORD null0; - NULL "end of records"?
- DWORD str_len; - length of WCHAR string including term
- WCHAR str[str_len]; - The "dataDescription" value
- DWORD unknown0; - unknown value (seems large, but only WORD large)
- DWORD unknown1; - unknown value (seems small, less than a BYTE)
- DWORD data_len; - length of data (was 16 in samples)
- BYTE data[data_len]; - unknown data (fingerprint?)
- DWORD null1; - NULL ?
- DWORD unknown2; - unknown value (seems large, but only WORD large)
- DWORD unknown3; - unknown value (seems small, less than a BYTE)
- DWORD salt_len; - length of salt(?) data
- BYTE salt[salt_len]; - salt(?) for symmetric encryption
- DWORD cipher_len; - length of cipher(?) data - was close to plain len
- BYTE cipher[cipher_len]; - cipher text?
- DWORD crc_len; - length of fingerprint(?) data - was 20 byte==160b SHA1
- BYTE crc[crc_len]; - fingerprint of record?
-
- * The data structures used in Wine are modelled after this guess.
- */
-
-struct protect_data_t
-{
- DWORD count0;
- DATA_BLOB info0; /* using this to hold crypt_magic_str */
- DWORD count1;
- DATA_BLOB info1;
- DWORD null0;
- WCHAR * szDataDescr; /* serialized differently than the DATA_BLOBs */
- DWORD unknown0; /* perhaps the HASH alg const should go here? */
- DWORD unknown1;
- DATA_BLOB data0;
- DWORD null1;
- DWORD unknown2; /* perhaps the KEY alg const should go here? */
- DWORD unknown3;
- DATA_BLOB salt;
- DATA_BLOB cipher;
- DATA_BLOB fingerprint;
-};
-
-/* this is used to check if an incoming structure was built by Wine */
-static const char * crypt_magic_str = "Wine Crypt32 ok";
-
-/* debugging tool to print strings of hex chars */
-static const char *
-hex_str(unsigned char *p, int n)
-{
- const char * ptr;
- char report[80];
- int r=-1;
- report[0]='\0';
- ptr = wine_dbg_sprintf("%s","");
- while (--n >= 0)
- {
- if (r++ % 20 == 19)
- {
- ptr = wine_dbg_sprintf("%s%s",ptr,report);
- report[0]='\0';
- }
- sprintf(report+strlen(report),"%s%02x", r ? "," : "", *p++);
- }
- return wine_dbg_sprintf("%s%s",ptr,report);
-}
-
-#define TRACE_DATA_BLOB(blob) do { \
- TRACE("%s cbData: %u\n", #blob ,(unsigned int)((blob)->cbData)); \
- TRACE("%s pbData @ %p:%s\n", #blob ,(blob)->pbData, \
- hex_str((blob)->pbData, (blob)->cbData)); \
-} while (0)
-
-static
-void serialize_dword(DWORD value,BYTE ** ptr)
-{
- /*TRACE("called\n");*/
-
- memcpy(*ptr,&value,sizeof(DWORD));
- *ptr+=sizeof(DWORD);
-}
-
-static
-void serialize_string(BYTE * str,BYTE ** ptr,DWORD len, DWORD width,
- BOOL prepend_len)
-{
- /*TRACE("called %ux%u\n",(unsigned int)len,(unsigned int)width);*/
-
- if (prepend_len)
- {
- serialize_dword(len,ptr);
- }
- memcpy(*ptr,str,len*width);
- *ptr+=len*width;
-}
-
-static
-BOOL unserialize_dword(BYTE * ptr, DWORD *index, DWORD size, DWORD * value)
-{
- /*TRACE("called\n");*/
-
- if (!ptr || !index || !value) return FALSE;
-
- if (*index+sizeof(DWORD)>size)
- {
- return FALSE;
- }
-
- memcpy(value,&(ptr[*index]),sizeof(DWORD));
- *index+=sizeof(DWORD);
-
- return TRUE;
-}
-
-static
-BOOL unserialize_string(BYTE * ptr, DWORD *index, DWORD size,
- DWORD len, DWORD width, BOOL inline_len,
- BYTE ** data, DWORD * stored)
-{
- /*TRACE("called\n");*/
-
- if (!ptr || !data) return FALSE;
-
- if (inline_len) {
- if (!unserialize_dword(ptr,index,size,&len))
- return FALSE;
- }
-
- if (*index+len*width>size)
- {
- return FALSE;
- }
-
- if (!(*data = CryptMemAlloc( len*width)))
- {
- return FALSE;
- }
-
- memcpy(*data,&(ptr[*index]),len*width);
- if (stored)
- {
- *stored = len;
- }
- *index+=len*width;
-
- return TRUE;
-}
-
-static
-BOOL serialize(struct protect_data_t * pInfo, DATA_BLOB * pSerial)
-{
- BYTE * ptr;
- DWORD dwStrLen;
- DWORD dwStruct;
-
- TRACE("called\n");
-
- if (!pInfo || !pInfo->szDataDescr || !pSerial ||
- !pInfo->info0.pbData || !pInfo->info1.pbData ||
- !pInfo->data0.pbData || !pInfo->salt.pbData ||
- !pInfo->cipher.pbData || !pInfo->fingerprint.pbData)
- {
- return FALSE;
- }
-
- if (pInfo->info0.cbData!=16)
- {
- ERR("protect_data_t info0 not 16 bytes long\n");
- }
-
- if (pInfo->info1.cbData!=16)
- {
- ERR("protect_data_t info1 not 16 bytes long\n");
- }
-
- dwStrLen=lstrlenW(pInfo->szDataDescr);
-
- pSerial->cbData=0;
- pSerial->cbData+=sizeof(DWORD)*8; /* 8 raw DWORDs */
- pSerial->cbData+=sizeof(DWORD)*4; /* 4 BLOBs with size */
- pSerial->cbData+=pInfo->info0.cbData;
- pSerial->cbData+=pInfo->info1.cbData;
- pSerial->cbData+=(dwStrLen+1)*sizeof(WCHAR) + 4; /* str, null, size */
- pSerial->cbData+=pInfo->data0.cbData;
- pSerial->cbData+=pInfo->salt.cbData;
- pSerial->cbData+=pInfo->cipher.cbData;
- pSerial->cbData+=pInfo->fingerprint.cbData;
-
- /* save the actual structure size */
- dwStruct = pSerial->cbData;
- /* There may be a 256 byte minimum, but I can't prove it. */
- /*if (pSerial->cbData<256) pSerial->cbData=256;*/
-
- pSerial->pbData=LocalAlloc(LPTR,pSerial->cbData);
- if (!pSerial->pbData) return FALSE;
-
- ptr=pSerial->pbData;
-
- /* count0 */
- serialize_dword(pInfo->count0,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* info0 */
- serialize_string(pInfo->info0.pbData,&ptr,
- pInfo->info0.cbData,sizeof(BYTE),FALSE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* count1 */
- serialize_dword(pInfo->count1,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* info1 */
- serialize_string(pInfo->info1.pbData,&ptr,
- pInfo->info1.cbData,sizeof(BYTE),FALSE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* null0 */
- serialize_dword(pInfo->null0,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* szDataDescr */
- serialize_string((BYTE*)pInfo->szDataDescr,&ptr,
- (dwStrLen+1)*sizeof(WCHAR),sizeof(BYTE),TRUE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* unknown0 */
- serialize_dword(pInfo->unknown0,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
- /* unknown1 */
- serialize_dword(pInfo->unknown1,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* data0 */
- serialize_string(pInfo->data0.pbData,&ptr,
- pInfo->data0.cbData,sizeof(BYTE),TRUE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* null1 */
- serialize_dword(pInfo->null1,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* unknown2 */
- serialize_dword(pInfo->unknown2,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
- /* unknown3 */
- serialize_dword(pInfo->unknown3,&ptr);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* salt */
- serialize_string(pInfo->salt.pbData,&ptr,
- pInfo->salt.cbData,sizeof(BYTE),TRUE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* cipher */
- serialize_string(pInfo->cipher.pbData,&ptr,
- pInfo->cipher.cbData,sizeof(BYTE),TRUE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- /* fingerprint */
- serialize_string(pInfo->fingerprint.pbData,&ptr,
- pInfo->fingerprint.cbData,sizeof(BYTE),TRUE);
- /*TRACE("used %u\n",ptr-pSerial->pbData);*/
-
- if (ptr - pSerial->pbData != dwStruct)
- {
- ERR("struct size changed!? %u != expected %u\n",
- ptr - pSerial->pbData, (unsigned int)dwStruct);
- LocalFree(pSerial->pbData);
- pSerial->pbData=NULL;
- pSerial->cbData=0;
- return FALSE;
- }
-
- return TRUE;
-}
-
-static
-BOOL unserialize(DATA_BLOB * pSerial, struct protect_data_t * pInfo)
-{
- BYTE * ptr;
- DWORD index;
- DWORD size;
- BOOL status=TRUE;
-
- TRACE("called\n");
-
- if (!pInfo || !pSerial || !pSerial->pbData)
- return FALSE;
-
- index=0;
- ptr=pSerial->pbData;
- size=pSerial->cbData;
-
- /* count0 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->count0))
- {
- ERR("reading count0 failed!\n");
- return FALSE;
- }
-
- /* info0 */
- if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
- &pInfo->info0.pbData, &pInfo->info0.cbData))
- {
- ERR("reading info0 failed!\n");
- return FALSE;
- }
-
- /* count1 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->count1))
- {
- ERR("reading count1 failed!\n");
- return FALSE;
- }
-
- /* info1 */
- if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
- &pInfo->info1.pbData, &pInfo->info1.cbData))
- {
- ERR("reading info1 failed!\n");
- return FALSE;
- }
-
- /* null0 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->null0))
- {
- ERR("reading null0 failed!\n");
- return FALSE;
- }
-
- /* szDataDescr */
- if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
- (BYTE**)&pInfo->szDataDescr, NULL))
- {
- ERR("reading szDataDescr failed!\n");
- return FALSE;
- }
-
- /* unknown0 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->unknown0))
- {
- ERR("reading unknown0 failed!\n");
- return FALSE;
- }
-
- /* unknown1 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->unknown1))
- {
- ERR("reading unknown1 failed!\n");
- return FALSE;
- }
-
- /* data0 */
- if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
- &pInfo->data0.pbData, &pInfo->data0.cbData))
- {
- ERR("reading data0 failed!\n");
- return FALSE;
- }
-
- /* null1 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->null1))
- {
- ERR("reading null1 failed!\n");
- return FALSE;
- }
-
- /* unknown2 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->unknown2))
- {
- ERR("reading unknown2 failed!\n");
- return FALSE;
- }
-
- /* unknown3 */
- if (!unserialize_dword(ptr,&index,size,&pInfo->unknown3))
- {
- ERR("reading unknown3 failed!\n");
- return FALSE;
- }
-
- /* salt */
- if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
- &pInfo->salt.pbData, &pInfo->salt.cbData))
- {
- ERR("reading salt failed!\n");
- return FALSE;
- }
-
- /* cipher */
- if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
- &pInfo->cipher.pbData, &pInfo->cipher.cbData))
- {
- ERR("reading cipher failed!\n");
- return FALSE;
- }
-
- /* fingerprint */
- if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
- &pInfo->fingerprint.pbData, &pInfo->fingerprint.cbData))
- {
- ERR("reading fingerprint failed!\n");
- return FALSE;
- }
-
- /* allow structure size to be too big (since some applications
- * will pad this up to 256 bytes, it seems) */
- if (index>size)
- {
- /* this is an impossible-to-reach test, but if the padding
- * issue is ever understood, this may become more useful */
- ERR("loaded corrupt structure! (used %u expected %u)\n",
- (unsigned int)index, (unsigned int)size);
- status=FALSE;
- }
-
- return status;
-}
-
-/* perform sanity checks */
-static
-BOOL valid_protect_data(struct protect_data_t * pInfo)
-{
- BOOL status=TRUE;
-
- TRACE("called\n");
-
- if (pInfo->count0 != 0x0001)
- {
- ERR("count0 != 0x0001 !\n");
- status=FALSE;
- }
- if (pInfo->count1 != 0x0001)
- {
- ERR("count0 != 0x0001 !\n");
- status=FALSE;
- }
- if (pInfo->null0 != 0x0000)
- {
- ERR("null0 != 0x0000 !\n");
- status=FALSE;
- }
- if (pInfo->null1 != 0x0000)
- {
- ERR("null1 != 0x0000 !\n");
- status=FALSE;
- }
- /* since we have no idea what info0 is used for, and it seems
- * rather constant, we can test for a Wine-specific magic string
- * there to be reasonably sure we're using data created by the Wine
- * implementation of CryptProtectData.
- */
- if (pInfo->info0.cbData!=strlen(crypt_magic_str)+1 ||
- strcmp( (LPCSTR)pInfo->info0.pbData,crypt_magic_str) != 0)
- {
- ERR("info0 magic value not matched !\n");
- status=FALSE;
- }
-
- if (!status)
- {
- ERR("unrecognized CryptProtectData block\n");
- }
-
- return status;
-}
-
-static
-void free_protect_data(struct protect_data_t * pInfo)
-{
- TRACE("called\n");
-
- if (!pInfo) return;
-
- if (pInfo->info0.pbData)
- CryptMemFree(pInfo->info0.pbData);
- if (pInfo->info1.pbData)
- CryptMemFree(pInfo->info1.pbData);
- if (pInfo->szDataDescr)
- CryptMemFree(pInfo->szDataDescr);
- if (pInfo->data0.pbData)
- CryptMemFree(pInfo->data0.pbData);
- if (pInfo->salt.pbData)
- CryptMemFree(pInfo->salt.pbData);
- if (pInfo->cipher.pbData)
- CryptMemFree(pInfo->cipher.pbData);
- if (pInfo->fingerprint.pbData)
- CryptMemFree(pInfo->fingerprint.pbData);
-}
-
-/* copies a string into a data blob */
-static
-BYTE * convert_str_to_blob(char* str, DATA_BLOB* blob)
-{
- if (!str || !blob) return NULL;
-
- blob->cbData=strlen(str)+1;
- if (!(blob->pbData=CryptMemAlloc(blob->cbData)))
- {
- blob->cbData=0;
- }
- else {
- strcpy((LPSTR)blob->pbData, str);
- }
-
- return blob->pbData;
-}
-
-/*
- * Populates everything except "cipher" and "fingerprint".
- */
-static
-BOOL fill_protect_data(struct protect_data_t * pInfo, LPCWSTR szDataDescr,
- HCRYPTPROV hProv)
-{
- DWORD dwStrLen;
-
- TRACE("called\n");
-
- if (!pInfo) return FALSE;
-
- dwStrLen=lstrlenW(szDataDescr);
-
- memset(pInfo,0,sizeof(*pInfo));
-
- pInfo->count0=0x0001;
-
- convert_str_to_blob((char*)crypt_magic_str,&pInfo->info0);
-
- pInfo->count1=0x0001;
-
- convert_str_to_blob((char*)crypt_magic_str,&pInfo->info1);
-
- pInfo->null0=0x0000;
-
- if ((pInfo->szDataDescr=CryptMemAlloc((dwStrLen+1)*sizeof(WCHAR))))
- {
- memcpy(pInfo->szDataDescr,szDataDescr,(dwStrLen+1)*sizeof(WCHAR));
- }
-
- pInfo->unknown0=0x0000;
- pInfo->unknown1=0x0000;
-
- convert_str_to_blob((char*)crypt_magic_str,&pInfo->data0);
-
- pInfo->null1=0x0000;
- pInfo->unknown2=0x0000;
- pInfo->unknown3=0x0000;
-
- /* allocate memory to hold a salt */
- pInfo->salt.cbData=CRYPT32_PROTECTDATA_SALT_LEN;
- if ((pInfo->salt.pbData=CryptMemAlloc(pInfo->salt.cbData)))
- {
- /* generate random salt */
- if (!CryptGenRandom(hProv, pInfo->salt.cbData, pInfo->salt.pbData))
- {
- ERR("CryptGenRandom\n");
- free_protect_data(pInfo);
- return FALSE;
- }
- }
-
- /* debug: show our salt */
- TRACE_DATA_BLOB(&pInfo->salt);
-
- pInfo->cipher.cbData=0;
- pInfo->cipher.pbData=NULL;
-
- pInfo->fingerprint.cbData=0;
- pInfo->fingerprint.pbData=NULL;
-
- /* check all the allocations at once */
- if (!pInfo->info0.pbData ||
- !pInfo->info1.pbData ||
- !pInfo->szDataDescr ||
- !pInfo->data0.pbData ||
- !pInfo->salt.pbData
- )
- {
- ERR("could not allocate protect_data structures\n");
- free_protect_data(pInfo);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static
-BOOL convert_hash_to_blob(HCRYPTHASH hHash, DATA_BLOB * blob)
-{
- DWORD dwSize;
-
- TRACE("called\n");
-
- if (!blob) return FALSE;
-
- dwSize=sizeof(DWORD);
- if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&blob->cbData,
- &dwSize, 0))
- {
- ERR("failed to get hash size\n");
- return FALSE;
- }
-
- if (!(blob->pbData=CryptMemAlloc(blob->cbData)))
- {
- ERR("failed to allocate blob memory\n");
- return FALSE;
- }
-
- dwSize=blob->cbData;
- if (!CryptGetHashParam(hHash, HP_HASHVAL, blob->pbData, &dwSize, 0))
- {
- ERR("failed to get hash value\n");
- CryptMemFree(blob->pbData);
- blob->pbData=NULL;
- blob->cbData=0;
- return FALSE;
- }
-
- return TRUE;
-}
-
-/* test that a given hash matches an exported-to-blob hash value */
-static
-BOOL hash_matches_blob(HCRYPTHASH hHash, DATA_BLOB * two)
-{
- BOOL rc = FALSE;
- DATA_BLOB one;
-
- if (!two || !two->pbData) return FALSE;
-
- if (!convert_hash_to_blob(hHash,&one)) {
- return FALSE;
- }
-
- if ( one.cbData == two->cbData &&
- memcmp( one.pbData, two->pbData, one.cbData ) == 0 )
- {
- rc = TRUE;
- }
-
- CryptMemFree(one.pbData);
- return rc;
-}
-
-/* create an encryption key from a given salt and optional entropy */
-static
-BOOL load_encryption_key(HCRYPTPROV hProv, DATA_BLOB * salt,
- DATA_BLOB * pOptionalEntropy, HCRYPTKEY * phKey)
-{
- BOOL rc = TRUE;
- HCRYPTHASH hSaltHash;
- char * szUsername = NULL;
- DWORD dwUsernameLen;
- DWORD dwError;
-
- /* create hash for salt */
- if (!salt || !phKey ||
- !CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hSaltHash))
- {
- ERR("CryptCreateHash\n");
- return FALSE;
- }
-
- /* This should be the "logon credentials" instead of username */
- dwError=GetLastError();
- dwUsernameLen = 0;
- if (!GetUserNameA(NULL,&dwUsernameLen) &&
- GetLastError()==ERROR_MORE_DATA && dwUsernameLen &&
- (szUsername = CryptMemAlloc(dwUsernameLen)))
- {
- szUsername[0]='\0';
- GetUserNameA( szUsername, &dwUsernameLen );
- }
- SetLastError(dwError);
-
- /* salt the hash with:
- * - the user id
- * - an "internal secret"
- * - randomness (from the salt)
- * - user-supplied entropy
- */
- if ((szUsername && !CryptHashData(hSaltHash,(LPBYTE)szUsername,dwUsernameLen,0)) ||
- !CryptHashData(hSaltHash,crypt32_protectdata_secret,
- sizeof(crypt32_protectdata_secret)-1,0) ||
- !CryptHashData(hSaltHash,salt->pbData,salt->cbData,0) ||
- (pOptionalEntropy && !CryptHashData(hSaltHash,
- pOptionalEntropy->pbData,
- pOptionalEntropy->cbData,0)))
- {
- ERR("CryptHashData\n");
- rc = FALSE;
- }
-
- /* produce a symmetric key */
- if (rc && !CryptDeriveKey(hProv,CRYPT32_PROTECTDATA_KEY_CALG,
- hSaltHash,CRYPT_EXPORTABLE,phKey))
- {
- ERR("CryptDeriveKey\n");
- rc = FALSE;
- }
-
- /* clean up */
- CryptDestroyHash(hSaltHash);
- if (szUsername) CryptMemFree(szUsername);
-
- return rc;
-}
-
-/* debugging tool to print the structures of a ProtectData call */
-static void
-report(DATA_BLOB* pDataIn, DATA_BLOB* pOptionalEntropy,
- CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags)
-{
- TRACE("pPromptStruct: %p\n", pPromptStruct);
- if (pPromptStruct)
- {
- TRACE(" cbSize: 0x%x\n",(unsigned int)pPromptStruct->cbSize);
- TRACE(" dwPromptFlags: 0x%x\n",(unsigned int)pPromptStruct->dwPromptFlags);
- TRACE(" hwndApp: %p\n", pPromptStruct->hwndApp);
- TRACE(" szPrompt: %p %s\n",
- pPromptStruct->szPrompt,
- pPromptStruct->szPrompt ? debugstr_w(pPromptStruct->szPrompt)
- : "");
- }
- TRACE("dwFlags: 0x%04x\n",(unsigned int)dwFlags);
- TRACE_DATA_BLOB(pDataIn);
- if (pOptionalEntropy)
- {
- TRACE_DATA_BLOB(pOptionalEntropy);
- TRACE(" %s\n",debugstr_an((LPCSTR)pOptionalEntropy->pbData,pOptionalEntropy->cbData));
- }
-
-}
-
-
-/***************************************************************************
- * CryptProtectData [CRYPT32.@]
- *
- * Generate Cipher data from given Plain and Entropy data.
- *
- * PARAMS
- * pDataIn [I] Plain data to be enciphered
- * szDataDescr [I] Optional Unicode string describing the Plain data
- * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL
- * pvReserved [I] Reserved, must be NULL
- * pPromptStruct [I] Structure describing if/how to prompt during ciphering
- * dwFlags [I] Flags describing options to the ciphering
- * pDataOut [O] Resulting Cipher data, for calls to CryptUnprotectData
- *
- * RETURNS
- * TRUE If a Cipher was generated.
- * FALSE If something failed and no Cipher is available.
- *
- * FIXME
- * The true Windows encryption and keying mechanisms are unknown.
- *
- * dwFlags and pPromptStruct are currently ignored.
- *
- * NOTES
- * Memory allocated in pDataOut must be freed with LocalFree.
- *
- */
-BOOL WINAPI CryptProtectData(DATA_BLOB* pDataIn,
- LPCWSTR szDataDescr,
- DATA_BLOB* pOptionalEntropy,
- PVOID pvReserved,
- CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
- DWORD dwFlags,
- DATA_BLOB* pDataOut)
-{
- BOOL rc = FALSE;
-
- HCRYPTPROV hProv;
- struct protect_data_t protect_data;
- HCRYPTHASH hHash;
- HCRYPTKEY hKey;
- DWORD dwLength;
-
- TRACE("called\n");
-
- SetLastError(ERROR_SUCCESS);
-
- if (!pDataIn || !pDataOut)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- goto finished;
- }
-
- /* debug: show our arguments */
- report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
- TRACE("\tszDataDescr: %p %s\n", szDataDescr,
- szDataDescr ? debugstr_w(szDataDescr) : "");
-
- /* Windows appears to create an empty szDataDescr instead of maintaining
- * a NULL */
- if (!szDataDescr)
- szDataDescr=(WCHAR[]){'\0'};
-
- /* get crypt context */
- if (!CryptAcquireContextW(&hProv,NULL,NULL,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT))
- {
- ERR("CryptAcquireContextW failed\n");
- goto finished;
- }
-
- /* populate our structure */
- if (!fill_protect_data(&protect_data,szDataDescr,hProv))
- {
- ERR("fill_protect_data\n");
- goto free_context;
- }
-
- /* load key */
- if (!load_encryption_key(hProv,&protect_data.salt,pOptionalEntropy,&hKey))
- {
- goto free_protect_data;
- }
-
- /* create a hash for the encryption validation */
- if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash))
- {
- ERR("CryptCreateHash\n");
- goto free_key;
- }
-
- /* calculate storage required */
- dwLength=pDataIn->cbData;
- if (CryptEncrypt(hKey, 0, TRUE, 0, pDataIn->pbData, &dwLength, 0) ||
- GetLastError()!=ERROR_MORE_DATA)
- {
- ERR("CryptEncrypt\n");
- goto free_hash;
- }
- TRACE("required encrypted storage: %u\n",(unsigned int)dwLength);
-
- /* copy plain text into cipher area for CryptEncrypt call */
- protect_data.cipher.cbData=dwLength;
- if (!(protect_data.cipher.pbData=CryptMemAlloc(
- protect_data.cipher.cbData)))
- {
- ERR("CryptMemAlloc\n");
- goto free_hash;
- }
- memcpy(protect_data.cipher.pbData,pDataIn->pbData,pDataIn->cbData);
-
- /* encrypt! */
- dwLength=pDataIn->cbData;
- if (!CryptEncrypt(hKey, hHash, TRUE, 0, protect_data.cipher.pbData,
- &dwLength, protect_data.cipher.cbData))
- {
- ERR("CryptEncrypt %u\n",(unsigned int)GetLastError());
- goto free_hash;
- }
- protect_data.cipher.cbData=dwLength;
-
- /* debug: show the cipher */
- TRACE_DATA_BLOB(&protect_data.cipher);
-
- /* attach our fingerprint */
- if (!convert_hash_to_blob(hHash, &protect_data.fingerprint))
- {
- ERR("convert_hash_to_blob\n");
- goto free_hash;
- }
-
- /* serialize into an opaque blob */
- if (!serialize(&protect_data, pDataOut))
- {
- ERR("serialize\n");
- goto free_hash;
- }
-
- /* success! */
- rc=TRUE;
-
-free_hash:
- CryptDestroyHash(hHash);
-free_key:
- CryptDestroyKey(hKey);
-free_protect_data:
- free_protect_data(&protect_data);
-free_context:
- CryptReleaseContext(hProv,0);
-finished:
- /* If some error occurred, and no error code was set, force one. */
- if (!rc && GetLastError()==ERROR_SUCCESS)
- {
- SetLastError(ERROR_INVALID_DATA);
- }
-
- if (rc)
- {
- SetLastError(ERROR_SUCCESS);
-
- TRACE_DATA_BLOB(pDataOut);
- }
-
- TRACE("returning %s\n", rc ? "ok" : "FAIL");
-
- return rc;
-}
-
-
-/***************************************************************************
- * CryptUnprotectData [CRYPT32.@]
- *
- * Generate Plain data and Description from given Cipher and Entropy data.
- *
- * PARAMS
- * pDataIn [I] Cipher data to be decoded
- * ppszDataDescr [O] Optional Unicode string describing the Plain data
- * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL
- * pvReserved [I] Reserved, must be NULL
- * pPromptStruct [I] Structure describing if/how to prompt during decoding
- * dwFlags [I] Flags describing options to the decoding
- * pDataOut [O] Resulting Plain data, from calls to CryptProtectData
- *
- * RETURNS
- * TRUE If a Plain was generated.
- * FALSE If something failed and no Plain is available.
- *
- * FIXME
- * The true Windows encryption and keying mechanisms are unknown.
- *
- * dwFlags and pPromptStruct are currently ignored.
- *
- * NOTES
- * Memory allocated in pDataOut and non-NULL ppszDataDescr must be freed
- * with LocalFree.
- *
- */
-BOOL WINAPI CryptUnprotectData(DATA_BLOB* pDataIn,
- LPWSTR * ppszDataDescr,
- DATA_BLOB* pOptionalEntropy,
- PVOID pvReserved,
- CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
- DWORD dwFlags,
- DATA_BLOB* pDataOut)
-{
- BOOL rc = FALSE;
-
- HCRYPTPROV hProv;
- struct protect_data_t protect_data;
- HCRYPTHASH hHash;
- HCRYPTKEY hKey;
- DWORD dwLength;
-
- const char * announce_bad_opaque_data = "CryptUnprotectData received a DATA_BLOB that seems to have NOT been generated by Wine. Please enable tracing ('export WINEDEBUG=crypt') to see details.";
-
- TRACE("called\n");
-
- SetLastError(ERROR_SUCCESS);
-
- if (!pDataIn || !pDataOut)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- goto finished;
- }
-
- /* debug: show our arguments */
- report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
- TRACE("\tppszDataDescr: %p\n", ppszDataDescr);
-
- /* take apart the opaque blob */
- if (!unserialize(pDataIn, &protect_data))
- {
- SetLastError(ERROR_INVALID_DATA);
- FIXME("%s\n",announce_bad_opaque_data);
- goto finished;
- }
-
- /* perform basic validation on the resulting structure */
- if (!valid_protect_data(&protect_data))
- {
- SetLastError(ERROR_INVALID_DATA);
- FIXME("%s\n",announce_bad_opaque_data);
- goto free_protect_data;
- }
-
- /* get a crypt context */
- if (!CryptAcquireContextW(&hProv,NULL,NULL,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT))
- {
- ERR("CryptAcquireContextW failed\n");
- goto free_protect_data;
- }
-
- /* load key */
- if (!load_encryption_key(hProv,&protect_data.salt,pOptionalEntropy,&hKey))
- {
- goto free_context;
- }
-
- /* create a hash for the decryption validation */
- if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash))
- {
- ERR("CryptCreateHash\n");
- goto free_key;
- }
-
- /* prepare for plaintext */
- pDataOut->cbData=protect_data.cipher.cbData;
- if (!(pDataOut->pbData=LocalAlloc( LPTR, pDataOut->cbData)))
- {
- ERR("CryptMemAlloc\n");
- goto free_hash;
- }
- memcpy(pDataOut->pbData,protect_data.cipher.pbData,protect_data.cipher.cbData);
-
- /* decrypt! */
- if (!CryptDecrypt(hKey, hHash, TRUE, 0, pDataOut->pbData,
- &pDataOut->cbData) ||
- /* check the hash fingerprint */
- pDataOut->cbData > protect_data.cipher.cbData ||
- !hash_matches_blob(hHash, &protect_data.fingerprint))
- {
- SetLastError(ERROR_INVALID_DATA);
-
- LocalFree( pDataOut->pbData );
- pDataOut->pbData = NULL;
- pDataOut->cbData = 0;
-
- goto free_hash;
- }
-
- /* Copy out the description */
- dwLength = (lstrlenW(protect_data.szDataDescr)+1) * sizeof(WCHAR);
- if (ppszDataDescr)
- {
- if (!(*ppszDataDescr = LocalAlloc(LPTR,dwLength)))
- {
- ERR("LocalAlloc (ppszDataDescr)\n");
- goto free_hash;
- }
- else {
- memcpy(*ppszDataDescr,protect_data.szDataDescr,dwLength);
- }
- }
-
- /* success! */
- rc = TRUE;
-
-free_hash:
- CryptDestroyHash(hHash);
-free_key:
- CryptDestroyKey(hKey);
-free_context:
- CryptReleaseContext(hProv,0);
-free_protect_data:
- free_protect_data(&protect_data);
-finished:
- /* If some error occurred, and no error code was set, force one. */
- if (!rc && GetLastError()==ERROR_SUCCESS)
- {
- SetLastError(ERROR_INVALID_DATA);
- }
-
- if (rc) {
- SetLastError(ERROR_SUCCESS);
-
- if (ppszDataDescr)
- {
- TRACE("szDataDescr: %s\n",debugstr_w(*ppszDataDescr));
- }
- TRACE_DATA_BLOB(pDataOut);
- }
-
- TRACE("returning %s\n", rc ? "ok" : "FAIL");
-
- return rc;
-}