ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

protectdata.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 2005 Kees Cook <kees@outflux.net>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2.1 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public
00015  * License along with this library; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00017  */
00018 
00019 
00020 /*
00021  * The Win32 CryptProtectData and CryptUnprotectData functions are meant
00022  * to provide a mechanism for encrypting data on a machine where other users
00023  * of the system can't be trusted.  It is used in many examples as a way
00024  * to store username and password information to the registry, but store
00025  * it not in the clear.
00026  *
00027  * The encryption is symmetric, but the method is unknown.  However, since
00028  * it is keyed to the machine and the user, it is unlikely that the values
00029  * would be portable.  Since programs must first call CryptProtectData to
00030  * get a cipher text, the underlying system doesn't have to exactly
00031  * match the real Windows version.  However, attempts have been made to
00032  * at least try to look like the Windows version, including guesses at the
00033  * purpose of various portions of the "opaque data blob" that is used.
00034  *
00035  */
00036 
00037 #include <stdarg.h>
00038 #include <stdio.h>
00039 #include <string.h>
00040 #include <stdlib.h>
00041 
00042 #include "windef.h"
00043 #include "winbase.h"
00044 #include "wincrypt.h"
00045 #include "wine/debug.h"
00046 
00047 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
00048 
00049 #define CRYPT32_PROTECTDATA_PROV      PROV_RSA_FULL
00050 #define CRYPT32_PROTECTDATA_HASH_CALG CALG_SHA1
00051 #define CRYPT32_PROTECTDATA_HASH_LEN  160
00052 #define CRYPT32_PROTECTDATA_KEY_CALG  CALG_3DES
00053 #define CRYPT32_PROTECTDATA_KEY_LEN   168
00054 #define CRYPT32_PROTECTDATA_SALT_LEN  16
00055 
00056 static const BYTE crypt32_protectdata_secret[] = {
00057     'I','\'','m',' ','h','u','n','t','i','n','g',' ',
00058     'w','a','b','b','i','t','s',0
00059 };
00060 
00061 /*
00062  * The data format returned by the real Windows CryptProtectData seems
00063  * to be something like this:
00064 
00065  DWORD  count0;         - how many "info0_*[16]" blocks follow (was always 1)
00066  BYTE   info0_0[16];    - unknown information - persistent across invocations,
00067  ...                      reboots, password changes, and users
00068  DWORD  count1;         - how many "info1_*[16]" blocks follow (was always 1)
00069  BYTE   info1_0[16];    - unknown information - unique to each user, but
00070  ...                      persistent across reboots and password changes
00071  DWORD  null0;          - NULL "end of records"?
00072  DWORD  str_len;        - byte length of WCHAR string including term
00073  BYTE   str[str_len];   - The "dataDescription" value as a NULL-terminated
00074                           little-endian WCHAR string
00075  ALG_ID cipher_alg;     - cipher algo - was CALG_3DES
00076  DWORD  cipher_key_len; - cipher key bit length - was 0xa8==168
00077  DWORD  data_len;       - length of data (was 16 in samples)
00078  BYTE   data[data_len]; - unknown data (fingerprint?)
00079  DWORD  null1;          - NULL ?
00080  ALG_ID hash_alg;       - hash algo - was CALG_SHA1
00081  DWORD  hash_len;       - bit length of hash - was 0xa0==160
00082  DWORD  salt_len;       - length of salt(?) data
00083  BYTE   salt[salt_len]; - salt(?) for symmetric encryption
00084  DWORD  cipher_len;     - length of cipher(?) data - was close to plain len
00085  BYTE   cipher[cipher_len]; - cipher text?
00086  DWORD  crc_len;        - length of fingerprint(?) data - was 20 byte==160b SHA1
00087  BYTE   crc[crc_len];   - fingerprint of record?
00088 
00089  * The data structures used in Wine are modelled after this guess.
00090  */
00091 
00092 struct protect_data_t
00093 {
00094     DWORD       count0;
00095     DATA_BLOB   info0;        /* using this to hold crypt_magic_str */
00096     DWORD       count1;
00097     DATA_BLOB   info1;
00098     DWORD       null0;
00099     WCHAR *     szDataDescr;  /* serialized differently than the DATA_BLOBs */
00100     ALG_ID      cipher_alg;
00101     DWORD       cipher_key_len;
00102     DATA_BLOB   data0;
00103     DWORD       null1;
00104     ALG_ID      hash_alg;
00105     DWORD       hash_len;
00106     DATA_BLOB   salt;
00107     DATA_BLOB   cipher;
00108     DATA_BLOB   fingerprint;
00109 };
00110 
00111 /* this is used to check if an incoming structure was built by Wine */
00112 static const char crypt_magic_str[] = "Wine Crypt32 ok";
00113 
00114 /* debugging tool to print strings of hex chars */
00115 static const char *
00116 hex_str(const unsigned char *p, int n)
00117 {
00118     const char * ptr;
00119     char report[80];
00120     int r=-1;
00121     report[0]='\0';
00122     ptr = wine_dbg_sprintf("%s","");
00123     while (--n >= 0)
00124     {
00125         if (r++ % 20 == 19)
00126         {
00127             ptr = wine_dbg_sprintf("%s%s",ptr,report);
00128             report[0]='\0';
00129         }
00130         sprintf(report+strlen(report),"%s%02x", r ? "," : "", *p++);
00131     }
00132     return wine_dbg_sprintf("%s%s",ptr,report);
00133 }
00134 
00135 #define TRACE_DATA_BLOB(blob) do { \
00136     TRACE("%s cbData: %u\n", #blob ,(unsigned int)((blob)->cbData)); \
00137     TRACE("%s pbData @ %p:%s\n", #blob ,(blob)->pbData, \
00138           hex_str((blob)->pbData, (blob)->cbData)); \
00139 } while (0)
00140 
00141 static
00142 void serialize_dword(DWORD value,BYTE ** ptr)
00143 {
00144     /*TRACE("called\n");*/
00145 
00146     memcpy(*ptr,&value,sizeof(DWORD));
00147     *ptr+=sizeof(DWORD);
00148 }
00149 
00150 static
00151 void serialize_string(const BYTE *str, BYTE **ptr, DWORD len, DWORD width,
00152                       BOOL prepend_len)
00153 {
00154     /*TRACE("called %ux%u\n",(unsigned int)len,(unsigned int)width);*/
00155 
00156     if (prepend_len)
00157     {
00158         serialize_dword(len,ptr);
00159     }
00160     memcpy(*ptr,str,len*width);
00161     *ptr+=len*width;
00162 }
00163 
00164 static
00165 BOOL unserialize_dword(const BYTE *ptr, DWORD *index, DWORD size, DWORD *value)
00166 {
00167     /*TRACE("called\n");*/
00168 
00169     if (!ptr || !index || !value) return FALSE;
00170 
00171     if (*index+sizeof(DWORD)>size)
00172     {
00173         return FALSE;
00174     }
00175 
00176     memcpy(value,&(ptr[*index]),sizeof(DWORD));
00177     *index+=sizeof(DWORD);
00178 
00179     return TRUE;
00180 }
00181 
00182 static
00183 BOOL unserialize_string(const BYTE *ptr, DWORD *index, DWORD size,
00184                         DWORD len, DWORD width, BOOL inline_len,
00185                         BYTE ** data, DWORD * stored)
00186 {
00187     /*TRACE("called\n");*/
00188 
00189     if (!ptr || !data) return FALSE;
00190 
00191     if (inline_len) {
00192         if (!unserialize_dword(ptr,index,size,&len))
00193             return FALSE;
00194     }
00195 
00196     if (*index+len*width>size)
00197     {
00198         return FALSE;
00199     }
00200 
00201     if (!(*data = CryptMemAlloc( len*width)))
00202     {
00203         return FALSE;
00204     }
00205 
00206     memcpy(*data,&(ptr[*index]),len*width);
00207     if (stored)
00208     {
00209         *stored = len;
00210     }
00211     *index+=len*width;
00212 
00213     return TRUE;
00214 }
00215 
00216 static
00217 BOOL serialize(const struct protect_data_t *pInfo, DATA_BLOB *pSerial)
00218 {
00219     BYTE * ptr;
00220     DWORD dwStrLen;
00221     DWORD dwStruct;
00222 
00223     TRACE("called\n");
00224 
00225     if (!pInfo || !pInfo->szDataDescr || !pSerial ||
00226         !pInfo->info0.pbData || !pInfo->info1.pbData ||
00227         !pInfo->data0.pbData || !pInfo->salt.pbData ||
00228         !pInfo->cipher.pbData || !pInfo->fingerprint.pbData)
00229     {
00230         return FALSE;
00231     }
00232 
00233     if (pInfo->info0.cbData!=16)
00234     {
00235         ERR("protect_data_t info0 not 16 bytes long\n");
00236     }
00237 
00238     if (pInfo->info1.cbData!=16)
00239     {
00240         ERR("protect_data_t info1 not 16 bytes long\n");
00241     }
00242 
00243     dwStrLen=lstrlenW(pInfo->szDataDescr);
00244 
00245     pSerial->cbData=0;
00246     pSerial->cbData+=sizeof(DWORD)*8; /* 8 raw DWORDs */
00247     pSerial->cbData+=sizeof(DWORD)*4; /* 4 BLOBs with size */
00248     pSerial->cbData+=pInfo->info0.cbData;
00249     pSerial->cbData+=pInfo->info1.cbData;
00250     pSerial->cbData+=(dwStrLen+1)*sizeof(WCHAR) + 4; /* str, null, size */
00251     pSerial->cbData+=pInfo->data0.cbData;
00252     pSerial->cbData+=pInfo->salt.cbData;
00253     pSerial->cbData+=pInfo->cipher.cbData;
00254     pSerial->cbData+=pInfo->fingerprint.cbData;
00255 
00256     /* save the actual structure size */
00257     dwStruct = pSerial->cbData;
00258     /* There may be a 256 byte minimum, but I can't prove it. */
00259     /*if (pSerial->cbData<256) pSerial->cbData=256;*/
00260 
00261     pSerial->pbData=LocalAlloc(LPTR,pSerial->cbData);
00262     if (!pSerial->pbData) return FALSE;
00263 
00264     ptr=pSerial->pbData;
00265 
00266     /* count0 */
00267     serialize_dword(pInfo->count0,&ptr);
00268     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00269     
00270     /* info0 */
00271     serialize_string(pInfo->info0.pbData,&ptr,
00272                      pInfo->info0.cbData,sizeof(BYTE),FALSE);
00273     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00274 
00275     /* count1 */
00276     serialize_dword(pInfo->count1,&ptr);
00277     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00278 
00279     /* info1 */
00280     serialize_string(pInfo->info1.pbData,&ptr,
00281                      pInfo->info1.cbData,sizeof(BYTE),FALSE);
00282     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00283 
00284     /* null0 */
00285     serialize_dword(pInfo->null0,&ptr);
00286     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00287     
00288     /* szDataDescr */
00289     serialize_string((BYTE*)pInfo->szDataDescr,&ptr,
00290                      (dwStrLen+1)*sizeof(WCHAR),sizeof(BYTE),TRUE);
00291     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00292 
00293     /* cipher_alg */
00294     serialize_dword(pInfo->cipher_alg,&ptr);
00295     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00296     /* cipher_key_len */
00297     serialize_dword(pInfo->cipher_key_len,&ptr);
00298     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00299     
00300     /* data0 */
00301     serialize_string(pInfo->data0.pbData,&ptr,
00302                      pInfo->data0.cbData,sizeof(BYTE),TRUE);
00303     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00304 
00305     /* null1 */
00306     serialize_dword(pInfo->null1,&ptr);
00307     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00308     
00309     /* hash_alg */
00310     serialize_dword(pInfo->hash_alg,&ptr);
00311     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00312     /* hash_len */
00313     serialize_dword(pInfo->hash_len,&ptr);
00314     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00315     
00316     /* salt */
00317     serialize_string(pInfo->salt.pbData,&ptr,
00318                      pInfo->salt.cbData,sizeof(BYTE),TRUE);
00319     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00320 
00321     /* cipher */
00322     serialize_string(pInfo->cipher.pbData,&ptr,
00323                      pInfo->cipher.cbData,sizeof(BYTE),TRUE);
00324     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00325 
00326     /* fingerprint */
00327     serialize_string(pInfo->fingerprint.pbData,&ptr,
00328                      pInfo->fingerprint.cbData,sizeof(BYTE),TRUE);
00329     /*TRACE("used %u\n",ptr-pSerial->pbData);*/
00330 
00331     if (ptr - pSerial->pbData != dwStruct)
00332     {
00333         ERR("struct size changed!? expected %u\n", dwStruct);
00334         LocalFree(pSerial->pbData);
00335         pSerial->pbData=NULL;
00336         pSerial->cbData=0;
00337         return FALSE;
00338     }
00339 
00340     return TRUE;
00341 }
00342 
00343 static
00344 BOOL unserialize(const DATA_BLOB *pSerial, struct protect_data_t *pInfo)
00345 {
00346     BYTE * ptr;
00347     DWORD index;
00348     DWORD size;
00349     BOOL status=TRUE;
00350 
00351     TRACE("called\n");
00352 
00353     if (!pInfo || !pSerial || !pSerial->pbData)
00354         return FALSE;
00355 
00356     index=0;
00357     ptr=pSerial->pbData;
00358     size=pSerial->cbData;
00359 
00360     /* count0 */
00361     if (!unserialize_dword(ptr,&index,size,&pInfo->count0))
00362     {
00363         ERR("reading count0 failed!\n");
00364         return FALSE;
00365     }
00366     
00367     /* info0 */
00368     if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
00369                             &pInfo->info0.pbData, &pInfo->info0.cbData))
00370     {
00371         ERR("reading info0 failed!\n");
00372         return FALSE;
00373     }
00374 
00375     /* count1 */
00376     if (!unserialize_dword(ptr,&index,size,&pInfo->count1))
00377     {
00378         ERR("reading count1 failed!\n");
00379         return FALSE;
00380     }
00381 
00382     /* info1 */
00383     if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
00384                             &pInfo->info1.pbData, &pInfo->info1.cbData))
00385     {
00386         ERR("reading info1 failed!\n");
00387         return FALSE;
00388     }
00389 
00390     /* null0 */
00391     if (!unserialize_dword(ptr,&index,size,&pInfo->null0))
00392     {
00393         ERR("reading null0 failed!\n");
00394         return FALSE;
00395     }
00396     
00397     /* szDataDescr */
00398     if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
00399                             (BYTE**)&pInfo->szDataDescr, NULL))
00400     {
00401         ERR("reading szDataDescr failed!\n");
00402         return FALSE;
00403     }
00404 
00405     /* cipher_alg */
00406     if (!unserialize_dword(ptr,&index,size,&pInfo->cipher_alg))
00407     {
00408         ERR("reading cipher_alg failed!\n");
00409         return FALSE;
00410     }
00411     
00412     /* cipher_key_len */
00413     if (!unserialize_dword(ptr,&index,size,&pInfo->cipher_key_len))
00414     {
00415         ERR("reading cipher_key_len failed!\n");
00416         return FALSE;
00417     }
00418     
00419     /* data0 */
00420     if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
00421                             &pInfo->data0.pbData, &pInfo->data0.cbData))
00422     {
00423         ERR("reading data0 failed!\n");
00424         return FALSE;
00425     }
00426 
00427     /* null1 */
00428     if (!unserialize_dword(ptr,&index,size,&pInfo->null1))
00429     {
00430         ERR("reading null1 failed!\n");
00431         return FALSE;
00432     }
00433     
00434     /* hash_alg */
00435     if (!unserialize_dword(ptr,&index,size,&pInfo->hash_alg))
00436     {
00437         ERR("reading hash_alg failed!\n");
00438         return FALSE;
00439     }
00440     
00441     /* hash_len */
00442     if (!unserialize_dword(ptr,&index,size,&pInfo->hash_len))
00443     {
00444         ERR("reading hash_len failed!\n");
00445         return FALSE;
00446     }
00447     
00448     /* salt */
00449     if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
00450                             &pInfo->salt.pbData, &pInfo->salt.cbData))
00451     {
00452         ERR("reading salt failed!\n");
00453         return FALSE;
00454     }
00455 
00456     /* cipher */
00457     if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
00458                             &pInfo->cipher.pbData, &pInfo->cipher.cbData))
00459     {
00460         ERR("reading cipher failed!\n");
00461         return FALSE;
00462     }
00463 
00464     /* fingerprint */
00465     if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
00466                             &pInfo->fingerprint.pbData, &pInfo->fingerprint.cbData))
00467     {
00468         ERR("reading fingerprint failed!\n");
00469         return FALSE;
00470     }
00471 
00472     /* allow structure size to be too big (since some applications
00473      * will pad this up to 256 bytes, it seems) */
00474     if (index>size)
00475     {
00476         /* this is an impossible-to-reach test, but if the padding
00477          * issue is ever understood, this may become more useful */
00478         ERR("loaded corrupt structure! (used %u expected %u)\n", index, size);
00479         status=FALSE;
00480     }
00481 
00482     return status;
00483 }
00484 
00485 /* perform sanity checks */
00486 static
00487 BOOL valid_protect_data(const struct protect_data_t *pInfo)
00488 {
00489     BOOL status=TRUE;
00490 
00491     TRACE("called\n");
00492 
00493     if (pInfo->count0 != 0x0001)
00494     {
00495         ERR("count0 != 0x0001 !\n");
00496         status=FALSE;
00497     }
00498     if (pInfo->count1 != 0x0001)
00499     {
00500         ERR("count0 != 0x0001 !\n");
00501         status=FALSE;
00502     }
00503     if (pInfo->null0 != 0x0000)
00504     {
00505         ERR("null0 != 0x0000 !\n");
00506         status=FALSE;
00507     }
00508     if (pInfo->null1 != 0x0000)
00509     {
00510         ERR("null1 != 0x0000 !\n");
00511         status=FALSE;
00512     }
00513     /* since we have no idea what info0 is used for, and it seems
00514      * rather constant, we can test for a Wine-specific magic string
00515      * there to be reasonably sure we're using data created by the Wine
00516      * implementation of CryptProtectData.
00517      */
00518     if (pInfo->info0.cbData!=strlen(crypt_magic_str)+1 ||
00519         strcmp( (LPCSTR)pInfo->info0.pbData,crypt_magic_str) != 0)
00520     {
00521         ERR("info0 magic value not matched !\n");
00522         status=FALSE;
00523     }
00524 
00525     if (!status)
00526     {
00527         ERR("unrecognized CryptProtectData block\n");
00528     }
00529 
00530     return status;
00531 }
00532 
00533 static
00534 void free_protect_data(struct protect_data_t * pInfo)
00535 {
00536     TRACE("called\n");
00537 
00538     if (!pInfo) return;
00539 
00540     CryptMemFree(pInfo->info0.pbData);
00541     CryptMemFree(pInfo->info1.pbData);
00542     CryptMemFree(pInfo->szDataDescr);
00543     CryptMemFree(pInfo->data0.pbData);
00544     CryptMemFree(pInfo->salt.pbData);
00545     CryptMemFree(pInfo->cipher.pbData);
00546     CryptMemFree(pInfo->fingerprint.pbData);
00547 }
00548 
00549 /* copies a string into a data blob */
00550 static
00551 BYTE *convert_str_to_blob(LPCSTR str, DATA_BLOB *blob)
00552 {
00553     if (!str || !blob) return NULL;
00554 
00555     blob->cbData=strlen(str)+1;
00556     if (!(blob->pbData=CryptMemAlloc(blob->cbData)))
00557     {
00558         blob->cbData=0;
00559     }
00560     else {
00561         strcpy((LPSTR)blob->pbData, str);
00562     }
00563 
00564     return blob->pbData;
00565 }
00566 
00567 /*
00568  * Populates everything except "cipher" and "fingerprint".
00569  */
00570 static
00571 BOOL fill_protect_data(struct protect_data_t * pInfo, LPCWSTR szDataDescr,
00572                        HCRYPTPROV hProv)
00573 {
00574     DWORD dwStrLen;
00575 
00576     TRACE("called\n");
00577 
00578     if (!pInfo) return FALSE;
00579 
00580     dwStrLen=lstrlenW(szDataDescr);
00581 
00582     memset(pInfo,0,sizeof(*pInfo));
00583 
00584     pInfo->count0=0x0001;
00585 
00586     convert_str_to_blob(crypt_magic_str, &pInfo->info0);
00587 
00588     pInfo->count1=0x0001;
00589 
00590     convert_str_to_blob(crypt_magic_str, &pInfo->info1);
00591 
00592     pInfo->null0=0x0000;
00593 
00594     if ((pInfo->szDataDescr=CryptMemAlloc((dwStrLen+1)*sizeof(WCHAR))))
00595     {
00596         memcpy(pInfo->szDataDescr,szDataDescr,(dwStrLen+1)*sizeof(WCHAR));
00597     }
00598 
00599     pInfo->cipher_alg=CRYPT32_PROTECTDATA_KEY_CALG;
00600     pInfo->cipher_key_len=CRYPT32_PROTECTDATA_KEY_LEN;
00601 
00602     convert_str_to_blob(crypt_magic_str, &pInfo->data0);
00603 
00604     pInfo->null1=0x0000;
00605     pInfo->hash_alg=CRYPT32_PROTECTDATA_HASH_CALG;
00606     pInfo->hash_len=CRYPT32_PROTECTDATA_HASH_LEN;
00607 
00608     /* allocate memory to hold a salt */
00609     if ((pInfo->salt.pbData=CryptMemAlloc(CRYPT32_PROTECTDATA_SALT_LEN)))
00610     {
00611         /* generate random salt */
00612         if (!CryptGenRandom(hProv, CRYPT32_PROTECTDATA_SALT_LEN, pInfo->salt.pbData))
00613         {
00614             ERR("CryptGenRandom\n");
00615             free_protect_data(pInfo);
00616             return FALSE;
00617         }
00618         pInfo->salt.cbData=CRYPT32_PROTECTDATA_SALT_LEN;
00619         /* debug: show our salt */
00620         TRACE_DATA_BLOB(&pInfo->salt);
00621     }
00622     pInfo->cipher.cbData=0;
00623     pInfo->cipher.pbData=NULL;
00624 
00625     pInfo->fingerprint.cbData=0;
00626     pInfo->fingerprint.pbData=NULL;
00627 
00628     /* check all the allocations at once */
00629     if (!pInfo->info0.pbData ||
00630         !pInfo->info1.pbData ||
00631         !pInfo->szDataDescr  ||
00632         !pInfo->data0.pbData ||
00633         !pInfo->salt.pbData
00634         )
00635     {
00636         ERR("could not allocate protect_data structures\n");
00637         free_protect_data(pInfo);
00638         return FALSE;
00639     }
00640 
00641     return TRUE;
00642 }
00643 
00644 static
00645 BOOL convert_hash_to_blob(HCRYPTHASH hHash, DATA_BLOB * blob)
00646 {
00647     DWORD dwSize;
00648 
00649     TRACE("called\n");
00650 
00651     if (!blob) return FALSE;
00652 
00653     dwSize=sizeof(DWORD);
00654     if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&blob->cbData,
00655                            &dwSize, 0))
00656     {
00657         ERR("failed to get hash size\n");
00658         return FALSE;
00659     }
00660 
00661     if (!(blob->pbData=CryptMemAlloc(blob->cbData)))
00662     {
00663         ERR("failed to allocate blob memory\n");
00664         return FALSE;
00665     }
00666 
00667     dwSize=blob->cbData;
00668     if (!CryptGetHashParam(hHash, HP_HASHVAL, blob->pbData, &dwSize, 0))
00669     {
00670         ERR("failed to get hash value\n");
00671         CryptMemFree(blob->pbData);
00672         blob->pbData=NULL;
00673         blob->cbData=0;
00674         return FALSE;
00675     }
00676 
00677     return TRUE;
00678 }
00679 
00680 /* test that a given hash matches an exported-to-blob hash value */
00681 static
00682 BOOL hash_matches_blob(HCRYPTHASH hHash, const DATA_BLOB *two)
00683 {
00684     BOOL rc = FALSE;
00685     DATA_BLOB one;
00686 
00687     if (!two || !two->pbData) return FALSE;
00688 
00689     if (!convert_hash_to_blob(hHash,&one)) {
00690         return FALSE;
00691     }
00692 
00693     if ( one.cbData == two->cbData &&
00694          memcmp( one.pbData, two->pbData, one.cbData ) == 0 )
00695     {
00696         rc = TRUE;
00697     }
00698 
00699     CryptMemFree(one.pbData);
00700     return rc;
00701 }
00702 
00703 /* create an encryption key from a given salt and optional entropy */
00704 static
00705 BOOL load_encryption_key(HCRYPTPROV hProv, DWORD key_len, const DATA_BLOB *salt,
00706                          const DATA_BLOB *pOptionalEntropy, HCRYPTKEY *phKey)
00707 {
00708     BOOL rc = TRUE;
00709     HCRYPTHASH hSaltHash;
00710     char * szUsername = NULL;
00711     DWORD dwUsernameLen;
00712     DWORD dwError;
00713 
00714     /* create hash for salt */
00715     if (!salt || !phKey ||
00716         !CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hSaltHash))
00717     {
00718         ERR("CryptCreateHash\n");
00719         return FALSE;
00720     }
00721 
00722     /* This should be the "logon credentials" instead of username */
00723     dwError=GetLastError();
00724     dwUsernameLen = 0;
00725     if (!GetUserNameA(NULL, &dwUsernameLen) &&
00726         GetLastError() == ERROR_INSUFFICIENT_BUFFER && dwUsernameLen &&
00727         (szUsername = CryptMemAlloc(dwUsernameLen)))
00728     {
00729         szUsername[0]='\0';
00730         GetUserNameA( szUsername, &dwUsernameLen );
00731     }
00732     SetLastError(dwError);
00733 
00734     /* salt the hash with:
00735      * - the user id
00736      * - an "internal secret"
00737      * - randomness (from the salt)
00738      * - user-supplied entropy
00739      */
00740     if ((szUsername && !CryptHashData(hSaltHash,(LPBYTE)szUsername,dwUsernameLen,0)) ||
00741         !CryptHashData(hSaltHash,crypt32_protectdata_secret,
00742                                  sizeof(crypt32_protectdata_secret)-1,0) ||
00743         !CryptHashData(hSaltHash,salt->pbData,salt->cbData,0) ||
00744         (pOptionalEntropy && !CryptHashData(hSaltHash,
00745                                             pOptionalEntropy->pbData,
00746                                             pOptionalEntropy->cbData,0)))
00747     {
00748         ERR("CryptHashData\n");
00749         rc = FALSE;
00750     }
00751 
00752     /* produce a symmetric key */
00753     if (rc && !CryptDeriveKey(hProv,CRYPT32_PROTECTDATA_KEY_CALG,
00754                               hSaltHash,key_len << 16 | CRYPT_EXPORTABLE,phKey))
00755     {
00756         ERR("CryptDeriveKey\n");
00757         rc = FALSE;
00758     }
00759 
00760     /* clean up */
00761     CryptDestroyHash(hSaltHash);
00762     CryptMemFree(szUsername);
00763 
00764     return rc;
00765 }
00766 
00767 /* debugging tool to print the structures of a ProtectData call */
00768 static void
00769 report(const DATA_BLOB* pDataIn, const DATA_BLOB* pOptionalEntropy,
00770        CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags)
00771 {
00772     TRACE("pPromptStruct: %p\n", pPromptStruct);
00773     if (pPromptStruct)
00774     {
00775         TRACE("  cbSize: 0x%x\n", pPromptStruct->cbSize);
00776         TRACE("  dwPromptFlags: 0x%x\n", pPromptStruct->dwPromptFlags);
00777         TRACE("  hwndApp: %p\n", pPromptStruct->hwndApp);
00778         TRACE("  szPrompt: %p %s\n",
00779               pPromptStruct->szPrompt,
00780               pPromptStruct->szPrompt ? debugstr_w(pPromptStruct->szPrompt)
00781               : "");
00782     }
00783     TRACE("dwFlags: 0x%04x\n", dwFlags);
00784     TRACE_DATA_BLOB(pDataIn);
00785     if (pOptionalEntropy)
00786     {
00787         TRACE_DATA_BLOB(pOptionalEntropy);
00788         TRACE("  %s\n",debugstr_an((LPCSTR)pOptionalEntropy->pbData,pOptionalEntropy->cbData));
00789     }
00790 
00791 }
00792 
00793 
00794 /***************************************************************************
00795  * CryptProtectData     [CRYPT32.@]
00796  *
00797  * Generate Cipher data from given Plain and Entropy data.
00798  *
00799  * PARAMS
00800  *  pDataIn          [I] Plain data to be enciphered
00801  *  szDataDescr      [I] Optional Unicode string describing the Plain data
00802  *  pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL
00803  *  pvReserved       [I] Reserved, must be NULL
00804  *  pPromptStruct    [I] Structure describing if/how to prompt during ciphering
00805  *  dwFlags          [I] Flags describing options to the ciphering
00806  *  pDataOut         [O] Resulting Cipher data, for calls to CryptUnprotectData
00807  *
00808  * RETURNS
00809  *  TRUE  If a Cipher was generated.
00810  *  FALSE If something failed and no Cipher is available.
00811  *
00812  * FIXME
00813  *  The true Windows encryption and keying mechanisms are unknown.
00814  *
00815  *  dwFlags and pPromptStruct are currently ignored.
00816  *
00817  * NOTES
00818  *  Memory allocated in pDataOut must be freed with LocalFree.
00819  *
00820  */
00821 BOOL WINAPI CryptProtectData(DATA_BLOB* pDataIn,
00822                              LPCWSTR szDataDescr,
00823                              DATA_BLOB* pOptionalEntropy,
00824                              PVOID pvReserved,
00825                              CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
00826                              DWORD dwFlags,
00827                              DATA_BLOB* pDataOut)
00828 {
00829     static const WCHAR empty_str[1];
00830     BOOL rc = FALSE;
00831     HCRYPTPROV hProv;
00832     struct protect_data_t protect_data;
00833     HCRYPTHASH hHash;
00834     HCRYPTKEY hKey;
00835     DWORD dwLength;
00836 
00837     TRACE("called\n");
00838 
00839     SetLastError(ERROR_SUCCESS);
00840 
00841     if (!pDataIn || !pDataOut)
00842     {
00843         SetLastError(ERROR_INVALID_PARAMETER);
00844         goto finished;
00845     }
00846 
00847     /* debug: show our arguments */
00848     report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
00849     TRACE("\tszDataDescr: %p %s\n", szDataDescr,
00850           szDataDescr ? debugstr_w(szDataDescr) : "");
00851 
00852     /* Windows appears to create an empty szDataDescr instead of maintaining
00853      * a NULL */
00854     if (!szDataDescr)
00855         szDataDescr = empty_str;
00856 
00857     /* get crypt context */
00858     if (!CryptAcquireContextW(&hProv,NULL,MS_ENHANCED_PROV_W,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT))
00859     {
00860         ERR("CryptAcquireContextW failed\n");
00861         goto finished;
00862     }
00863 
00864     /* populate our structure */
00865     if (!fill_protect_data(&protect_data,szDataDescr,hProv))
00866     {
00867         ERR("fill_protect_data\n");
00868         goto free_context;
00869     }
00870 
00871     /* load key */
00872     if (!load_encryption_key(hProv,protect_data.cipher_key_len,&protect_data.salt,pOptionalEntropy,&hKey))
00873     {
00874         goto free_protect_data;
00875     }
00876 
00877     /* create a hash for the encryption validation */
00878     if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash))
00879     {
00880         ERR("CryptCreateHash\n");
00881         goto free_key;
00882     }
00883 
00884     /* calculate storage required */
00885     dwLength=pDataIn->cbData;
00886     if (CryptEncrypt(hKey, 0, TRUE, 0, pDataIn->pbData, &dwLength, 0) ||
00887         GetLastError()!=ERROR_MORE_DATA)
00888     {
00889         ERR("CryptEncrypt\n");
00890         goto free_hash;
00891     }
00892     TRACE("required encrypted storage: %u\n", dwLength);
00893 
00894     /* copy plain text into cipher area for CryptEncrypt call */
00895     protect_data.cipher.cbData=dwLength;
00896     if (!(protect_data.cipher.pbData=CryptMemAlloc(
00897                                                 protect_data.cipher.cbData)))
00898     {
00899         ERR("CryptMemAlloc\n");
00900         goto free_hash;
00901     }
00902     memcpy(protect_data.cipher.pbData,pDataIn->pbData,pDataIn->cbData);
00903 
00904     /* encrypt! */
00905     dwLength=pDataIn->cbData;
00906     if (!CryptEncrypt(hKey, hHash, TRUE, 0, protect_data.cipher.pbData,
00907                       &dwLength, protect_data.cipher.cbData))
00908     {
00909         ERR("CryptEncrypt %u\n", GetLastError());
00910         goto free_hash;
00911     }
00912     protect_data.cipher.cbData=dwLength;
00913 
00914     /* debug: show the cipher */
00915     TRACE_DATA_BLOB(&protect_data.cipher);
00916 
00917     /* attach our fingerprint */
00918     if (!convert_hash_to_blob(hHash, &protect_data.fingerprint))
00919     {
00920         ERR("convert_hash_to_blob\n");
00921         goto free_hash;
00922     }
00923 
00924     /* serialize into an opaque blob */
00925     if (!serialize(&protect_data, pDataOut))
00926     {
00927         ERR("serialize\n");
00928         goto free_hash;
00929     }
00930 
00931     /* success! */
00932     rc=TRUE;
00933 
00934 free_hash:
00935     CryptDestroyHash(hHash);
00936 free_key:
00937     CryptDestroyKey(hKey);
00938 free_protect_data:
00939     free_protect_data(&protect_data);
00940 free_context:
00941     CryptReleaseContext(hProv,0);
00942 finished:
00943     /* If some error occurred, and no error code was set, force one. */
00944     if (!rc && GetLastError()==ERROR_SUCCESS)
00945     {
00946         SetLastError(ERROR_INVALID_DATA);
00947     }
00948 
00949     if (rc)
00950     {
00951         SetLastError(ERROR_SUCCESS);
00952 
00953         TRACE_DATA_BLOB(pDataOut);
00954     }
00955 
00956     TRACE("returning %s\n", rc ? "ok" : "FAIL");
00957 
00958     return rc;
00959 }
00960 
00961 
00962 /***************************************************************************
00963  * CryptUnprotectData   [CRYPT32.@]
00964  *
00965  * Generate Plain data and Description from given Cipher and Entropy data.
00966  *
00967  * PARAMS
00968  *  pDataIn          [I] Cipher data to be decoded
00969  *  ppszDataDescr    [O] Optional Unicode string describing the Plain data
00970  *  pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL
00971  *  pvReserved       [I] Reserved, must be NULL
00972  *  pPromptStruct    [I] Structure describing if/how to prompt during decoding
00973  *  dwFlags          [I] Flags describing options to the decoding
00974  *  pDataOut         [O] Resulting Plain data, from calls to CryptProtectData
00975  *
00976  * RETURNS
00977  *  TRUE  If a Plain was generated.
00978  *  FALSE If something failed and no Plain is available.
00979  *
00980  * FIXME
00981  *  The true Windows encryption and keying mechanisms are unknown.
00982  *
00983  *  dwFlags and pPromptStruct are currently ignored.
00984  *
00985  * NOTES
00986  *  Memory allocated in pDataOut and non-NULL ppszDataDescr must be freed
00987  *  with LocalFree.
00988  *
00989  */
00990 BOOL WINAPI CryptUnprotectData(DATA_BLOB* pDataIn,
00991                                LPWSTR * ppszDataDescr,
00992                                DATA_BLOB* pOptionalEntropy,
00993                                PVOID pvReserved,
00994                                CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
00995                                DWORD dwFlags,
00996                                DATA_BLOB* pDataOut)
00997 {
00998     BOOL rc = FALSE;
00999 
01000     HCRYPTPROV hProv;
01001     struct protect_data_t protect_data;
01002     HCRYPTHASH hHash;
01003     HCRYPTKEY hKey;
01004     DWORD dwLength;
01005 
01006     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.";
01007 
01008     TRACE("called\n");
01009 
01010     SetLastError(ERROR_SUCCESS);
01011 
01012     if (!pDataIn || !pDataOut)
01013     {
01014         SetLastError(ERROR_INVALID_PARAMETER);
01015         goto finished;
01016     }
01017     if (!pDataIn->cbData)
01018     {
01019         SetLastError(ERROR_INVALID_DATA);
01020         goto finished;
01021     }
01022 
01023     /* debug: show our arguments */
01024     report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
01025     TRACE("\tppszDataDescr: %p\n", ppszDataDescr);
01026 
01027     /* take apart the opaque blob */
01028     if (!unserialize(pDataIn, &protect_data))
01029     {
01030         SetLastError(ERROR_INVALID_DATA);
01031         FIXME("%s\n",announce_bad_opaque_data);
01032         goto finished;
01033     }
01034 
01035     /* perform basic validation on the resulting structure */
01036     if (!valid_protect_data(&protect_data))
01037     {
01038         SetLastError(ERROR_INVALID_DATA);
01039         FIXME("%s\n",announce_bad_opaque_data);
01040         goto free_protect_data;
01041     }
01042 
01043     /* get a crypt context */
01044     if (!CryptAcquireContextW(&hProv,NULL,MS_ENHANCED_PROV_W,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT))
01045     {
01046         ERR("CryptAcquireContextW failed\n");
01047         goto free_protect_data;
01048     }
01049 
01050     /* load key */
01051     if (!load_encryption_key(hProv,protect_data.cipher_key_len,&protect_data.salt,pOptionalEntropy,&hKey))
01052     {
01053         goto free_context;
01054     }
01055 
01056     /* create a hash for the decryption validation */
01057     if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash))
01058     {
01059         ERR("CryptCreateHash\n");
01060         goto free_key;
01061     }
01062 
01063     /* prepare for plaintext */
01064     pDataOut->cbData=protect_data.cipher.cbData;
01065     if (!(pDataOut->pbData=LocalAlloc( LPTR, pDataOut->cbData)))
01066     {
01067         ERR("CryptMemAlloc\n");
01068         goto free_hash;
01069     }
01070     memcpy(pDataOut->pbData,protect_data.cipher.pbData,protect_data.cipher.cbData);
01071 
01072     /* decrypt! */
01073     if (!CryptDecrypt(hKey, hHash, TRUE, 0, pDataOut->pbData,
01074                       &pDataOut->cbData) ||
01075         /* check the hash fingerprint */
01076         pDataOut->cbData > protect_data.cipher.cbData ||
01077         !hash_matches_blob(hHash, &protect_data.fingerprint))
01078     {
01079         SetLastError(ERROR_INVALID_DATA);
01080 
01081         LocalFree( pDataOut->pbData );
01082         pDataOut->pbData = NULL;
01083         pDataOut->cbData = 0;
01084 
01085         goto free_hash;
01086     }
01087 
01088     /* Copy out the description */
01089     dwLength = (lstrlenW(protect_data.szDataDescr)+1) * sizeof(WCHAR);
01090     if (ppszDataDescr)
01091     {
01092         if (!(*ppszDataDescr = LocalAlloc(LPTR,dwLength)))
01093         {
01094             ERR("LocalAlloc (ppszDataDescr)\n");
01095             goto free_hash;
01096         }
01097         else {
01098             memcpy(*ppszDataDescr,protect_data.szDataDescr,dwLength);
01099         }
01100     }
01101 
01102     /* success! */
01103     rc = TRUE;
01104 
01105 free_hash:
01106     CryptDestroyHash(hHash);
01107 free_key:
01108     CryptDestroyKey(hKey);
01109 free_context:
01110     CryptReleaseContext(hProv,0);
01111 free_protect_data:
01112     free_protect_data(&protect_data);
01113 finished:
01114     /* If some error occurred, and no error code was set, force one. */
01115     if (!rc && GetLastError()==ERROR_SUCCESS)
01116     {
01117         SetLastError(ERROR_INVALID_DATA);
01118     }
01119 
01120     if (rc) {
01121         SetLastError(ERROR_SUCCESS);
01122 
01123         if (ppszDataDescr)
01124         {
01125             TRACE("szDataDescr: %s\n",debugstr_w(*ppszDataDescr));
01126         }
01127         TRACE_DATA_BLOB(pDataOut);
01128     }
01129 
01130     TRACE("returning %s\n", rc ? "ok" : "FAIL");
01131 
01132     return rc;
01133 }

Generated on Sun May 27 2012 04:23:18 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.