source: trunk/src/crypt32/protectdata.c@ 21311

Last change on this file since 21311 was 21311, checked in by vladest, 16 years ago

Added CRYPT32 and MSCMS APIs support

File size: 31.4 KB
Line 
1/*
2 * Copyright 2005 Kees Cook <kees@outflux.net>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17 */
18
19
20/*
21 * The Win32 CryptProtectData and CryptUnprotectData functions are meant
22 * to provide a mechanism for encrypting data on a machine where other users
23 * of the system can't be trusted. It is used in many examples as a way
24 * to store username and password information to the registry, but store
25 * it not in the clear.
26 *
27 * The encryption is symmetric, but the method is unknown. However, since
28 * it is keyed to the machine and the user, it is unlikely that the values
29 * would be portable. Since programs must first call CryptProtectData to
30 * get a cipher text, the underlying system doesn't have to exactly
31 * match the real Windows version. However, attempts have been made to
32 * at least try to look like the Windows version, including guesses at the
33 * purpose of various portions of the "opaque data blob" that is used.
34 *
35 */
36
37#include <stdarg.h>
38#include <stdio.h>
39#include <string.h>
40#include <stdlib.h>
41
42#include "windef.h"
43#include "winbase.h"
44#include "wincrypt.h"
45#include "winerror.h"
46#include "wine/debug.h"
47
48WINE_DEFAULT_DEBUG_CHANNEL(crypt);
49
50#define CRYPT32_PROTECTDATA_PROV PROV_RSA_FULL
51#define CRYPT32_PROTECTDATA_HASH_CALG CALG_SHA1
52#define CRYPT32_PROTECTDATA_HASH_LEN 160
53#define CRYPT32_PROTECTDATA_KEY_CALG CALG_3DES
54#define CRYPT32_PROTECTDATA_KEY_LEN 168
55#define CRYPT32_PROTECTDATA_SALT_LEN 16
56
57static const BYTE crypt32_protectdata_secret[] = {
58 'I','\'','m',' ','h','u','n','t','i','n','g',' ',
59 'w','a','b','b','i','t','s',0
60};
61
62/*
63 * The data format returned by the real Windows CryptProtectData seems
64 * to be something like this:
65
66 DWORD count0; - how many "info0_*[16]" blocks follow (was always 1)
67 BYTE info0_0[16]; - unknown information - persistent across invocations,
68 ... reboots, password changes, and users
69 DWORD count1; - how many "info1_*[16]" blocks follow (was always 1)
70 BYTE info1_0[16]; - unknown information - unique to each user, but
71 ... persistent across reboots and password changes
72 DWORD null0; - NULL "end of records"?
73 DWORD str_len; - byte length of WCHAR string including term
74 BYTE str[str_len]; - The "dataDescription" value as a NULL-terminated
75 little-endian WCHAR string
76 ALG_ID cipher_alg; - cipher algo - was CALG_3DES
77 DWORD cipher_key_len; - cipher key bit length - was 0xa8==168
78 DWORD data_len; - length of data (was 16 in samples)
79 BYTE data[data_len]; - unknown data (fingerprint?)
80 DWORD null1; - NULL ?
81 ALG_ID hash_alg; - hash algo - was CALG_SHA1
82 DWORD hash_len; - bit length of hash - was 0xa0==160
83 DWORD salt_len; - length of salt(?) data
84 BYTE salt[salt_len]; - salt(?) for symmetric encryption
85 DWORD cipher_len; - length of cipher(?) data - was close to plain len
86 BYTE cipher[cipher_len]; - cipher text?
87 DWORD crc_len; - length of fingerprint(?) data - was 20 byte==160b SHA1
88 BYTE crc[crc_len]; - fingerprint of record?
89
90 * The data structures used in Wine are modelled after this guess.
91 */
92
93struct protect_data_t
94{
95 DWORD count0;
96 DATA_BLOB info0; /* using this to hold crypt_magic_str */
97 DWORD count1;
98 DATA_BLOB info1;
99 DWORD null0;
100 WCHAR * szDataDescr; /* serialized differently than the DATA_BLOBs */
101 ALG_ID cipher_alg;
102 DWORD cipher_key_len;
103 DATA_BLOB data0;
104 DWORD null1;
105 ALG_ID hash_alg;
106 DWORD hash_len;
107 DATA_BLOB salt;
108 DATA_BLOB cipher;
109 DATA_BLOB fingerprint;
110};
111
112/* this is used to check if an incoming structure was built by Wine */
113static const char crypt_magic_str[] = "Wine Crypt32 ok";
114
115/* debugging tool to print strings of hex chars */
116static const char *
117hex_str(const unsigned char *p, int n)
118{
119#if 1
120 return NULL;
121#else
122 const char * ptr;
123 char report[80];
124 int r=-1;
125 report[0]='\0';
126 ptr = wine_dbg_sprintf("%s","");
127 while (--n >= 0)
128 {
129 if (r++ % 20 == 19)
130 {
131 ptr = wine_dbg_sprintf("%s%s",ptr,report);
132 report[0]='\0';
133 }
134 sprintf(report+strlen(report),"%s%02x", r ? "," : "", *p++);
135 }
136 return wine_dbg_sprintf("%s%s",ptr,report);
137#endif
138}
139
140#define TRACE_DATA_BLOB(blob) do { \
141 TRACE("%s cbData: %u\n", #blob ,(unsigned int)((blob)->cbData)); \
142 TRACE("%s pbData @ %p:%s\n", #blob ,(blob)->pbData, \
143 hex_str((blob)->pbData, (blob)->cbData)); \
144} while (0)
145
146static
147void serialize_dword(DWORD value,BYTE ** ptr)
148{
149 /*TRACE("called\n");*/
150
151 memcpy(*ptr,&value,sizeof(DWORD));
152 *ptr+=sizeof(DWORD);
153}
154
155static
156void serialize_string(const BYTE *str, BYTE **ptr, DWORD len, DWORD width,
157 BOOL prepend_len)
158{
159 /*TRACE("called %ux%u\n",(unsigned int)len,(unsigned int)width);*/
160
161 if (prepend_len)
162 {
163 serialize_dword(len,ptr);
164 }
165 memcpy(*ptr,str,len*width);
166 *ptr+=len*width;
167}
168
169static
170BOOL unserialize_dword(const BYTE *ptr, DWORD *index, DWORD size, DWORD *value)
171{
172 /*TRACE("called\n");*/
173
174 if (!ptr || !index || !value) return FALSE;
175
176 if (*index+sizeof(DWORD)>size)
177 {
178 return FALSE;
179 }
180
181 memcpy(value,&(ptr[*index]),sizeof(DWORD));
182 *index+=sizeof(DWORD);
183
184 return TRUE;
185}
186
187static
188BOOL unserialize_string(const BYTE *ptr, DWORD *index, DWORD size,
189 DWORD len, DWORD width, BOOL inline_len,
190 BYTE ** data, DWORD * stored)
191{
192 /*TRACE("called\n");*/
193
194 if (!ptr || !data) return FALSE;
195
196 if (inline_len) {
197 if (!unserialize_dword(ptr,index,size,&len))
198 return FALSE;
199 }
200
201 if (*index+len*width>size)
202 {
203 return FALSE;
204 }
205
206 if (!(*data = CryptMemAlloc( len*width)))
207 {
208 return FALSE;
209 }
210
211 memcpy(*data,&(ptr[*index]),len*width);
212 if (stored)
213 {
214 *stored = len;
215 }
216 *index+=len*width;
217
218 return TRUE;
219}
220
221static
222BOOL serialize(const struct protect_data_t *pInfo, DATA_BLOB *pSerial)
223{
224 BYTE * ptr;
225 DWORD dwStrLen;
226 DWORD dwStruct;
227
228 TRACE("called\n");
229
230 if (!pInfo || !pInfo->szDataDescr || !pSerial ||
231 !pInfo->info0.pbData || !pInfo->info1.pbData ||
232 !pInfo->data0.pbData || !pInfo->salt.pbData ||
233 !pInfo->cipher.pbData || !pInfo->fingerprint.pbData)
234 {
235 return FALSE;
236 }
237
238 if (pInfo->info0.cbData!=16)
239 {
240 ERR("protect_data_t info0 not 16 bytes long\n");
241 }
242
243 if (pInfo->info1.cbData!=16)
244 {
245 ERR("protect_data_t info1 not 16 bytes long\n");
246 }
247
248 dwStrLen=lstrlenW(pInfo->szDataDescr);
249
250 pSerial->cbData=0;
251 pSerial->cbData+=sizeof(DWORD)*8; /* 8 raw DWORDs */
252 pSerial->cbData+=sizeof(DWORD)*4; /* 4 BLOBs with size */
253 pSerial->cbData+=pInfo->info0.cbData;
254 pSerial->cbData+=pInfo->info1.cbData;
255 pSerial->cbData+=(dwStrLen+1)*sizeof(WCHAR) + 4; /* str, null, size */
256 pSerial->cbData+=pInfo->data0.cbData;
257 pSerial->cbData+=pInfo->salt.cbData;
258 pSerial->cbData+=pInfo->cipher.cbData;
259 pSerial->cbData+=pInfo->fingerprint.cbData;
260
261 /* save the actual structure size */
262 dwStruct = pSerial->cbData;
263 /* There may be a 256 byte minimum, but I can't prove it. */
264 /*if (pSerial->cbData<256) pSerial->cbData=256;*/
265
266 pSerial->pbData=LocalAlloc(LPTR,pSerial->cbData);
267 if (!pSerial->pbData) return FALSE;
268
269 ptr=pSerial->pbData;
270
271 /* count0 */
272 serialize_dword(pInfo->count0,&ptr);
273 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
274
275 /* info0 */
276 serialize_string(pInfo->info0.pbData,&ptr,
277 pInfo->info0.cbData,sizeof(BYTE),FALSE);
278 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
279
280 /* count1 */
281 serialize_dword(pInfo->count1,&ptr);
282 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
283
284 /* info1 */
285 serialize_string(pInfo->info1.pbData,&ptr,
286 pInfo->info1.cbData,sizeof(BYTE),FALSE);
287 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
288
289 /* null0 */
290 serialize_dword(pInfo->null0,&ptr);
291 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
292
293 /* szDataDescr */
294 serialize_string((BYTE*)pInfo->szDataDescr,&ptr,
295 (dwStrLen+1)*sizeof(WCHAR),sizeof(BYTE),TRUE);
296 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
297
298 /* cipher_alg */
299 serialize_dword(pInfo->cipher_alg,&ptr);
300 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
301 /* cipher_key_len */
302 serialize_dword(pInfo->cipher_key_len,&ptr);
303 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
304
305 /* data0 */
306 serialize_string(pInfo->data0.pbData,&ptr,
307 pInfo->data0.cbData,sizeof(BYTE),TRUE);
308 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
309
310 /* null1 */
311 serialize_dword(pInfo->null1,&ptr);
312 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
313
314 /* hash_alg */
315 serialize_dword(pInfo->hash_alg,&ptr);
316 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
317 /* hash_len */
318 serialize_dword(pInfo->hash_len,&ptr);
319 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
320
321 /* salt */
322 serialize_string(pInfo->salt.pbData,&ptr,
323 pInfo->salt.cbData,sizeof(BYTE),TRUE);
324 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
325
326 /* cipher */
327 serialize_string(pInfo->cipher.pbData,&ptr,
328 pInfo->cipher.cbData,sizeof(BYTE),TRUE);
329 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
330
331 /* fingerprint */
332 serialize_string(pInfo->fingerprint.pbData,&ptr,
333 pInfo->fingerprint.cbData,sizeof(BYTE),TRUE);
334 /*TRACE("used %u\n",ptr-pSerial->pbData);*/
335
336 if (ptr - pSerial->pbData != dwStruct)
337 {
338 ERR("struct size changed!? %u != expected %u\n",
339 ptr - pSerial->pbData, dwStruct);
340 LocalFree(pSerial->pbData);
341 pSerial->pbData=NULL;
342 pSerial->cbData=0;
343 return FALSE;
344 }
345
346 return TRUE;
347}
348
349static
350BOOL unserialize(const DATA_BLOB *pSerial, struct protect_data_t *pInfo)
351{
352 BYTE * ptr;
353 DWORD index;
354 DWORD size;
355 BOOL status=TRUE;
356
357 TRACE("called\n");
358
359 if (!pInfo || !pSerial || !pSerial->pbData)
360 return FALSE;
361
362 index=0;
363 ptr=pSerial->pbData;
364 size=pSerial->cbData;
365
366 /* count0 */
367 if (!unserialize_dword(ptr,&index,size,&pInfo->count0))
368 {
369 ERR("reading count0 failed!\n");
370 return FALSE;
371 }
372
373 /* info0 */
374 if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
375 &pInfo->info0.pbData, &pInfo->info0.cbData))
376 {
377 ERR("reading info0 failed!\n");
378 return FALSE;
379 }
380
381 /* count1 */
382 if (!unserialize_dword(ptr,&index,size,&pInfo->count1))
383 {
384 ERR("reading count1 failed!\n");
385 return FALSE;
386 }
387
388 /* info1 */
389 if (!unserialize_string(ptr,&index,size,16,sizeof(BYTE),FALSE,
390 &pInfo->info1.pbData, &pInfo->info1.cbData))
391 {
392 ERR("reading info1 failed!\n");
393 return FALSE;
394 }
395
396 /* null0 */
397 if (!unserialize_dword(ptr,&index,size,&pInfo->null0))
398 {
399 ERR("reading null0 failed!\n");
400 return FALSE;
401 }
402
403 /* szDataDescr */
404 if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
405 (BYTE**)&pInfo->szDataDescr, NULL))
406 {
407 ERR("reading szDataDescr failed!\n");
408 return FALSE;
409 }
410
411 /* cipher_alg */
412 if (!unserialize_dword(ptr,&index,size,&pInfo->cipher_alg))
413 {
414 ERR("reading cipher_alg failed!\n");
415 return FALSE;
416 }
417
418 /* cipher_key_len */
419 if (!unserialize_dword(ptr,&index,size,&pInfo->cipher_key_len))
420 {
421 ERR("reading cipher_key_len failed!\n");
422 return FALSE;
423 }
424
425 /* data0 */
426 if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
427 &pInfo->data0.pbData, &pInfo->data0.cbData))
428 {
429 ERR("reading data0 failed!\n");
430 return FALSE;
431 }
432
433 /* null1 */
434 if (!unserialize_dword(ptr,&index,size,&pInfo->null1))
435 {
436 ERR("reading null1 failed!\n");
437 return FALSE;
438 }
439
440 /* hash_alg */
441 if (!unserialize_dword(ptr,&index,size,&pInfo->hash_alg))
442 {
443 ERR("reading hash_alg failed!\n");
444 return FALSE;
445 }
446
447 /* hash_len */
448 if (!unserialize_dword(ptr,&index,size,&pInfo->hash_len))
449 {
450 ERR("reading hash_len failed!\n");
451 return FALSE;
452 }
453
454 /* salt */
455 if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
456 &pInfo->salt.pbData, &pInfo->salt.cbData))
457 {
458 ERR("reading salt failed!\n");
459 return FALSE;
460 }
461
462 /* cipher */
463 if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
464 &pInfo->cipher.pbData, &pInfo->cipher.cbData))
465 {
466 ERR("reading cipher failed!\n");
467 return FALSE;
468 }
469
470 /* fingerprint */
471 if (!unserialize_string(ptr,&index,size,0,sizeof(BYTE),TRUE,
472 &pInfo->fingerprint.pbData, &pInfo->fingerprint.cbData))
473 {
474 ERR("reading fingerprint failed!\n");
475 return FALSE;
476 }
477
478 /* allow structure size to be too big (since some applications
479 * will pad this up to 256 bytes, it seems) */
480 if (index>size)
481 {
482 /* this is an impossible-to-reach test, but if the padding
483 * issue is ever understood, this may become more useful */
484 ERR("loaded corrupt structure! (used %u expected %u)\n", index, size);
485 status=FALSE;
486 }
487
488 return status;
489}
490
491/* perform sanity checks */
492static
493BOOL valid_protect_data(const struct protect_data_t *pInfo)
494{
495 BOOL status=TRUE;
496
497 TRACE("called\n");
498
499 if (pInfo->count0 != 0x0001)
500 {
501 ERR("count0 != 0x0001 !\n");
502 status=FALSE;
503 }
504 if (pInfo->count1 != 0x0001)
505 {
506 ERR("count0 != 0x0001 !\n");
507 status=FALSE;
508 }
509 if (pInfo->null0 != 0x0000)
510 {
511 ERR("null0 != 0x0000 !\n");
512 status=FALSE;
513 }
514 if (pInfo->null1 != 0x0000)
515 {
516 ERR("null1 != 0x0000 !\n");
517 status=FALSE;
518 }
519 /* since we have no idea what info0 is used for, and it seems
520 * rather constant, we can test for a Wine-specific magic string
521 * there to be reasonably sure we're using data created by the Wine
522 * implementation of CryptProtectData.
523 */
524 if (pInfo->info0.cbData!=strlen(crypt_magic_str)+1 ||
525 strcmp( (LPCSTR)pInfo->info0.pbData,crypt_magic_str) != 0)
526 {
527 ERR("info0 magic value not matched !\n");
528 status=FALSE;
529 }
530
531 if (!status)
532 {
533 ERR("unrecognized CryptProtectData block\n");
534 }
535
536 return status;
537}
538
539static
540void free_protect_data(struct protect_data_t * pInfo)
541{
542 TRACE("called\n");
543
544 if (!pInfo) return;
545
546 CryptMemFree(pInfo->info0.pbData);
547 CryptMemFree(pInfo->info1.pbData);
548 CryptMemFree(pInfo->szDataDescr);
549 CryptMemFree(pInfo->data0.pbData);
550 CryptMemFree(pInfo->salt.pbData);
551 CryptMemFree(pInfo->cipher.pbData);
552 CryptMemFree(pInfo->fingerprint.pbData);
553}
554
555/* copies a string into a data blob */
556static
557BYTE *convert_str_to_blob(LPCSTR str, DATA_BLOB *blob)
558{
559 if (!str || !blob) return NULL;
560
561 blob->cbData=strlen(str)+1;
562 if (!(blob->pbData=CryptMemAlloc(blob->cbData)))
563 {
564 blob->cbData=0;
565 }
566 else {
567 strcpy((LPSTR)blob->pbData, str);
568 }
569
570 return blob->pbData;
571}
572
573/*
574 * Populates everything except "cipher" and "fingerprint".
575 */
576static
577BOOL fill_protect_data(struct protect_data_t * pInfo, LPCWSTR szDataDescr,
578 HCRYPTPROV hProv)
579{
580 DWORD dwStrLen;
581
582 TRACE("called\n");
583
584 if (!pInfo) return FALSE;
585
586 dwStrLen=lstrlenW(szDataDescr);
587
588 memset(pInfo,0,sizeof(*pInfo));
589
590 pInfo->count0=0x0001;
591
592 convert_str_to_blob(crypt_magic_str, &pInfo->info0);
593
594 pInfo->count1=0x0001;
595
596 convert_str_to_blob(crypt_magic_str, &pInfo->info1);
597
598 pInfo->null0=0x0000;
599
600 if ((pInfo->szDataDescr=CryptMemAlloc((dwStrLen+1)*sizeof(WCHAR))))
601 {
602 memcpy(pInfo->szDataDescr,szDataDescr,(dwStrLen+1)*sizeof(WCHAR));
603 }
604
605 pInfo->cipher_alg=CRYPT32_PROTECTDATA_KEY_CALG;
606 pInfo->cipher_key_len=CRYPT32_PROTECTDATA_KEY_LEN;
607
608 convert_str_to_blob(crypt_magic_str, &pInfo->data0);
609
610 pInfo->null1=0x0000;
611 pInfo->hash_alg=CRYPT32_PROTECTDATA_HASH_CALG;
612 pInfo->hash_len=CRYPT32_PROTECTDATA_HASH_LEN;
613
614 /* allocate memory to hold a salt */
615 if ((pInfo->salt.pbData=CryptMemAlloc(CRYPT32_PROTECTDATA_SALT_LEN)))
616 {
617 /* generate random salt */
618 if (!CryptGenRandom(hProv, pInfo->salt.cbData, pInfo->salt.pbData))
619 {
620 ERR("CryptGenRandom\n");
621 free_protect_data(pInfo);
622 return FALSE;
623 }
624 pInfo->salt.cbData=CRYPT32_PROTECTDATA_SALT_LEN;
625 }
626
627 /* debug: show our salt */
628 TRACE_DATA_BLOB(&pInfo->salt);
629
630 pInfo->cipher.cbData=0;
631 pInfo->cipher.pbData=NULL;
632
633 pInfo->fingerprint.cbData=0;
634 pInfo->fingerprint.pbData=NULL;
635
636 /* check all the allocations at once */
637 if (!pInfo->info0.pbData ||
638 !pInfo->info1.pbData ||
639 !pInfo->szDataDescr ||
640 !pInfo->data0.pbData ||
641 !pInfo->salt.pbData
642 )
643 {
644 ERR("could not allocate protect_data structures\n");
645 free_protect_data(pInfo);
646 return FALSE;
647 }
648
649 return TRUE;
650}
651
652static
653BOOL convert_hash_to_blob(HCRYPTHASH hHash, DATA_BLOB * blob)
654{
655 DWORD dwSize;
656
657 TRACE("called\n");
658
659 if (!blob) return FALSE;
660
661 dwSize=sizeof(DWORD);
662 if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&blob->cbData,
663 &dwSize, 0))
664 {
665 ERR("failed to get hash size\n");
666 return FALSE;
667 }
668
669 if (!(blob->pbData=CryptMemAlloc(blob->cbData)))
670 {
671 ERR("failed to allocate blob memory\n");
672 return FALSE;
673 }
674
675 dwSize=blob->cbData;
676 if (!CryptGetHashParam(hHash, HP_HASHVAL, blob->pbData, &dwSize, 0))
677 {
678 ERR("failed to get hash value\n");
679 CryptMemFree(blob->pbData);
680 blob->pbData=NULL;
681 blob->cbData=0;
682 return FALSE;
683 }
684
685 return TRUE;
686}
687
688/* test that a given hash matches an exported-to-blob hash value */
689static
690BOOL hash_matches_blob(HCRYPTHASH hHash, const DATA_BLOB *two)
691{
692 BOOL rc = FALSE;
693 DATA_BLOB one;
694
695 if (!two || !two->pbData) return FALSE;
696
697 if (!convert_hash_to_blob(hHash,&one)) {
698 return FALSE;
699 }
700
701 if ( one.cbData == two->cbData &&
702 memcmp( one.pbData, two->pbData, one.cbData ) == 0 )
703 {
704 rc = TRUE;
705 }
706
707 CryptMemFree(one.pbData);
708 return rc;
709}
710
711/* create an encryption key from a given salt and optional entropy */
712static
713BOOL load_encryption_key(HCRYPTPROV hProv, DWORD key_len, const DATA_BLOB *salt,
714 const DATA_BLOB *pOptionalEntropy, HCRYPTKEY *phKey)
715{
716 BOOL rc = TRUE;
717 HCRYPTHASH hSaltHash;
718 char * szUsername = NULL;
719 DWORD dwUsernameLen;
720 DWORD dwError;
721
722 /* create hash for salt */
723 if (!salt || !phKey ||
724 !CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hSaltHash))
725 {
726 ERR("CryptCreateHash\n");
727 return FALSE;
728 }
729
730 /* This should be the "logon credentials" instead of username */
731 dwError=GetLastError();
732 dwUsernameLen = 0;
733 if (!GetUserNameA(NULL,&dwUsernameLen) &&
734 GetLastError()==ERROR_MORE_DATA && dwUsernameLen &&
735 (szUsername = CryptMemAlloc(dwUsernameLen)))
736 {
737 szUsername[0]='\0';
738 GetUserNameA( szUsername, &dwUsernameLen );
739 }
740 SetLastError(dwError);
741
742 /* salt the hash with:
743 * - the user id
744 * - an "internal secret"
745 * - randomness (from the salt)
746 * - user-supplied entropy
747 */
748 if ((szUsername && !CryptHashData(hSaltHash,(LPBYTE)szUsername,dwUsernameLen,0)) ||
749 !CryptHashData(hSaltHash,crypt32_protectdata_secret,
750 sizeof(crypt32_protectdata_secret)-1,0) ||
751 !CryptHashData(hSaltHash,salt->pbData,salt->cbData,0) ||
752 (pOptionalEntropy && !CryptHashData(hSaltHash,
753 pOptionalEntropy->pbData,
754 pOptionalEntropy->cbData,0)))
755 {
756 ERR("CryptHashData\n");
757 rc = FALSE;
758 }
759
760 /* produce a symmetric key */
761 if (rc && !CryptDeriveKey(hProv,CRYPT32_PROTECTDATA_KEY_CALG,
762 hSaltHash,key_len << 16 | CRYPT_EXPORTABLE,phKey))
763 {
764 ERR("CryptDeriveKey\n");
765 rc = FALSE;
766 }
767
768 /* clean up */
769 CryptDestroyHash(hSaltHash);
770 CryptMemFree(szUsername);
771
772 return rc;
773}
774
775/* debugging tool to print the structures of a ProtectData call */
776static void
777report(const DATA_BLOB* pDataIn, const DATA_BLOB* pOptionalEntropy,
778 CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags)
779{
780 TRACE("pPromptStruct: %p\n", pPromptStruct);
781 if (pPromptStruct)
782 {
783 TRACE(" cbSize: 0x%x\n", pPromptStruct->cbSize);
784 TRACE(" dwPromptFlags: 0x%x\n", pPromptStruct->dwPromptFlags);
785 TRACE(" hwndApp: %p\n", pPromptStruct->hwndApp);
786 TRACE(" szPrompt: %p %s\n",
787 pPromptStruct->szPrompt,
788 pPromptStruct->szPrompt ? debugstr_w(pPromptStruct->szPrompt)
789 : "");
790 }
791 TRACE("dwFlags: 0x%04x\n", dwFlags);
792 TRACE_DATA_BLOB(pDataIn);
793 if (pOptionalEntropy)
794 {
795 TRACE_DATA_BLOB(pOptionalEntropy);
796 TRACE(" %s\n",debugstr_an((LPCSTR)pOptionalEntropy->pbData,pOptionalEntropy->cbData));
797 }
798
799}
800
801
802/***************************************************************************
803 * CryptProtectData [CRYPT32.@]
804 *
805 * Generate Cipher data from given Plain and Entropy data.
806 *
807 * PARAMS
808 * pDataIn [I] Plain data to be enciphered
809 * szDataDescr [I] Optional Unicode string describing the Plain data
810 * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL
811 * pvReserved [I] Reserved, must be NULL
812 * pPromptStruct [I] Structure describing if/how to prompt during ciphering
813 * dwFlags [I] Flags describing options to the ciphering
814 * pDataOut [O] Resulting Cipher data, for calls to CryptUnprotectData
815 *
816 * RETURNS
817 * TRUE If a Cipher was generated.
818 * FALSE If something failed and no Cipher is available.
819 *
820 * FIXME
821 * The true Windows encryption and keying mechanisms are unknown.
822 *
823 * dwFlags and pPromptStruct are currently ignored.
824 *
825 * NOTES
826 * Memory allocated in pDataOut must be freed with LocalFree.
827 *
828 */
829BOOL WINAPI CryptProtectData(DATA_BLOB* pDataIn,
830 LPCWSTR szDataDescr,
831 DATA_BLOB* pOptionalEntropy,
832 PVOID pvReserved,
833 CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
834 DWORD dwFlags,
835 DATA_BLOB* pDataOut)
836{
837 static const WCHAR empty_str[1];
838 BOOL rc = FALSE;
839 HCRYPTPROV hProv;
840 struct protect_data_t protect_data;
841 HCRYPTHASH hHash;
842 HCRYPTKEY hKey;
843 DWORD dwLength;
844
845 TRACE("called\n");
846
847 SetLastError(ERROR_SUCCESS);
848
849 if (!pDataIn || !pDataOut)
850 {
851 SetLastError(ERROR_INVALID_PARAMETER);
852 goto finished;
853 }
854
855 /* debug: show our arguments */
856 report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
857 TRACE("\tszDataDescr: %p %s\n", szDataDescr,
858 szDataDescr ? debugstr_w(szDataDescr) : "");
859
860 /* Windows appears to create an empty szDataDescr instead of maintaining
861 * a NULL */
862 if (!szDataDescr)
863 szDataDescr = empty_str;
864
865 /* get crypt context */
866 if (!CryptAcquireContextW(&hProv,NULL,MS_ENHANCED_PROV_W,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT))
867 {
868 ERR("CryptAcquireContextW failed\n");
869 goto finished;
870 }
871
872 /* populate our structure */
873 if (!fill_protect_data(&protect_data,szDataDescr,hProv))
874 {
875 ERR("fill_protect_data\n");
876 goto free_context;
877 }
878
879 /* load key */
880 if (!load_encryption_key(hProv,protect_data.cipher_key_len,&protect_data.salt,pOptionalEntropy,&hKey))
881 {
882 goto free_protect_data;
883 }
884
885 /* create a hash for the encryption validation */
886 if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash))
887 {
888 ERR("CryptCreateHash\n");
889 goto free_key;
890 }
891
892 /* calculate storage required */
893 dwLength=pDataIn->cbData;
894 if (CryptEncrypt(hKey, 0, TRUE, 0, pDataIn->pbData, &dwLength, 0) ||
895 GetLastError()!=ERROR_MORE_DATA)
896 {
897 ERR("CryptEncrypt\n");
898 goto free_hash;
899 }
900 TRACE("required encrypted storage: %u\n", dwLength);
901
902 /* copy plain text into cipher area for CryptEncrypt call */
903 protect_data.cipher.cbData=dwLength;
904 if (!(protect_data.cipher.pbData=CryptMemAlloc(
905 protect_data.cipher.cbData)))
906 {
907 ERR("CryptMemAlloc\n");
908 goto free_hash;
909 }
910 memcpy(protect_data.cipher.pbData,pDataIn->pbData,pDataIn->cbData);
911
912 /* encrypt! */
913 dwLength=pDataIn->cbData;
914 if (!CryptEncrypt(hKey, hHash, TRUE, 0, protect_data.cipher.pbData,
915 &dwLength, protect_data.cipher.cbData))
916 {
917 ERR("CryptEncrypt %u\n", GetLastError());
918 goto free_hash;
919 }
920 protect_data.cipher.cbData=dwLength;
921
922 /* debug: show the cipher */
923 TRACE_DATA_BLOB(&protect_data.cipher);
924
925 /* attach our fingerprint */
926 if (!convert_hash_to_blob(hHash, &protect_data.fingerprint))
927 {
928 ERR("convert_hash_to_blob\n");
929 goto free_hash;
930 }
931
932 /* serialize into an opaque blob */
933 if (!serialize(&protect_data, pDataOut))
934 {
935 ERR("serialize\n");
936 goto free_hash;
937 }
938
939 /* success! */
940 rc=TRUE;
941
942free_hash:
943 CryptDestroyHash(hHash);
944free_key:
945 CryptDestroyKey(hKey);
946free_protect_data:
947 free_protect_data(&protect_data);
948free_context:
949 CryptReleaseContext(hProv,0);
950finished:
951 /* If some error occurred, and no error code was set, force one. */
952 if (!rc && GetLastError()==ERROR_SUCCESS)
953 {
954 SetLastError(ERROR_INVALID_DATA);
955 }
956
957 if (rc)
958 {
959 SetLastError(ERROR_SUCCESS);
960
961 TRACE_DATA_BLOB(pDataOut);
962 }
963
964 TRACE("returning %s\n", rc ? "ok" : "FAIL");
965
966 return rc;
967}
968
969
970/***************************************************************************
971 * CryptUnprotectData [CRYPT32.@]
972 *
973 * Generate Plain data and Description from given Cipher and Entropy data.
974 *
975 * PARAMS
976 * pDataIn [I] Cipher data to be decoded
977 * ppszDataDescr [O] Optional Unicode string describing the Plain data
978 * pOptionalEntropy [I] Optional entropy data to adjust cipher, can be NULL
979 * pvReserved [I] Reserved, must be NULL
980 * pPromptStruct [I] Structure describing if/how to prompt during decoding
981 * dwFlags [I] Flags describing options to the decoding
982 * pDataOut [O] Resulting Plain data, from calls to CryptProtectData
983 *
984 * RETURNS
985 * TRUE If a Plain was generated.
986 * FALSE If something failed and no Plain is available.
987 *
988 * FIXME
989 * The true Windows encryption and keying mechanisms are unknown.
990 *
991 * dwFlags and pPromptStruct are currently ignored.
992 *
993 * NOTES
994 * Memory allocated in pDataOut and non-NULL ppszDataDescr must be freed
995 * with LocalFree.
996 *
997 */
998BOOL WINAPI CryptUnprotectData(DATA_BLOB* pDataIn,
999 LPWSTR * ppszDataDescr,
1000 DATA_BLOB* pOptionalEntropy,
1001 PVOID pvReserved,
1002 CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct,
1003 DWORD dwFlags,
1004 DATA_BLOB* pDataOut)
1005{
1006 BOOL rc = FALSE;
1007
1008 HCRYPTPROV hProv;
1009 struct protect_data_t protect_data;
1010 HCRYPTHASH hHash;
1011 HCRYPTKEY hKey;
1012 DWORD dwLength;
1013
1014 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.";
1015
1016 TRACE("called\n");
1017
1018 SetLastError(ERROR_SUCCESS);
1019
1020 if (!pDataIn || !pDataOut)
1021 {
1022 SetLastError(ERROR_INVALID_PARAMETER);
1023 goto finished;
1024 }
1025 if (!pDataIn->cbData)
1026 {
1027 SetLastError(ERROR_INVALID_DATA);
1028 goto finished;
1029 }
1030
1031 /* debug: show our arguments */
1032 report(pDataIn,pOptionalEntropy,pPromptStruct,dwFlags);
1033 TRACE("\tppszDataDescr: %p\n", ppszDataDescr);
1034
1035 /* take apart the opaque blob */
1036 if (!unserialize(pDataIn, &protect_data))
1037 {
1038 SetLastError(ERROR_INVALID_DATA);
1039 FIXME("%s\n",announce_bad_opaque_data);
1040 goto finished;
1041 }
1042
1043 /* perform basic validation on the resulting structure */
1044 if (!valid_protect_data(&protect_data))
1045 {
1046 SetLastError(ERROR_INVALID_DATA);
1047 FIXME("%s\n",announce_bad_opaque_data);
1048 goto free_protect_data;
1049 }
1050
1051 /* get a crypt context */
1052 if (!CryptAcquireContextW(&hProv,NULL,MS_ENHANCED_PROV_W,CRYPT32_PROTECTDATA_PROV,CRYPT_VERIFYCONTEXT))
1053 {
1054 ERR("CryptAcquireContextW failed\n");
1055 goto free_protect_data;
1056 }
1057
1058 /* load key */
1059 if (!load_encryption_key(hProv,protect_data.cipher_key_len,&protect_data.salt,pOptionalEntropy,&hKey))
1060 {
1061 goto free_context;
1062 }
1063
1064 /* create a hash for the decryption validation */
1065 if (!CryptCreateHash(hProv,CRYPT32_PROTECTDATA_HASH_CALG,0,0,&hHash))
1066 {
1067 ERR("CryptCreateHash\n");
1068 goto free_key;
1069 }
1070
1071 /* prepare for plaintext */
1072 pDataOut->cbData=protect_data.cipher.cbData;
1073 if (!(pDataOut->pbData=LocalAlloc( LPTR, pDataOut->cbData)))
1074 {
1075 ERR("CryptMemAlloc\n");
1076 goto free_hash;
1077 }
1078 memcpy(pDataOut->pbData,protect_data.cipher.pbData,protect_data.cipher.cbData);
1079
1080 /* decrypt! */
1081 if (!CryptDecrypt(hKey, hHash, TRUE, 0, pDataOut->pbData,
1082 &pDataOut->cbData) ||
1083 /* check the hash fingerprint */
1084 pDataOut->cbData > protect_data.cipher.cbData ||
1085 !hash_matches_blob(hHash, &protect_data.fingerprint))
1086 {
1087 SetLastError(ERROR_INVALID_DATA);
1088
1089 LocalFree( pDataOut->pbData );
1090 pDataOut->pbData = NULL;
1091 pDataOut->cbData = 0;
1092
1093 goto free_hash;
1094 }
1095
1096 /* Copy out the description */
1097 dwLength = (lstrlenW(protect_data.szDataDescr)+1) * sizeof(WCHAR);
1098 if (ppszDataDescr)
1099 {
1100 if (!(*ppszDataDescr = LocalAlloc(LPTR,dwLength)))
1101 {
1102 ERR("LocalAlloc (ppszDataDescr)\n");
1103 goto free_hash;
1104 }
1105 else {
1106 memcpy(*ppszDataDescr,protect_data.szDataDescr,dwLength);
1107 }
1108 }
1109
1110 /* success! */
1111 rc = TRUE;
1112
1113free_hash:
1114 CryptDestroyHash(hHash);
1115free_key:
1116 CryptDestroyKey(hKey);
1117free_context:
1118 CryptReleaseContext(hProv,0);
1119free_protect_data:
1120 free_protect_data(&protect_data);
1121finished:
1122 /* If some error occurred, and no error code was set, force one. */
1123 if (!rc && GetLastError()==ERROR_SUCCESS)
1124 {
1125 SetLastError(ERROR_INVALID_DATA);
1126 }
1127
1128 if (rc) {
1129 SetLastError(ERROR_SUCCESS);
1130
1131 if (ppszDataDescr)
1132 {
1133 TRACE("szDataDescr: %s\n",debugstr_w(*ppszDataDescr));
1134 }
1135 TRACE_DATA_BLOB(pDataOut);
1136 }
1137
1138 TRACE("returning %s\n", rc ? "ok" : "FAIL");
1139
1140 return rc;
1141}
Note: See TracBrowser for help on using the repository browser.