Use shell icon cache instead of an own IExtractIcon implementation.
[wine] / dlls / crypt32 / encode.c
1 /*
2  * Copyright 2002 Mike McCormack for CodeWeavers
3  * Copyright 2005 Juan Lang
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 #include <stdarg.h>
20 #include <stdio.h>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "wincrypt.h"
24 #include "winreg.h"
25 #include "snmp.h"
26 #include "wine/debug.h"
27
28 /* a few asn.1 tags we need */
29 #define ASN_UTCTIME     (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17)
30 #define ASN_GENERALTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18)
31
32 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
33
34 static const WCHAR szDllName[] = { 'D','l','l',0 };
35
36 static char *CRYPT_GetKeyName(DWORD dwEncodingType, LPCSTR pszFuncName,
37  LPCSTR pszOID)
38 {
39     static const char szEncodingTypeFmt[] =
40      "Software\\Microsoft\\Cryptography\\OID\\EncodingType %ld\\%s\\%s";
41     UINT len;
42     char numericOID[7]; /* enough for "#65535" */
43     const char *oid;
44     LPSTR szKey;
45
46     /* MSDN says the encoding type is a mask, but it isn't treated that way.
47      * (E.g., if dwEncodingType were 3, the key names "EncodingType 1" and
48      * "EncodingType 2" would be expected if it were a mask.  Instead native
49      * stores values in "EncodingType 3".
50      */
51     if (!HIWORD(pszOID))
52     {
53         snprintf(numericOID, sizeof(numericOID), "#%d", (int)pszOID);
54         oid = numericOID;
55     }
56     else
57         oid = pszOID;
58
59     /* This is enough: the lengths of the two string parameters are explicitly
60      * counted, and we need up to five additional characters for the encoding
61      * type.  These are covered by the "%d", "%s", and "%s" characters in the
62      * format specifier that are removed by sprintf.
63      */
64     len = sizeof(szEncodingTypeFmt) + lstrlenA(pszFuncName) + lstrlenA(oid);
65     szKey = HeapAlloc(GetProcessHeap(), 0, len);
66     if (szKey)
67         sprintf(szKey, szEncodingTypeFmt, dwEncodingType, pszFuncName, oid);
68     return szKey;
69 }
70
71 BOOL WINAPI CryptRegisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName,
72                   LPCSTR pszOID, LPCWSTR pwszDll, LPCSTR pszOverrideFuncName)
73 {
74     LONG r;
75     HKEY hKey;
76     LPSTR szKey;
77
78     TRACE("%lx %s %s %s %s\n", dwEncodingType, pszFuncName, pszOID,
79           debugstr_w(pwszDll), pszOverrideFuncName);
80
81     /* This only registers functions for encoding certs, not messages */
82     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
83         return TRUE;
84
85     /* Native does nothing pwszDll is NULL */
86     if (!pwszDll)
87         return TRUE;
88
89     /* I'm not matching MS bug for bug here, because I doubt any app depends on
90      * it:
91      * - native "succeeds" if pszFuncName is NULL, but the nonsensical entry
92      *   it creates would never be used
93      * - native returns an HRESULT rather than a Win32 error if pszOID is NULL.
94      * Instead I disallow both of these with ERROR_INVALID_PARAMETER.
95      */
96     if (!pszFuncName || !pszOID)
97     {
98         SetLastError(ERROR_INVALID_PARAMETER);
99         return FALSE;
100     }
101
102     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
103     TRACE("Key name is %s\n", debugstr_a(szKey));
104
105     if (!szKey)
106         return FALSE;
107
108     r = RegCreateKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
109     HeapFree(GetProcessHeap(), 0, szKey);
110     if(r != ERROR_SUCCESS)
111         return FALSE;
112
113     /* write the values */
114     if (pszOverrideFuncName)
115         RegSetValueExA(hKey, "FuncName", 0, REG_SZ, pszOverrideFuncName,
116          lstrlenA(pszOverrideFuncName) + 1);
117     RegSetValueExW(hKey, szDllName, 0, REG_SZ, (const BYTE*) pwszDll,
118                     (lstrlenW(pwszDll) + 1) * sizeof (WCHAR));
119
120     RegCloseKey(hKey);
121     return TRUE;
122 }
123
124 BOOL WINAPI CryptUnregisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName,
125  LPCSTR pszOID)
126 {
127     LPSTR szKey;
128     LONG rc;
129
130     TRACE("%lx %s %s\n", dwEncodingType, pszFuncName, pszOID);
131
132     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
133         return TRUE;
134
135     if (!pszFuncName || !pszOID)
136     {
137         SetLastError(ERROR_INVALID_PARAMETER);
138         return FALSE;
139     }
140
141     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
142     rc = RegDeleteKeyA(HKEY_LOCAL_MACHINE, szKey);
143     HeapFree(GetProcessHeap(), 0, szKey);
144     if (rc)
145         SetLastError(rc);
146     return rc ? FALSE : TRUE;
147 }
148
149 BOOL WINAPI CryptGetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName,
150  LPCSTR pszOID, LPCWSTR pwszValueName, DWORD *pdwValueType, BYTE *pbValueData,
151  DWORD *pcbValueData)
152 {
153     LPSTR szKey;
154     LONG rc;
155     HKEY hKey;
156
157     TRACE("%lx %s %s %s %p %p %p\n", dwEncodingType, debugstr_a(pszFuncName),
158      debugstr_a(pszOID), debugstr_w(pwszValueName), pdwValueType, pbValueData,
159      pcbValueData);
160
161     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
162         return TRUE;
163
164     if (!pszFuncName || !pszOID || !pwszValueName)
165     {
166         SetLastError(ERROR_INVALID_PARAMETER);
167         return FALSE;
168     }
169
170     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
171     rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
172     HeapFree(GetProcessHeap(), 0, szKey);
173     if (rc)
174         SetLastError(rc);
175     else
176     {
177         rc = RegQueryValueExW(hKey, pwszValueName, NULL, pdwValueType,
178          pbValueData, pcbValueData);
179         if (rc)
180             SetLastError(rc);
181         RegCloseKey(hKey);
182     }
183     return rc ? FALSE : TRUE;
184 }
185
186 BOOL WINAPI CryptSetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName,
187  LPCSTR pszOID, LPCWSTR pwszValueName, DWORD dwValueType,
188  const BYTE *pbValueData, DWORD cbValueData)
189 {
190     LPSTR szKey;
191     LONG rc;
192     HKEY hKey;
193
194     TRACE("%lx %s %s %s %ld %p %ld\n", dwEncodingType, debugstr_a(pszFuncName),
195      debugstr_a(pszOID), debugstr_w(pwszValueName), dwValueType, pbValueData,
196      cbValueData);
197
198     if (!GET_CERT_ENCODING_TYPE(dwEncodingType))
199         return TRUE;
200
201     if (!pszFuncName || !pszOID || !pwszValueName)
202     {
203         SetLastError(ERROR_INVALID_PARAMETER);
204         return FALSE;
205     }
206
207     szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID);
208     rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
209     HeapFree(GetProcessHeap(), 0, szKey);
210     if (rc)
211         SetLastError(rc);
212     else
213     {
214         rc = RegSetValueExW(hKey, pwszValueName, 0, dwValueType, pbValueData,
215          cbValueData);
216         if (rc)
217             SetLastError(rc);
218         RegCloseKey(hKey);
219     }
220     return rc ? FALSE : TRUE;
221 }
222
223 /* Gets the registered function named szFuncName for dwCertEncodingType and
224  * lpszStructType, or NULL if one could not be found.  *lib will be set to the
225  * handle of the module it's in, or NULL if no module was loaded.  If the
226  * return value is NULL, *lib will also be NULL, to simplify error handling.
227  */
228 static void *CRYPT_GetFunc(DWORD dwCertEncodingType, LPCSTR lpszStructType,
229  LPCSTR szFuncName, HMODULE *lib)
230 {
231     void *ret = NULL;
232     char *szKey = CRYPT_GetKeyName(dwCertEncodingType, szFuncName,
233      lpszStructType);
234     const char *funcName;
235     long r;
236     HKEY hKey;
237     DWORD type, size = 0;
238
239     *lib = NULL;
240     r = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey);
241     HeapFree(GetProcessHeap(), 0, szKey);
242     if(r != ERROR_SUCCESS)
243         return NULL;
244
245     RegQueryValueExA(hKey, "FuncName", NULL, &type, NULL, &size);
246     if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ)
247     {
248         funcName = HeapAlloc(GetProcessHeap(), 0, size);
249         RegQueryValueExA(hKey, "FuncName", NULL, &type, (LPBYTE)funcName,
250          &size);
251     }
252     else
253         funcName = szFuncName;
254     RegQueryValueExW(hKey, szDllName, NULL, &type, NULL, &size);
255     if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ)
256     {
257         LPWSTR dllName = HeapAlloc(GetProcessHeap(), 0, size);
258
259         RegQueryValueExW(hKey, szDllName, NULL, &type, (LPBYTE)dllName,
260          &size);
261         *lib = LoadLibraryW(dllName);
262         if (*lib)
263         {
264              ret = GetProcAddress(*lib, funcName);
265              if (!ret)
266              {
267                  /* Unload the library, the caller doesn't want to unload it
268                   * when the return value is NULL.
269                   */
270                  FreeLibrary(*lib);
271                  *lib = NULL;
272              }
273         }
274         HeapFree(GetProcessHeap(), 0, dllName);
275     }
276     if (funcName != szFuncName)
277         HeapFree(GetProcessHeap(), 0, (char *)funcName);
278     return ret;
279 }
280
281 typedef BOOL (WINAPI *CryptEncodeObjectFunc)(DWORD, LPCSTR, const void *,
282  BYTE *, DWORD *);
283
284 BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
285  const void *pvStructInfo, BYTE *pbEncoded, DWORD *pcbEncoded)
286 {
287     BOOL ret = FALSE;
288     HMODULE lib;
289     CryptEncodeObjectFunc pCryptEncodeObject;
290
291     TRACE("(0x%08lx, %s, %p, %p, %p)\n",
292      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
293      "(integer value)", pvStructInfo, pbEncoded, pcbEncoded);
294
295     if (!pbEncoded && !pcbEncoded)
296     {
297         SetLastError(ERROR_INVALID_PARAMETER);
298         return FALSE;
299     }
300
301     /* Try registered DLL first.. */
302     pCryptEncodeObject =
303      (CryptEncodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType,
304      lpszStructType, "CryptEncodeObject", &lib);
305     if (pCryptEncodeObject)
306     {
307         ret = pCryptEncodeObject(dwCertEncodingType, lpszStructType,
308          pvStructInfo, pbEncoded, pcbEncoded);
309         FreeLibrary(lib);
310     }
311     else
312     {
313         /* If not, use CryptEncodeObjectEx */
314         ret = CryptEncodeObjectEx(dwCertEncodingType, lpszStructType,
315          pvStructInfo, 0, NULL, pbEncoded, pcbEncoded);
316     }
317     return ret;
318 }
319
320 static BOOL CRYPT_EncodeEnsureSpace(DWORD dwFlags,
321  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded,
322  DWORD bytesNeeded)
323 {
324     if (!pbEncoded || (!(dwFlags & CRYPT_ENCODE_ALLOC_FLAG) &&
325      bytesNeeded > *pcbEncoded))
326     {
327         *pcbEncoded = bytesNeeded;
328         SetLastError(ERROR_MORE_DATA);
329         return FALSE;
330     }
331     if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
332     {
333         if (pEncodePara && pEncodePara->pfnAlloc)
334             *(BYTE **)pbEncoded = pEncodePara->pfnAlloc(bytesNeeded);
335         else
336             *(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded);
337         if (!*(BYTE **)pbEncoded)
338             return FALSE;
339     }
340     return TRUE;
341 }
342
343 static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
344  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
345  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
346 {
347     INT val, i;
348     BYTE significantBytes, padByte = 0, bytesNeeded;
349     BOOL neg = FALSE, pad = FALSE;
350
351     if (!pvStructInfo)
352     {
353         SetLastError(ERROR_INVALID_PARAMETER);
354         return FALSE;
355     }
356
357     memcpy(&val, pvStructInfo, sizeof(val));
358     /* Count the number of significant bytes.  Temporarily swap sign for
359      * negatives so I count the minimum number of bytes.
360      */
361     if (val < 0)
362     {
363         neg = TRUE;
364         val = -val;
365     }
366     for (significantBytes = sizeof(val); !(val & 0xff000000);
367      val <<= 8, significantBytes--)
368         ;
369     if (neg)
370     {
371         val = -val;
372         if ((val & 0xff000000) < 0x80000000)
373         {
374             padByte = 0xff;
375             pad = TRUE;
376         }
377     }
378     else if ((val & 0xff000000) > 0x7f000000)
379     {
380         padByte = 0;
381         pad = TRUE;
382     }
383     bytesNeeded = 2 + significantBytes;
384     if (pad)
385         bytesNeeded++;
386     if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
387      bytesNeeded))
388         return FALSE;
389     if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
390         pbEncoded = *(BYTE **)pbEncoded;
391     *pbEncoded++ = ASN_INTEGER;
392     if (pad)
393     {
394         *pbEncoded++ = significantBytes + 1;
395         *pbEncoded++ = padByte;
396     }
397     else
398         *pbEncoded++ = significantBytes;
399     for (i = 0; i < significantBytes; i++, val <<= 8)
400         *(pbEncoded + i) = (BYTE)((val & 0xff000000) >> 24);
401     return TRUE;
402 }
403
404 static BOOL WINAPI CRYPT_AsnEncodeUtcTime(DWORD dwCertEncodingType,
405  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
406  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
407 {
408     SYSTEMTIME sysTime;
409     /* sorry, magic number: enough for tag, len, YYMMDDHHMMSSZ\0.  I use a
410      * temporary buffer because the output buffer is not NULL-terminated.
411      */
412     char buf[16];
413     static const DWORD bytesNeeded = sizeof(buf) - 1;
414
415     if (!pvStructInfo)
416     {
417         SetLastError(ERROR_INVALID_PARAMETER);
418         return FALSE;
419     }
420     /* Sanity check the year, this is a two-digit year format */
421     if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime))
422         return FALSE;
423     if (sysTime.wYear < 1950 || sysTime.wYear > 2050)
424     {
425         SetLastError(CRYPT_E_BAD_ENCODE);
426         return FALSE;
427     }
428     if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
429      bytesNeeded))
430         return FALSE;
431     if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
432         pbEncoded = *(BYTE **)pbEncoded;
433     buf[0] = ASN_UTCTIME;
434     buf[1] = bytesNeeded - 2;
435     snprintf(buf + 2, sizeof(buf) - 2, "%02d%02d%02d%02d%02d%02dZ",
436      sysTime.wYear >= 2000 ? sysTime.wYear - 2000 : sysTime.wYear - 1900,
437      sysTime.wDay, sysTime.wMonth, sysTime.wHour, sysTime.wMinute,
438      sysTime.wSecond);
439     memcpy(pbEncoded, buf, bytesNeeded);
440     return TRUE;
441 }
442
443 static BOOL WINAPI CRYPT_AsnEncodeChoiceOfTime(DWORD dwCertEncodingType,
444  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
445  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
446 {
447     SYSTEMTIME sysTime;
448     /* sorry, magic number: enough for tag, len, YYYYMMDDHHMMSSZ\0.  I use a
449      * temporary buffer because the output buffer is not NULL-terminated.
450      */
451     char buf[18];
452     static const DWORD bytesNeeded = sizeof(buf) - 1;
453
454     if (!pvStructInfo)
455     {
456         SetLastError(ERROR_INVALID_PARAMETER);
457         return FALSE;
458     }
459     /* Check the year, if it's in the UTCTime range call that encode func */
460     if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime))
461         return FALSE;
462     if (sysTime.wYear >= 1950 && sysTime.wYear <= 2050)
463         return CRYPT_AsnEncodeUtcTime(dwCertEncodingType, lpszStructType,
464          pvStructInfo, dwFlags, pEncodePara, pbEncoded, pcbEncoded);
465     if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
466      bytesNeeded))
467         return FALSE;
468     if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
469         pbEncoded = *(BYTE **)pbEncoded;
470     buf[0] = ASN_GENERALTIME;
471     buf[1] = bytesNeeded - 2;
472     snprintf(buf + 2, sizeof(buf) - 2, "%04d%02d%02d%02d%02d%02dZ",
473      sysTime.wYear, sysTime.wDay, sysTime.wMonth, sysTime.wHour,
474      sysTime.wMinute, sysTime.wSecond);
475     memcpy(pbEncoded, buf, bytesNeeded);
476     return TRUE;
477 }
478
479 typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *,
480  DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *);
481
482 BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
483  const void *pvStructInfo, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara,
484  BYTE *pbEncoded, DWORD *pcbEncoded)
485 {
486     BOOL ret = FALSE;
487     HMODULE lib = NULL;
488     CryptEncodeObjectExFunc encodeFunc = NULL;
489
490     TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p): semi-stub\n",
491      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
492      "(integer value)", pvStructInfo, dwFlags, pEncodePara, pbEncoded,
493      pcbEncoded);
494
495     if (!pbEncoded && !pcbEncoded)
496     {
497         SetLastError(ERROR_INVALID_PARAMETER);
498         return FALSE;
499     }
500     if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING
501      && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING)
502     {
503         SetLastError(ERROR_FILE_NOT_FOUND);
504         return FALSE;
505     }
506
507     if (!HIWORD(lpszStructType))
508     {
509         switch (LOWORD(lpszStructType))
510         {
511         case (WORD)X509_INTEGER:
512             encodeFunc = CRYPT_AsnEncodeInt;
513             break;
514         case (WORD)X509_CHOICE_OF_TIME:
515             encodeFunc = CRYPT_AsnEncodeChoiceOfTime;
516             break;
517         case (WORD)PKCS_UTC_TIME:
518             encodeFunc = CRYPT_AsnEncodeUtcTime;
519             break;
520         default:
521             FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
522         }
523     }
524     else if (!strcmp(lpszStructType, szOID_RSA_signingTime))
525         encodeFunc = CRYPT_AsnEncodeUtcTime;
526     if (!encodeFunc)
527         encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
528          lpszStructType, "CryptEncodeObjectEx", &lib);
529     if (encodeFunc)
530         ret = encodeFunc(dwCertEncodingType, lpszStructType, pvStructInfo,
531          dwFlags, pEncodePara, pbEncoded, pcbEncoded);
532     else
533         SetLastError(ERROR_FILE_NOT_FOUND);
534     if (lib)
535         FreeLibrary(lib);
536     return ret;
537 }
538
539 typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *,
540  DWORD, DWORD, void *, DWORD *);
541
542 BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
543  const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo,
544  DWORD *pcbStructInfo)
545 {
546     BOOL ret = FALSE;
547     HMODULE lib;
548     CryptDecodeObjectFunc pCryptDecodeObject;
549
550     TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p)\n",
551      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
552      "(integer value)", pbEncoded, cbEncoded, dwFlags, pvStructInfo,
553      pcbStructInfo);
554
555     if (!pvStructInfo && !pcbStructInfo)
556     {
557         SetLastError(ERROR_INVALID_PARAMETER);
558         return FALSE;
559     }
560
561     /* Try registered DLL first.. */
562     pCryptDecodeObject =
563      (CryptDecodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType,
564      lpszStructType, "CryptDecodeObject", &lib);
565     if (pCryptDecodeObject)
566     {
567         ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
568          pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo);
569         FreeLibrary(lib);
570     }
571     else
572     {
573         /* If not, use CryptDecodeObjectEx */
574         ret = CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, pbEncoded,
575          cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo);
576     }
577     return ret;
578 }
579
580 static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags,
581  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo,
582  DWORD bytesNeeded)
583 {
584     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
585     {
586         if (pDecodePara && pDecodePara->pfnAlloc)
587             *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(bytesNeeded);
588         else
589             *(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded);
590         if (!*(BYTE **)pvStructInfo)
591             return FALSE;
592     }
593     else if (*pcbStructInfo < bytesNeeded)
594     {
595         *pcbStructInfo = bytesNeeded;
596         SetLastError(ERROR_MORE_DATA);
597         return FALSE;
598     }
599     return TRUE;
600 }
601
602 static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
603  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
604  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
605 {
606     int val, i;
607
608     if (!pbEncoded || !cbEncoded)
609     {
610         SetLastError(ERROR_INVALID_PARAMETER);
611         return FALSE;
612     }
613     if (!pvStructInfo)
614     {
615         *pcbStructInfo = sizeof(int);
616         SetLastError(ERROR_MORE_DATA);
617         return FALSE;
618     }
619     if (pbEncoded[0] != ASN_INTEGER)
620     {
621         SetLastError(CRYPT_E_ASN1_BADTAG);
622         return FALSE;
623     }
624     if (pbEncoded[1] == 0)
625     {
626         SetLastError(CRYPT_E_ASN1_CORRUPT);
627         return FALSE;
628     }
629     if (pbEncoded[1] > sizeof(int))
630     {
631         SetLastError(CRYPT_E_ASN1_LARGE);
632         return FALSE;
633     }
634     if (pbEncoded[2] & 0x80)
635     {
636         /* initialize to a negative value to sign-extend */
637         val = -1;
638     }
639     else
640         val = 0;
641     for (i = 0; i < pbEncoded[1]; i++)
642     {
643         val <<= 8;
644         val |= pbEncoded[2 + i];
645     }
646     if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
647      pcbStructInfo, sizeof(int)))
648         return FALSE;
649     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
650         pvStructInfo = *(BYTE **)pvStructInfo;
651     *pcbStructInfo = sizeof(int);
652     memcpy(pvStructInfo, &val, sizeof(int));
653     return TRUE;
654 }
655
656 #define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \
657  do { \
658     BYTE i; \
659  \
660     (word) = 0; \
661     for (i = 0; (len) > 0 && i < (numDigits); i++, (len)--) \
662     { \
663         if (!isdigit(*(pbEncoded))) \
664         { \
665             SetLastError(CRYPT_E_ASN1_CORRUPT); \
666             return FALSE; \
667         } \
668         (word) *= 10; \
669         (word) += *(pbEncoded)++ - '0'; \
670     } \
671  } while (0)
672
673 static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType,
674  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
675  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
676 {
677     SYSTEMTIME sysTime = { 0 };
678     BYTE len;
679
680     if (!pbEncoded || !cbEncoded)
681     {
682         SetLastError(ERROR_INVALID_PARAMETER);
683         return FALSE;
684     }
685     if (!pvStructInfo)
686     {
687         *pcbStructInfo = sizeof(FILETIME);
688         SetLastError(ERROR_MORE_DATA);
689         return FALSE;
690     }
691     if (pbEncoded[0] != ASN_UTCTIME)
692     {
693         SetLastError(CRYPT_E_ASN1_BADTAG);
694         return FALSE;
695     }
696     len = pbEncoded[1];
697     /* FIXME: magic # */
698     if (len < 10)
699     {
700         SetLastError(CRYPT_E_ASN1_CORRUPT);
701         return FALSE;
702     }
703     pbEncoded += 2;
704     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wYear);
705     if (sysTime.wYear >= 50)
706         sysTime.wYear += 1900;
707     else
708         sysTime.wYear += 2000;
709     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth);
710     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay);
711     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour);
712     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute);
713     if (len > 0)
714     {
715         if (len >= 2 && isdigit(*pbEncoded) && isdigit(*(pbEncoded + 1)))
716             CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wSecond);
717         else if (isdigit(*pbEncoded))
718             CRYPT_TIME_GET_DIGITS(pbEncoded, len, 1, sysTime.wSecond);
719         if (len > 0)
720         {
721             /* FIXME: get timezone, for now assuming UTC (no adjustment) */
722         }
723     }
724
725     if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
726      pcbStructInfo, sizeof(FILETIME)))
727         return FALSE;
728     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
729         pvStructInfo = *(BYTE **)pvStructInfo;
730     *pcbStructInfo = sizeof(FILETIME);
731     return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo);
732 }
733
734 static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType,
735  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
736  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
737 {
738     SYSTEMTIME sysTime = { 0 };
739     BYTE len;
740
741     if (!pbEncoded || !cbEncoded)
742     {
743         SetLastError(ERROR_INVALID_PARAMETER);
744         return FALSE;
745     }
746     if (!pvStructInfo)
747     {
748         *pcbStructInfo = sizeof(FILETIME);
749         SetLastError(ERROR_MORE_DATA);
750         return FALSE;
751     }
752     if (pbEncoded[0] == ASN_UTCTIME)
753         return CRYPT_AsnDecodeUtcTime(dwCertEncodingType, lpszStructType,
754          pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo,
755          pcbStructInfo);
756     if (pbEncoded[0] != ASN_GENERALTIME)
757     {
758         SetLastError(CRYPT_E_ASN1_BADTAG);
759         return FALSE;
760     }
761     len = pbEncoded[1];
762     /* FIXME: magic # */
763     if (len < 10)
764     {
765         SetLastError(CRYPT_E_ASN1_CORRUPT);
766         return FALSE;
767     }
768     pbEncoded += 2;
769     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 4, sysTime.wYear);
770     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth);
771     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay);
772     CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour);
773     if (len > 0)
774     {
775         CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute);
776         if (len > 0)
777             CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wSecond);
778         if (len > 0 && (*pbEncoded == '.' || *pbEncoded == ','))
779         {
780             BYTE digits;
781
782             pbEncoded++;
783             len--;
784             digits = min(len, 3); /* workaround macro weirdness */
785             CRYPT_TIME_GET_DIGITS(pbEncoded, len, digits,
786              sysTime.wMilliseconds);
787         }
788         if (len >= 5 && (*pbEncoded == '+' || *pbEncoded == '-'))
789         {
790             WORD hours, minutes;
791             BYTE sign = *pbEncoded++;
792
793             len--;
794             CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours);
795             if (hours >= 24)
796                 return CRYPT_E_ASN1_CORRUPT;
797             CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes);
798             if (minutes >= 60)
799                 return CRYPT_E_ASN1_CORRUPT;
800             if (sign == '+')
801             {
802                 sysTime.wHour += hours;
803                 sysTime.wMinute += minutes;
804             }
805             else
806             {
807                 if (hours > sysTime.wHour)
808                 {
809                     sysTime.wDay--;
810                     sysTime.wHour = 24 - (hours - sysTime.wHour);
811                 }
812                 else
813                     sysTime.wHour -= hours;
814                 if (minutes > sysTime.wMinute)
815                 {
816                     sysTime.wHour--;
817                     sysTime.wMinute = 60 - (minutes - sysTime.wMinute);
818                 }
819                 else
820                     sysTime.wMinute -= minutes;
821             }
822         }
823     }
824
825     if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
826      pcbStructInfo, sizeof(FILETIME)))
827         return FALSE;
828     if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
829         pvStructInfo = *(BYTE **)pvStructInfo;
830     *pcbStructInfo = sizeof(FILETIME);
831     return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo);
832 }
833
834 typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *,
835  DWORD, DWORD, PCRYPT_DECODE_PARA, void *, DWORD *);
836
837 BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
838  const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
839  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
840 {
841     BOOL ret = FALSE;
842     HMODULE lib = NULL;
843     CryptDecodeObjectExFunc decodeFunc = NULL;
844
845     TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p): semi-stub\n",
846      dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) :
847      "(integer value)", pbEncoded, cbEncoded, dwFlags, pDecodePara,
848      pvStructInfo, pcbStructInfo);
849
850     if (!pvStructInfo && !pcbStructInfo)
851     {
852         SetLastError(ERROR_INVALID_PARAMETER);
853         return FALSE;
854     }
855     if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING
856      && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING)
857     {
858         SetLastError(ERROR_FILE_NOT_FOUND);
859         return FALSE;
860     }
861
862     if (!HIWORD(lpszStructType))
863     {
864         switch (LOWORD(lpszStructType))
865         {
866         case (WORD)X509_INTEGER:
867             decodeFunc = CRYPT_AsnDecodeInt;
868             break;
869         case (WORD)X509_CHOICE_OF_TIME:
870             decodeFunc = CRYPT_AsnDecodeChoiceOfTime;
871             break;
872         case (WORD)PKCS_UTC_TIME:
873             decodeFunc = CRYPT_AsnDecodeUtcTime;
874             break;
875         default:
876             FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
877         }
878     }
879     else if (!strcmp(lpszStructType, szOID_RSA_signingTime))
880         decodeFunc = CRYPT_AsnDecodeUtcTime;
881     if (!decodeFunc)
882         decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
883          lpszStructType, "CryptDecodeObjectEx", &lib);
884     if (decodeFunc)
885         ret = decodeFunc(dwCertEncodingType, lpszStructType, pbEncoded,
886          cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
887     else
888         SetLastError(ERROR_FILE_NOT_FOUND);
889     if (lib)
890         FreeLibrary(lib);
891     return ret;
892 }