- encode/decode X509_NAMEs for simple string encodings, with tests
[wine] / dlls / crypt32 / tests / encode.c
1 /*
2  * Unit test suite for crypt32.dll's CryptEncodeObjectEx/CryptDecodeObjectEx
3  *
4  * Copyright 2005 Juan Lang
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20 #include <stdio.h>
21 #include <stdarg.h>
22 #include <windef.h>
23 #include <winbase.h>
24 #include <winerror.h>
25 #include <wincrypt.h>
26
27 #include "wine/test.h"
28
29 struct encodedInt
30 {
31     int val;
32     BYTE encoded[6];
33 };
34
35 static const struct encodedInt ints[] = {
36  { 1,          { 2, 1, 1 } },
37  { 127,        { 2, 1, 0x7f } },
38  { 128,        { 2, 2, 0x00, 0x80 } },
39  { 256,        { 2, 2, 0x01, 0x00 } },
40  { -128,       { 2, 1, 0x80 } },
41  { -129,       { 2, 2, 0xff, 0x7f } },
42  { 0xbaddf00d, { 2, 4, 0xba, 0xdd, 0xf0, 0x0d } },
43 };
44
45 static void test_encodeint(DWORD dwEncoding)
46 {
47     DWORD bufSize = 0;
48     int i;
49     BOOL ret;
50
51     /* CryptEncodeObjectEx with NULL bufSize crashes..
52     ret = CryptEncodeObjectEx(3, X509_INTEGER, &ints[0].val, 0, NULL, NULL,
53      NULL);
54      */
55     /* check bogus encoding */
56     ret = CryptEncodeObjectEx(0, X509_INTEGER, &ints[0].val, 0, NULL, NULL,
57      &bufSize);
58     ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
59      "Expected ERROR_FILE_NOT_FOUND, got %ld\n", GetLastError());
60     /* check with NULL integer buffer.  Windows XP incorrectly returns an
61      * NTSTATUS.
62      */
63     ret = CryptEncodeObjectEx(dwEncoding, X509_INTEGER, NULL, 0, NULL, NULL,
64      &bufSize);
65     ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
66      "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
67     for (i = 0; i < sizeof(ints) / sizeof(ints[0]); i++)
68     {
69         BYTE *buf = NULL;
70
71         ret = CryptEncodeObjectEx(dwEncoding, X509_INTEGER, &ints[i].val, 0,
72          NULL, NULL, &bufSize);
73         ok(ret, "Expected success, got %ld\n", GetLastError());
74         ret = CryptEncodeObjectEx(dwEncoding, X509_INTEGER, &ints[i].val,
75          CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
76         ok(ret, "CryptEncodeObjectEx failed: %ld\n", GetLastError());
77         if (buf)
78         {
79             ok(buf[0] == 2, "Got unexpected type %d for integer (expected 2)\n",
80              buf[0]);
81             ok(!memcmp(buf + 1, ints[i].encoded + 1, ints[i].encoded[1] + 1),
82              "Encoded value of 0x%08x didn't match expected\n", ints[i].val);
83             LocalFree(buf);
84         }
85     }
86 }
87
88 static void test_decodeint(DWORD dwEncoding)
89 {
90     static const char bigInt[] = { 2, 5, 0xff, 0xfe, 0xff, 0xfe, 0xff };
91     static const char testStr[] = { 0x16, 4, 't', 'e', 's', 't' };
92     BYTE *buf = NULL;
93     DWORD bufSize = 0;
94     int i;
95     BOOL ret;
96
97     /* CryptDecodeObjectEx with NULL bufSize crashes..
98     ret = CryptDecodeObjectEx(3, X509_INTEGER, &ints[0].encoded, 
99      ints[0].encoded[1] + 2, 0, NULL, NULL, NULL);
100      */
101     /* check bogus encoding */
102     ret = CryptDecodeObjectEx(3, X509_INTEGER, (BYTE *)&ints[0].encoded, 
103      ints[0].encoded[1] + 2, 0, NULL, NULL, &bufSize);
104     ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
105      "Expected ERROR_FILE_NOT_FOUND, got %ld\n", GetLastError());
106     /* check with NULL integer buffer */
107     ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER, NULL, 0, 0, NULL, NULL,
108      &bufSize);
109     ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD,
110      "Expected CRYPT_E_ASN1_EOD, got %08lx\n", GetLastError());
111     /* check with a valid, but too large, integer */
112     ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER, bigInt, bigInt[1] + 2,
113      CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
114     ok(!ret && GetLastError() == CRYPT_E_ASN1_LARGE,
115      "Expected CRYPT_E_ASN1_LARGE, got %ld\n", GetLastError());
116     /* check with a DER-encoded string */
117     ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER, testStr, testStr[1] + 2,
118      CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
119     ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG,
120      "Expected CRYPT_E_ASN1_BADTAG, got %ld\n", GetLastError());
121     for (i = 0; i < sizeof(ints) / sizeof(ints[0]); i++)
122     {
123         /* When the output buffer is NULL, this always succeeds */
124         SetLastError(0xdeadbeef);
125         ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER,
126          (BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2, 0, NULL, NULL,
127          &bufSize);
128         ok(ret && GetLastError() == NOERROR,
129          "Expected success and NOERROR, got %ld\n", GetLastError());
130         ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER,
131          (BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2,
132          CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
133         ok(ret, "CryptDecodeObjectEx failed: %ld\n", GetLastError());
134         ok(bufSize == sizeof(int), "Expected size %d, got %ld\n", sizeof(int),
135          bufSize);
136         ok(buf != NULL, "Expected allocated buffer\n");
137         if (buf)
138         {
139             ok(!memcmp(buf, &ints[i].val, bufSize), "Expected %d, got %d\n",
140              ints[i].val, *(int *)buf);
141             LocalFree(buf);
142         }
143     }
144 }
145
146 struct encodedFiletime
147 {
148     SYSTEMTIME sysTime;
149     BYTE *encodedTime;
150 };
151
152 static void testTimeEncoding(DWORD dwEncoding, LPCSTR structType,
153  const struct encodedFiletime *time)
154 {
155     FILETIME ft = { 0 };
156     BYTE *buf = NULL;
157     DWORD bufSize = 0;
158     BOOL ret;
159
160     ret = SystemTimeToFileTime(&time->sysTime, &ft);
161     ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
162     ret = CryptEncodeObjectEx(dwEncoding, structType, &ft,
163      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
164     /* years other than 1950-2050 are not allowed for encodings other than
165      * X509_CHOICE_OF_TIME.
166      */
167     if (structType == X509_CHOICE_OF_TIME ||
168      (time->sysTime.wYear >= 1950 && time->sysTime.wYear <= 2050))
169     {
170         ok(ret, "CryptEncodeObjectEx failed: %ld (0x%08lx)\n", GetLastError(),
171          GetLastError());
172         ok(buf != NULL, "Expected an allocated buffer\n");
173         if (buf)
174         {
175             ok(buf[0] == time->encodedTime[0],
176              "Expected type 0x%02x, got 0x%02x\n", time->encodedTime[0],
177              buf[0]);
178             ok(buf[1] == time->encodedTime[1], "Expected %d bytes, got %ld\n",
179              time->encodedTime[1], bufSize);
180             ok(!memcmp(time->encodedTime + 2, buf + 2, time->encodedTime[1]),
181              "Got unexpected value for time encoding\n");
182             LocalFree(buf);
183         }
184     }
185     else
186         ok(!ret && GetLastError() == CRYPT_E_BAD_ENCODE,
187          "Expected CRYPT_E_BAD_ENCODE, got 0x%08lx\n", GetLastError());
188 }
189
190 static void testTimeDecoding(DWORD dwEncoding, LPCSTR structType,
191  const struct encodedFiletime *time)
192 {
193     FILETIME ft1 = { 0 }, ft2 = { 0 };
194     DWORD size = sizeof(ft2);
195     BOOL ret;
196
197     ret = SystemTimeToFileTime(&time->sysTime, &ft1);
198     ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
199     ret = CryptDecodeObjectEx(dwEncoding, structType, time->encodedTime,
200      time->encodedTime[1] + 2, 0, NULL, &ft2, &size);
201     /* years other than 1950-2050 are not allowed for encodings other than
202      * X509_CHOICE_OF_TIME.
203      */
204     if (structType == X509_CHOICE_OF_TIME ||
205      (time->sysTime.wYear >= 1950 && time->sysTime.wYear <= 2050))
206     {
207         ok(ret, "CryptDecodeObjectEx failed: %ld (0x%08lx)\n", GetLastError(),
208          GetLastError());
209         ok(!memcmp(&ft1, &ft2, sizeof(ft1)),
210          "Got unexpected value for time decoding\n");
211     }
212     else
213         ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG,
214          "Expected CRYPT_E_ASN1_BADTAG, got 0x%08lx\n", GetLastError());
215 }
216
217 static const struct encodedFiletime times[] = {
218  { { 2005, 6, 1, 6, 16, 10, 0, 0 }, "\x17" "\x0d" "050606161000Z" },
219  { { 1945, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x0f" "19450606161000Z" },
220  { { 2145, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x0f" "21450606161000Z" },
221 };
222
223 static void test_encodeFiletime(DWORD dwEncoding)
224 {
225     DWORD i;
226
227     for (i = 0; i < sizeof(times) / sizeof(times[0]); i++)
228     {
229         testTimeEncoding(dwEncoding, X509_CHOICE_OF_TIME, &times[i]);
230         testTimeEncoding(dwEncoding, PKCS_UTC_TIME, &times[i]);
231         testTimeEncoding(dwEncoding, szOID_RSA_signingTime, &times[i]);
232     }
233 }
234
235 static void test_decodeFiletime(DWORD dwEncoding)
236 {
237     static const struct encodedFiletime otherTimes[] = {
238      { { 1945, 6, 1, 6, 16, 10, 0, 0 },   "\x18" "\x13" "19450606161000.000Z" },
239      { { 1945, 6, 1, 6, 16, 10, 0, 999 }, "\x18" "\x13" "19450606161000.999Z" },
240      { { 1945, 6, 1, 6, 17, 10, 0, 0 },   "\x18" "\x13" "19450606161000+0100" },
241      { { 1945, 6, 1, 6, 15, 10, 0, 0 },   "\x18" "\x13" "19450606161000-0100" },
242      { { 1945, 6, 1, 6, 14, 55, 0, 0 },   "\x18" "\x13" "19450606161000-0115" },
243      { { 2145, 6, 1, 6, 16,  0, 0, 0 },   "\x18" "\x0a" "2145060616" },
244      { { 2045, 6, 1, 6, 16, 10, 0, 0 },   "\x17" "\x0a" "4506061610" },
245      { { 2045, 6, 1, 6, 16, 10, 0, 0 },   "\x17" "\x0b" "4506061610Z" },
246      { { 2045, 6, 1, 6, 17, 10, 0, 0 },   "\x17" "\x0d" "4506061610+01" },
247      { { 2045, 6, 1, 6, 15, 10, 0, 0 },   "\x17" "\x0d" "4506061610-01" },
248      { { 2045, 6, 1, 6, 17, 10, 0, 0 },   "\x17" "\x0f" "4506061610+0100" },
249      { { 2045, 6, 1, 6, 15, 10, 0, 0 },   "\x17" "\x0f" "4506061610-0100" },
250     };
251     /* An oddball case that succeeds in Windows, but doesn't seem correct
252      { { 2145, 6, 1, 2, 11, 31, 0, 0 },   "\x18" "\x13" "21450606161000-9999" },
253      */
254     static const char *bogusTimes[] = {
255      /* oddly, this succeeds on Windows, with year 2765
256      "\x18" "\x0f" "21r50606161000Z",
257       */
258      "\x17" "\x08" "45060616",
259      "\x18" "\x0f" "aaaaaaaaaaaaaaZ",
260      "\x18" "\x04" "2145",
261      "\x18" "\x08" "21450606",
262     };
263     DWORD i, size;
264     FILETIME ft1 = { 0 }, ft2 = { 0 };
265     BOOL ret;
266
267     /* Check bogus length with non-NULL buffer */
268     ret = SystemTimeToFileTime(&times[0].sysTime, &ft1);
269     ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
270     size = 1;
271     ret = CryptDecodeObjectEx(dwEncoding, X509_CHOICE_OF_TIME,
272      times[0].encodedTime, times[0].encodedTime[1] + 2, 0, NULL, &ft2, &size);
273     ok(!ret && GetLastError() == ERROR_MORE_DATA,
274      "Expected ERROR_MORE_DATA, got %ld\n", GetLastError());
275     /* Normal tests */
276     for (i = 0; i < sizeof(times) / sizeof(times[0]); i++)
277     {
278         testTimeDecoding(dwEncoding, X509_CHOICE_OF_TIME, &times[i]);
279         testTimeDecoding(dwEncoding, PKCS_UTC_TIME, &times[i]);
280         testTimeDecoding(dwEncoding, szOID_RSA_signingTime, &times[i]);
281     }
282     for (i = 0; i < sizeof(otherTimes) / sizeof(otherTimes[0]); i++)
283     {
284         testTimeDecoding(dwEncoding, X509_CHOICE_OF_TIME, &otherTimes[i]);
285         testTimeDecoding(dwEncoding, PKCS_UTC_TIME, &otherTimes[i]);
286         testTimeDecoding(dwEncoding, szOID_RSA_signingTime, &otherTimes[i]);
287     }
288     for (i = 0; i < sizeof(bogusTimes) / sizeof(bogusTimes[0]); i++)
289     {
290         size = sizeof(ft1);
291         ret = CryptDecodeObjectEx(dwEncoding, X509_CHOICE_OF_TIME,
292          bogusTimes[i], bogusTimes[i][1] + 2, 0, NULL, &ft1, &size);
293         ok(!ret && GetLastError() == CRYPT_E_ASN1_CORRUPT,
294          "Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
295     }
296 }
297
298 struct EncodedName
299 {
300     CERT_RDN_ATTR attr;
301     BYTE *encoded;
302 };
303
304 static const char commonName[] = "Juan Lang";
305 static const char surName[] = "Lang";
306 static const char bogusIA5[] = "\x80";
307 static const char bogusPrintable[] = "~";
308 static const char bogusNumeric[] = "A";
309 static const struct EncodedName names[] = {
310  { { szOID_COMMON_NAME, CERT_RDN_PRINTABLE_STRING,
311    { sizeof(commonName), (BYTE *)commonName } },
312  "\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x13\x0aJuan Lang" },
313  { { szOID_COMMON_NAME, CERT_RDN_IA5_STRING,
314    { sizeof(commonName), (BYTE *)commonName } },
315  "\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x16\x0aJuan Lang" },
316  { { szOID_SUR_NAME, CERT_RDN_IA5_STRING,
317    { sizeof(surName), (BYTE *)surName } },
318  "\x30\x10\x31\x0e\x30\x0c\x06\x03\x55\x04\x04\x16\x05Lang" },
319  { { NULL, CERT_RDN_PRINTABLE_STRING,
320    { sizeof(commonName), (BYTE *)commonName } },
321  "\x30\x12\x31\x10\x30\x0e\x06\x00\x13\x0aJuan Lang" },
322 /* The following test isn't a very good one, because it doesn't encode any
323  * Japanese characters.  I'm leaving it out for now.
324  { { szOID_COMMON_NAME, CERT_RDN_T61_STRING,
325    { sizeof(commonName), (BYTE *)commonName } },
326  "\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x14\x0aJuan Lang" },
327  */
328  /* The following tests succeed under Windows, but really should fail,
329   * they contain characters that are illegal for the encoding.  I'm
330   * including them to justify my lazy encoding.
331   */
332  { { szOID_COMMON_NAME, CERT_RDN_IA5_STRING,
333    { sizeof(bogusIA5), (BYTE *)bogusIA5 } },
334  "\x30\x0d\x31\x0b\x30\x09\x06\x03\x55\x04\x03\x16\x02\x80" },
335  { { szOID_COMMON_NAME, CERT_RDN_PRINTABLE_STRING,
336    { sizeof(bogusPrintable), (BYTE *)bogusPrintable } },
337  "\x30\x0d\x31\x0b\x30\x09\x06\x03\x55\x04\x03\x13\x02\x7e" },
338  { { szOID_COMMON_NAME, CERT_RDN_NUMERIC_STRING,
339    { sizeof(bogusNumeric), (BYTE *)bogusNumeric } },
340  "\x30\x0d\x31\x0b\x30\x09\x06\x03\x55\x04\x03\x12\x02\x41" },
341 };
342
343 static const BYTE emptyName[] = { 0x30, 0 };
344 static const BYTE emptyRDNs[] = { 0x30, 0x02, 0x31, 0 };
345 static const BYTE twoRDNs[] = "\x30\x23\x31\x21\x30\x0c\x06\x03\x55\x04\x04"
346  "\x13\x05\x4c\x61\x6e\x67\x00\x30\x11\x06\x03\x55\x04\x03"
347  "\x13\x0a\x4a\x75\x61\x6e\x20\x4c\x61\x6e\x67";
348
349 static void test_encodeName(DWORD dwEncoding)
350 {
351     CERT_RDN_ATTR attrs[2];
352     CERT_RDN rdn;
353     CERT_NAME_INFO info;
354     BYTE *buf = NULL;
355     DWORD size = 0, i;
356     BOOL ret;
357
358     /* Test with NULL pvStructInfo */
359     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, NULL,
360      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
361     ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
362      "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
363     /* Test with empty CERT_NAME_INFO */
364     info.cRDN = 0;
365     info.rgRDN = NULL;
366     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
367      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
368     ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
369     if (buf)
370     {
371         ok(!memcmp(buf, emptyName, sizeof(emptyName)),
372          "Got unexpected encoding for empty name\n");
373         LocalFree(buf);
374     }
375     /* Test with bogus CERT_RDN */
376     info.cRDN = 1;
377     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
378      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
379     ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
380      "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
381     /* Test with empty CERT_RDN */
382     rdn.cRDNAttr = 0;
383     rdn.rgRDNAttr = NULL;
384     info.cRDN = 1;
385     info.rgRDN = &rdn;
386     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
387      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
388     ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
389     if (buf)
390     {
391         ok(!memcmp(buf, emptyRDNs, sizeof(emptyRDNs)),
392          "Got unexpected encoding for empty RDN array\n");
393         LocalFree(buf);
394     }
395     /* Test with bogus attr array */
396     rdn.cRDNAttr = 1;
397     rdn.rgRDNAttr = NULL;
398     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
399      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
400     ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
401      "Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
402     /* oddly, a bogus OID is accepted by Windows XP; not testing.
403     attrs[0].pszObjId = "bogus";
404     attrs[0].dwValueType = CERT_RDN_PRINTABLE_STRING;
405     attrs[0].Value.cbData = sizeof(commonName);
406     attrs[0].Value.pbData = (BYTE *)commonName;
407     rdn.cRDNAttr = 1;
408     rdn.rgRDNAttr = attrs;
409     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
410      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
411     ok(!ret, "Expected failure, got success\n");
412      */
413     /* Check with two CERT_RDN_ATTRs.  Note DER encoding forces the order of
414      * the encoded attributes to be swapped.
415      */
416     attrs[0].pszObjId = szOID_COMMON_NAME;
417     attrs[0].dwValueType = CERT_RDN_PRINTABLE_STRING;
418     attrs[0].Value.cbData = sizeof(commonName);
419     attrs[0].Value.pbData = (BYTE *)commonName;
420     attrs[1].pszObjId = szOID_SUR_NAME;
421     attrs[1].dwValueType = CERT_RDN_PRINTABLE_STRING;
422     attrs[1].Value.cbData = sizeof(surName);
423     attrs[1].Value.pbData = (BYTE *)surName;
424     rdn.cRDNAttr = 2;
425     rdn.rgRDNAttr = attrs;
426     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
427      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
428     ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
429     if (buf)
430     {
431         ok(!memcmp(buf, twoRDNs, sizeof(twoRDNs)),
432          "Got unexpected encoding for two RDN array\n");
433         LocalFree(buf);
434     }
435     /* CERT_RDN_ANY_TYPE is too vague for X509_NAMEs, check the return */
436     rdn.cRDNAttr = 1;
437     attrs[0].dwValueType = CERT_RDN_ANY_TYPE;
438     ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
439      CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
440     ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
441      "Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError());
442     for (i = 0; i < sizeof(names) / sizeof(names[0]); i++)
443     {
444         rdn.cRDNAttr = 1;
445         rdn.rgRDNAttr = (CERT_RDN_ATTR *)&names[i].attr;
446         ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
447          CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
448         ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
449         if (buf)
450         {
451             ok(size == names[i].encoded[1] + 2, "Expected size %d, got %ld\n",
452              names[i].encoded[1] + 2, size);
453             ok(!memcmp(buf, names[i].encoded, names[i].encoded[1] + 2),
454              "Got unexpected encoding\n");
455             LocalFree(buf);
456         }
457     }
458 }
459
460 static void compareNames(const CERT_NAME_INFO *expected,
461  const CERT_NAME_INFO *got)
462 {
463     ok(got->cRDN == expected->cRDN, "Expected %ld RDNs, got %ld\n",
464      expected->cRDN, got->cRDN);
465     if (expected->cRDN)
466     {
467         ok(got->rgRDN[0].cRDNAttr == expected->rgRDN[0].cRDNAttr,
468          "Expected %ld RDN attrs, got %ld\n", expected->rgRDN[0].cRDNAttr,
469          got->rgRDN[0].cRDNAttr);
470         if (expected->rgRDN[0].cRDNAttr)
471         {
472             if (expected->rgRDN[0].rgRDNAttr[0].pszObjId &&
473              strlen(expected->rgRDN[0].rgRDNAttr[0].pszObjId))
474             {
475                 ok(got->rgRDN[0].rgRDNAttr[0].pszObjId != NULL,
476                  "Expected OID %s, got NULL\n",
477                  expected->rgRDN[0].rgRDNAttr[0].pszObjId);
478                 if (got->rgRDN[0].rgRDNAttr[0].pszObjId)
479                     ok(!strcmp(got->rgRDN[0].rgRDNAttr[0].pszObjId,
480                      expected->rgRDN[0].rgRDNAttr[0].pszObjId),
481                      "Got unexpected OID %s, expected %s\n",
482                      got->rgRDN[0].rgRDNAttr[0].pszObjId,
483                      expected->rgRDN[0].rgRDNAttr[0].pszObjId);
484             }
485             ok(got->rgRDN[0].rgRDNAttr[0].Value.cbData ==
486              expected->rgRDN[0].rgRDNAttr[0].Value.cbData,
487              "Unexpected data size, got %ld, expected %ld\n",
488              got->rgRDN[0].rgRDNAttr[0].Value.cbData,
489              expected->rgRDN[0].rgRDNAttr[0].Value.cbData);
490             if (expected->rgRDN[0].rgRDNAttr[0].Value.pbData)
491                 ok(!memcmp(got->rgRDN[0].rgRDNAttr[0].Value.pbData,
492                  expected->rgRDN[0].rgRDNAttr[0].Value.pbData,
493                  expected->rgRDN[0].rgRDNAttr[0].Value.cbData),
494                  "Unexpected value\n");
495         }
496     }
497 }
498
499 static void test_decodeName(DWORD dwEncoding)
500 {
501     int i;
502     BYTE *buf = NULL;
503     DWORD bufSize = 0;
504     BOOL ret;
505     CERT_RDN rdn;
506     CERT_NAME_INFO info = { 1, &rdn };
507
508     for (i = 0; i < sizeof(names) / sizeof(names[0]); i++)
509     {
510         /* When the output buffer is NULL, this always succeeds */
511         SetLastError(0xdeadbeef);
512         ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, names[i].encoded,
513          names[i].encoded[1] + 2, 0, NULL, NULL, &bufSize);
514         ok(ret && GetLastError() == NOERROR,
515          "Expected success and NOERROR, got %08lx\n", GetLastError());
516         ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, names[i].encoded,
517          names[i].encoded[1] + 2,
518          CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
519          (BYTE *)&buf, &bufSize);
520         ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
521         rdn.cRDNAttr = 1;
522         rdn.rgRDNAttr = (CERT_RDN_ATTR *)&names[i].attr;
523         if (buf)
524         {
525             compareNames((CERT_NAME_INFO *)buf, &info);
526             LocalFree(buf);
527         }
528     }
529     /* test empty name */
530     bufSize = 0;
531     ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, emptyName,
532      emptyName[1] + 2,
533      CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
534      (BYTE *)&buf, &bufSize);
535     ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
536     /* Interestingly, in Windows, if cRDN is 0, rgRGN may not be NULL.  My
537      * decoder works the same way, so only test the count.
538      */
539     if (buf)
540     {
541         ok(bufSize == sizeof(CERT_NAME_INFO),
542          "Expected bufSize %d, got %ld\n", sizeof(CERT_NAME_INFO), bufSize);
543         ok(((CERT_NAME_INFO *)buf)->cRDN == 0,
544          "Expected 0 RDNs in empty info, got %ld\n",
545          ((CERT_NAME_INFO *)buf)->cRDN);
546         LocalFree(buf);
547     }
548     /* test empty RDN */
549     bufSize = 0;
550     ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, emptyRDNs,
551      emptyRDNs[1] + 2,
552      CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
553      (BYTE *)&buf, &bufSize);
554     ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
555     if (buf)
556     {
557         CERT_NAME_INFO *info = (CERT_NAME_INFO *)buf;
558
559         ok(bufSize == sizeof(CERT_NAME_INFO) + sizeof(CERT_RDN) &&
560          info->cRDN == 1 && info->rgRDN && info->rgRDN[0].cRDNAttr == 0,
561          "Got unexpected value for empty RDN\n");
562         LocalFree(buf);
563     }
564     /* test two RDN attrs */
565     bufSize = 0;
566     ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, twoRDNs,
567      twoRDNs[1] + 2,
568      CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
569      (BYTE *)&buf, &bufSize);
570     ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
571     if (buf)
572     {
573         CERT_RDN_ATTR attrs[] = {
574          { szOID_SUR_NAME, CERT_RDN_PRINTABLE_STRING, { sizeof(surName),
575           (BYTE *)surName } },
576          { szOID_COMMON_NAME, CERT_RDN_PRINTABLE_STRING, { sizeof(commonName),
577           (BYTE *)commonName } },
578         };
579
580         rdn.cRDNAttr = sizeof(attrs) / sizeof(attrs[0]);
581         rdn.rgRDNAttr = attrs;
582         compareNames((CERT_NAME_INFO *)buf, &info);
583         LocalFree(buf);
584     }
585 }
586
587 static void test_registerOIDFunction(void)
588 {
589     static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
590     BOOL ret;
591
592     /* oddly, this succeeds under WinXP; the function name key is merely
593      * omitted.  This may be a side effect of the registry code, I don't know.
594      * I don't check it because I doubt anyone would depend on it.
595     ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, NULL,
596      "1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
597      */
598     /* On windows XP, GetLastError is incorrectly being set with an HRESULT,
599      * HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)
600      */
601     ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "foo", NULL, bogusDll,
602      NULL);
603     ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() ==
604      HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)),
605      "Expected ERROR_INVALID_PARAMETER: %ld\n", GetLastError());
606     /* This has no effect, but "succeeds" on XP */
607     ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "foo",
608      "1.2.3.4.5.6.7.8.9.10", NULL, NULL);
609     ok(ret, "Expected pseudo-success, got %ld\n", GetLastError());
610     ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "CryptDllEncodeObject",
611      "1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
612     ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
613     ret = CryptUnregisterOIDFunction(X509_ASN_ENCODING, "CryptDllEncodeObject",
614      "1.2.3.4.5.6.7.8.9.10");
615     ok(ret, "CryptUnregisterOIDFunction failed: %ld\n", GetLastError());
616     ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "bogus",
617      "1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
618     ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
619     ret = CryptUnregisterOIDFunction(X509_ASN_ENCODING, "bogus",
620      "1.2.3.4.5.6.7.8.9.10");
621     ok(ret, "CryptUnregisterOIDFunction failed: %ld\n", GetLastError());
622     /* This has no effect */
623     ret = CryptRegisterOIDFunction(PKCS_7_ASN_ENCODING, "CryptDllEncodeObject",
624      "1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
625     ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
626     /* Check with bogus encoding type: */
627     ret = CryptRegisterOIDFunction(0, "CryptDllEncodeObject",
628      "1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
629     ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
630     /* This is written with value 3 verbatim.  Thus, the encoding type isn't
631      * (for now) treated as a mask.
632      */
633     ret = CryptRegisterOIDFunction(3, "CryptDllEncodeObject",
634      "1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
635     ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
636     ret = CryptUnregisterOIDFunction(3, "CryptDllEncodeObject",
637      "1.2.3.4.5.6.7.8.9.10");
638     ok(ret, "CryptUnregisterOIDFunction failed: %ld\n", GetLastError());
639 }
640
641 START_TEST(encode)
642 {
643     static const DWORD encodings[] = { X509_ASN_ENCODING, PKCS_7_ASN_ENCODING,
644      X509_ASN_ENCODING | PKCS_7_ASN_ENCODING };
645     DWORD i;
646
647     for (i = 0; i < sizeof(encodings) / sizeof(encodings[0]); i++)
648     {
649         test_encodeint(encodings[i]);
650         test_decodeint(encodings[i]);
651         test_encodeFiletime(encodings[i]);
652         test_decodeFiletime(encodings[i]);
653         test_encodeName(encodings[i]);
654         test_decodeName(encodings[i]);
655     }
656     test_registerOIDFunction();
657 }