1 /*
2  * Unit test suite for crypt32.dll's CryptProtectData/CryptUnprotectData
3  *
4  * Copyright 2005 Kees Cook <kees@outflux.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "precomp.h"
22 
23 static BOOL (WINAPI *pCryptProtectData)(DATA_BLOB*,LPCWSTR,DATA_BLOB*,PVOID,CRYPTPROTECT_PROMPTSTRUCT*,DWORD,DATA_BLOB*);
24 static BOOL (WINAPI *pCryptUnprotectData)(DATA_BLOB*,LPWSTR*,DATA_BLOB*,PVOID,CRYPTPROTECT_PROMPTSTRUCT*,DWORD,DATA_BLOB*);
25 
26 static char  secret[]     = "I am a super secret string that no one can see!";
27 static char  secret2[]    = "I am a super secret string indescribable string";
28 static char  key[]        = "Wibble wibble wibble";
29 static const WCHAR desc[] = {'U','l','t','r','a',' ','s','e','c','r','e','t',' ','t','e','s','t',' ','m','e','s','s','a','g','e',0};
30 static BOOL protected = FALSE; /* if true, the unprotect tests can run */
31 static DATA_BLOB cipher;
32 static DATA_BLOB cipher_entropy;
33 static DATA_BLOB cipher_no_desc;
34 
35 static void test_cryptprotectdata(void)
36 {
37     LONG r;
38     DATA_BLOB plain;
39     DATA_BLOB entropy;
40 
41     plain.pbData=(void*)secret;
42     plain.cbData=strlen(secret)+1;
43 
44     entropy.pbData=(void*)key;
45     entropy.cbData=strlen(key)+1;
46 
47     SetLastError(0xDEADBEEF);
48     protected = pCryptProtectData(NULL,desc,NULL,NULL,NULL,0,&cipher);
49     ok(!protected, "Encrypting without plain data source.\n");
50     r = GetLastError();
51     ok(r == ERROR_INVALID_PARAMETER, "Wrong (%u) GetLastError seen\n",r);
52 
53     SetLastError(0xDEADBEEF);
54     protected = pCryptProtectData(&plain,desc,NULL,NULL,NULL,0,NULL);
55     ok(!protected, "Encrypting without cipher destination.\n");
56     r = GetLastError();
57     ok(r == ERROR_INVALID_PARAMETER, "Wrong (%u) GetLastError seen\n",r);
58 
59     cipher.pbData=NULL;
60     cipher.cbData=0;
61 
62     /* without entropy */
63     SetLastError(0xDEADBEEF);
64     protected = pCryptProtectData(&plain,desc,NULL,NULL,NULL,0,&cipher);
65     ok(protected ||
66      broken(!protected), /* Win9x/NT4 */
67      "Encrypting without entropy.\n");
68     if (protected)
69     {
70         r = GetLastError();
71         ok(r == ERROR_SUCCESS ||
72            r == ERROR_IO_PENDING, /* win2k */
73            "Expected ERROR_SUCCESS or ERROR_IO_PENDING, got %d\n",r);
74     }
75 
76     cipher_entropy.pbData=NULL;
77     cipher_entropy.cbData=0;
78 
79     /* with entropy */
80     SetLastError(0xDEADBEEF);
81     protected = pCryptProtectData(&plain,desc,&entropy,NULL,NULL,0,&cipher_entropy);
82     ok(protected ||
83      broken(!protected), /* Win9x/NT4 */
84      "Encrypting with entropy.\n");
85 
86     cipher_no_desc.pbData=NULL;
87     cipher_no_desc.cbData=0;
88 
89     /* with entropy but no description */
90     plain.pbData=(void*)secret2;
91     plain.cbData=strlen(secret2)+1;
92     SetLastError(0xDEADBEEF);
93     protected = pCryptProtectData(&plain,NULL,&entropy,NULL,NULL,0,&cipher_no_desc);
94     if (!protected)
95     {
96         /* fails in win2k */
97         ok(GetLastError() == ERROR_INVALID_PARAMETER,
98            "Expected ERROR_INVALID_PARAMETER, got %d\n", GetLastError());
99     }
100 }
101 
102 static void test_cryptunprotectdata(void)
103 {
104     LONG r;
105     DATA_BLOB plain;
106     DATA_BLOB entropy;
107     BOOL okay;
108     WCHAR * data_desc;
109 
110     entropy.pbData=(void*)key;
111     entropy.cbData=strlen(key)+1;
112 
113     /* fails in win2k */
114     if (!protected)
115     {
116         skip("CryptProtectData failed to run\n");
117         return;
118     }
119 
120     plain.pbData=NULL;
121     plain.cbData=0;
122 
123     SetLastError(0xDEADBEEF);
124     okay = pCryptUnprotectData(&cipher,NULL,NULL,NULL,NULL,0,NULL);
125     ok(!okay,"Decrypting without destination\n");
126     r = GetLastError();
127     ok(r == ERROR_INVALID_PARAMETER, "Wrong (%u) GetLastError seen\n",r);
128 
129     SetLastError(0xDEADBEEF);
130     okay = pCryptUnprotectData(NULL,NULL,NULL,NULL,NULL,0,&plain);
131     ok(!okay,"Decrypting without source\n");
132     r = GetLastError();
133     ok(r == ERROR_INVALID_PARAMETER, "Wrong (%u) GetLastError seen\n",r);
134 
135     plain.pbData=NULL;
136     plain.cbData=0;
137 
138     SetLastError(0xDEADBEEF);
139     okay = pCryptUnprotectData(&cipher_entropy,NULL,NULL,NULL,NULL,0,&plain);
140     ok(!okay,"Decrypting without needed entropy\n");
141     r = GetLastError();
142     ok(r == ERROR_INVALID_DATA, "Wrong (%u) GetLastError seen\n", r);
143 
144     plain.pbData=NULL;
145     plain.cbData=0;
146     data_desc=NULL;
147 
148     /* without entropy */
149     SetLastError(0xDEADBEEF);
150     okay = pCryptUnprotectData(&cipher,&data_desc,NULL,NULL,NULL,0,&plain);
151     ok(okay,"Decrypting without entropy\n");
152 
153     ok(plain.pbData!=NULL,"Plain DATA_BLOB missing data\n");
154     ok(plain.cbData==strlen(secret)+1,"Plain DATA_BLOB wrong length\n");
155     ok(!strcmp((const char*)plain.pbData,secret),"Plain does not match secret\n");
156     ok(data_desc!=NULL,"Description not allocated\n");
157     ok(!lstrcmpW(data_desc,desc),"Description does not match\n");
158 
159     LocalFree(plain.pbData);
160     LocalFree(data_desc);
161 
162     plain.pbData=NULL;
163     plain.cbData=0;
164     data_desc=NULL;
165 
166     /* with wrong entropy */
167     SetLastError(0xDEADBEEF);
168     okay = pCryptUnprotectData(&cipher_entropy,&data_desc,&cipher_entropy,NULL,NULL,0,&plain);
169     ok(!okay,"Decrypting with wrong entropy\n");
170     r = GetLastError();
171     ok(r == ERROR_INVALID_DATA, "Wrong (%u) GetLastError seen\n",r);
172 
173     /* with entropy */
174     SetLastError(0xDEADBEEF);
175     okay = pCryptUnprotectData(&cipher_entropy,&data_desc,&entropy,NULL,NULL,0,&plain);
176     ok(okay,"Decrypting with entropy\n");
177 
178     ok(plain.pbData!=NULL,"Plain DATA_BLOB missing data\n");
179     ok(plain.cbData==strlen(secret)+1,"Plain DATA_BLOB wrong length\n");
180     ok(!strcmp((const char*)plain.pbData,secret),"Plain does not match secret\n");
181     ok(data_desc!=NULL,"Description not allocated\n");
182     ok(!lstrcmpW(data_desc,desc),"Description does not match\n");
183 
184     LocalFree(plain.pbData);
185     LocalFree(data_desc);
186 
187     plain.pbData=NULL;
188     plain.cbData=0;
189     data_desc=NULL;
190 
191     /* with entropy but no description */
192     SetLastError(0xDEADBEEF);
193     okay = pCryptUnprotectData(&cipher_no_desc,&data_desc,&entropy,NULL,NULL,0,&plain);
194     ok(okay,"Decrypting with entropy and no description\n");
195 
196     ok(plain.pbData!=NULL,"Plain DATA_BLOB missing data\n");
197     ok(plain.cbData==strlen(secret2)+1,"Plain DATA_BLOB wrong length\n");
198     ok(!strcmp((const char*)plain.pbData,secret2),"Plain does not match secret\n");
199     ok(data_desc!=NULL,"Description not allocated\n");
200     ok(data_desc[0]=='\0',"Description not empty\n");
201 
202     LocalFree(data_desc);
203     LocalFree(plain.pbData);
204 
205     plain.pbData=NULL;
206     plain.cbData=0;
207 }
208 
209 static void test_simpleroundtrip(const char *plaintext)
210 {
211     DATA_BLOB input;
212     DATA_BLOB encrypted;
213     DATA_BLOB output;
214     int res;
215     WCHAR emptyW[1];
216 
217     emptyW[0] = 0;
218     input.pbData = (unsigned char *)plaintext;
219     input.cbData = strlen(plaintext);
220     res = pCryptProtectData(&input, emptyW, NULL, NULL, NULL, 0, &encrypted);
221     ok(res != 0 || broken(!res), "can't protect\n");
222     if (!res)
223     {
224         /* Fails on Win9x, NT4 */
225         win_skip("CryptProtectData failed\n");
226         return;
227     }
228 
229     res = pCryptUnprotectData(&encrypted, NULL, NULL, NULL, NULL, 0, &output);
230     ok(res != 0, "can't unprotect; last error %u\n", GetLastError());
231     ok(output.cbData == strlen(plaintext), "output wrong length %d for input '%s', wanted %d\n", output.cbData, plaintext, lstrlenA(plaintext));
232     ok(!memcmp(plaintext, (char *)output.pbData, output.cbData), "output wrong contents for input '%s'\n", plaintext);
233     LocalFree(output.pbData);
234     LocalFree(encrypted.pbData);
235 }
236 
237 START_TEST(protectdata)
238 {
239     HMODULE hCrypt32 = GetModuleHandleA("crypt32.dll");
240     pCryptProtectData = (void*)GetProcAddress(hCrypt32, "CryptProtectData");
241     pCryptUnprotectData = (void*)GetProcAddress(hCrypt32, "CryptUnprotectData");
242     if (!pCryptProtectData || !pCryptUnprotectData)
243     {
244         win_skip("Crypt(Un)ProtectData() is not available\n");
245         return;
246     }
247 
248     protected=FALSE;
249     test_cryptprotectdata();
250     test_cryptunprotectdata();
251     test_simpleroundtrip("");
252     test_simpleroundtrip("hello");
253 
254     /* deinit globals here */
255     if (cipher.pbData) LocalFree(cipher.pbData);
256     if (cipher_entropy.pbData) LocalFree(cipher_entropy.pbData);
257     if (cipher_no_desc.pbData) LocalFree(cipher_no_desc.pbData);
258 }
259