Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenprotectdata.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
1.7.6.1
|