xref: /reactos/dll/win32/crypt32/base64.c (revision fb5d5ecd)
1 /*
2  * base64 encoder/decoder
3  *
4  * Copyright 2005 by Kai Blin
5  * Copyright 2006 Juan Lang
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winerror.h"
26 #include "wincrypt.h"
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29 
30 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
31 
32 #define CERT_HEADER          "-----BEGIN CERTIFICATE-----"
33 #define CERT_HEADER_START    "-----BEGIN "
34 #define CERT_DELIMITER       "-----"
35 #define CERT_TRAILER         "-----END CERTIFICATE-----"
36 #define CERT_TRAILER_START   "-----END "
37 #define CERT_REQUEST_HEADER  "-----BEGIN NEW CERTIFICATE REQUEST-----"
38 #define CERT_REQUEST_TRAILER "-----END NEW CERTIFICATE REQUEST-----"
39 #define X509_HEADER          "-----BEGIN X509 CRL-----"
40 #define X509_TRAILER         "-----END X509 CRL-----"
41 
42 static const WCHAR CERT_HEADER_W[] = {
43 '-','-','-','-','-','B','E','G','I','N',' ','C','E','R','T','I','F','I','C',
44 'A','T','E','-','-','-','-','-',0 };
45 static const WCHAR CERT_HEADER_START_W[] = {
46 '-','-','-','-','-','B','E','G','I','N',' ',0 };
47 static const WCHAR CERT_DELIMITER_W[] = {
48 '-','-','-','-','-',0 };
49 static const WCHAR CERT_TRAILER_W[] = {
50 '-','-','-','-','-','E','N','D',' ','C','E','R','T','I','F','I','C','A','T',
51 'E','-','-','-','-','-',0 };
52 static const WCHAR CERT_TRAILER_START_W[] = {
53 '-','-','-','-','-','E','N','D',' ',0 };
54 static const WCHAR CERT_REQUEST_HEADER_W[] = {
55 '-','-','-','-','-','B','E','G','I','N',' ','N','E','W',' ','C','E','R','T',
56 'I','F','I','C','A','T','E','R','E','Q','U','E','S','T','-','-','-','-','-',0 };
57 static const WCHAR CERT_REQUEST_TRAILER_W[] = {
58 '-','-','-','-','-','E','N','D',' ','N','E','W',' ','C','E','R','T','I','F',
59 'I','C','A','T','E','R','E','Q','U','E','S','T','-','-','-','-','-',0 };
60 static const WCHAR X509_HEADER_W[] = {
61 '-','-','-','-','-','B','E','G','I','N',' ','X','5','0','9',' ','C','R','L',
62 '-','-','-','-','-',0 };
63 static const WCHAR X509_TRAILER_W[] = {
64 '-','-','-','-','-','E','N','D',' ','X','5','0','9',' ','C','R','L','-','-',
65 '-','-','-',0 };
66 
67 static const char b64[] =
68 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
69 
70 typedef BOOL (*BinaryToStringAFunc)(const BYTE *pbBinary,
71  DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString);
72 typedef BOOL (*BinaryToStringWFunc)(const BYTE *pbBinary,
73  DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD *pcchString);
74 
75 static BOOL EncodeBinaryToBinaryA(const BYTE *pbBinary,
76  DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString)
77 {
78     BOOL ret = TRUE;
79 
80     if (*pcchString < cbBinary)
81     {
82         if (!pszString)
83             *pcchString = cbBinary;
84         else
85         {
86             SetLastError(ERROR_INSUFFICIENT_BUFFER);
87             *pcchString = cbBinary;
88             ret = FALSE;
89         }
90     }
91     else
92     {
93         if (cbBinary)
94             memcpy(pszString, pbBinary, cbBinary);
95         *pcchString = cbBinary;
96     }
97     return ret;
98 }
99 
100 static LONG encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep,
101  char* out_buf, DWORD *out_len)
102 {
103     int div, i;
104     const BYTE *d = in_buf;
105     int bytes = (in_len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0;
106     DWORD needed;
107     LPSTR ptr;
108 
109     TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes);
110     needed = bytes + pad_bytes + 1;
111     needed += (needed / 64 + 1) * strlen(sep);
112 
113     if (needed > *out_len)
114     {
115         *out_len = needed;
116         return ERROR_INSUFFICIENT_BUFFER;
117     }
118     else
119         *out_len = needed;
120 
121     /* Three bytes of input give 4 chars of output */
122     div = in_len / 3;
123 
124     ptr = out_buf;
125     i = 0;
126     while (div > 0)
127     {
128         if (i && i % 64 == 0)
129         {
130             strcpy(ptr, sep);
131             ptr += strlen(sep);
132         }
133         /* first char is the first 6 bits of the first byte*/
134         *ptr++ = b64[ ( d[0] >> 2) & 0x3f ];
135         /* second char is the last 2 bits of the first byte and the first 4
136          * bits of the second byte */
137         *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)];
138         /* third char is the last 4 bits of the second byte and the first 2
139          * bits of the third byte */
140         *ptr++ = b64[ ((d[1] << 2) & 0x3c) | (d[2] >> 6 & 0x03)];
141         /* fourth char is the remaining 6 bits of the third byte */
142         *ptr++ = b64[   d[2]       & 0x3f];
143         i += 4;
144         d += 3;
145         div--;
146     }
147 
148     switch(pad_bytes)
149     {
150         case 1:
151             /* first char is the first 6 bits of the first byte*/
152             *ptr++ = b64[ ( d[0] >> 2) & 0x3f ];
153             /* second char is the last 2 bits of the first byte and the first 4
154              * bits of the second byte */
155             *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)];
156             /* third char is the last 4 bits of the second byte padded with
157              * two zeroes */
158             *ptr++ = b64[ ((d[1] << 2) & 0x3c) ];
159             /* fourth char is a = to indicate one byte of padding */
160             *ptr++ = '=';
161             break;
162         case 2:
163             /* first char is the first 6 bits of the first byte*/
164             *ptr++ = b64[ ( d[0] >> 2) & 0x3f ];
165             /* second char is the last 2 bits of the first byte padded with
166              * four zeroes*/
167             *ptr++ = b64[ ((d[0] << 4) & 0x30)];
168             /* third char is = to indicate padding */
169             *ptr++ = '=';
170             /* fourth char is = to indicate padding */
171             *ptr++ = '=';
172             break;
173     }
174     strcpy(ptr, sep);
175 
176     return ERROR_SUCCESS;
177 }
178 
179 static BOOL BinaryToBase64A(const BYTE *pbBinary,
180  DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString)
181 {
182     static const char crlf[] = "\r\n", lf[] = "\n";
183     BOOL ret = TRUE;
184     LPCSTR header = NULL, trailer = NULL, sep;
185     DWORD charsNeeded;
186 
187     if (dwFlags & CRYPT_STRING_NOCR)
188         sep = lf;
189     else if (dwFlags & CRYPT_STRING_NOCRLF)
190         sep = "";
191     else
192         sep = crlf;
193     switch (dwFlags & 0x0fffffff)
194     {
195     case CRYPT_STRING_BASE64:
196         /* no header or footer */
197         break;
198     case CRYPT_STRING_BASE64HEADER:
199         header = CERT_HEADER;
200         trailer = CERT_TRAILER;
201         break;
202     case CRYPT_STRING_BASE64REQUESTHEADER:
203         header = CERT_REQUEST_HEADER;
204         trailer = CERT_REQUEST_TRAILER;
205         break;
206     case CRYPT_STRING_BASE64X509CRLHEADER:
207         header = X509_HEADER;
208         trailer = X509_TRAILER;
209         break;
210     }
211 
212     charsNeeded = 0;
213     encodeBase64A(pbBinary, cbBinary, sep, NULL, &charsNeeded);
214     if (header)
215         charsNeeded += strlen(header) + strlen(sep);
216     if (trailer)
217         charsNeeded += strlen(trailer) + strlen(sep);
218     if (charsNeeded <= *pcchString)
219     {
220         LPSTR ptr = pszString;
221         DWORD size = charsNeeded;
222 
223         if (header)
224         {
225             strcpy(ptr, header);
226             ptr += strlen(ptr);
227             strcpy(ptr, sep);
228             ptr += strlen(sep);
229         }
230         encodeBase64A(pbBinary, cbBinary, sep, ptr, &size);
231         ptr += size - 1;
232         if (trailer)
233         {
234             strcpy(ptr, trailer);
235             ptr += strlen(ptr);
236             strcpy(ptr, sep);
237         }
238         *pcchString = charsNeeded - 1;
239     }
240     else if (pszString)
241     {
242         *pcchString = charsNeeded;
243         SetLastError(ERROR_INSUFFICIENT_BUFFER);
244         ret = FALSE;
245     }
246     else
247         *pcchString = charsNeeded;
248     return ret;
249 }
250 
251 BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary,
252  DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString)
253 {
254     BinaryToStringAFunc encoder = NULL;
255 
256     TRACE("(%p, %d, %08x, %p, %p)\n", pbBinary, cbBinary, dwFlags, pszString,
257      pcchString);
258 
259     if (!pbBinary)
260     {
261         SetLastError(ERROR_INVALID_PARAMETER);
262         return FALSE;
263     }
264     if (!pcchString)
265     {
266         SetLastError(ERROR_INVALID_PARAMETER);
267         return FALSE;
268     }
269 
270     switch (dwFlags & 0x0fffffff)
271     {
272     case CRYPT_STRING_BINARY:
273         encoder = EncodeBinaryToBinaryA;
274         break;
275     case CRYPT_STRING_BASE64:
276     case CRYPT_STRING_BASE64HEADER:
277     case CRYPT_STRING_BASE64REQUESTHEADER:
278     case CRYPT_STRING_BASE64X509CRLHEADER:
279         encoder = BinaryToBase64A;
280         break;
281     case CRYPT_STRING_HEX:
282     case CRYPT_STRING_HEXASCII:
283     case CRYPT_STRING_HEXADDR:
284     case CRYPT_STRING_HEXASCIIADDR:
285         FIXME("Unimplemented type %d\n", dwFlags & 0x0fffffff);
286         /* fall through */
287     default:
288         SetLastError(ERROR_INVALID_PARAMETER);
289         return FALSE;
290     }
291     return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString);
292 }
293 
294 static LONG encodeBase64W(const BYTE *in_buf, int in_len, LPCWSTR sep,
295  WCHAR* out_buf, DWORD *out_len)
296 {
297     int div, i;
298     const BYTE *d = in_buf;
299     int bytes = (in_len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0;
300     DWORD needed;
301     LPWSTR ptr;
302 
303     TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes);
304     needed = bytes + pad_bytes + 1;
305     needed += (needed / 64 + 1) * strlenW(sep);
306 
307     if (needed > *out_len)
308     {
309         *out_len = needed;
310         return ERROR_INSUFFICIENT_BUFFER;
311     }
312     else
313         *out_len = needed;
314 
315     /* Three bytes of input give 4 chars of output */
316     div = in_len / 3;
317 
318     ptr = out_buf;
319     i = 0;
320     while (div > 0)
321     {
322         if (i && i % 64 == 0)
323         {
324             strcpyW(ptr, sep);
325             ptr += strlenW(sep);
326         }
327         /* first char is the first 6 bits of the first byte*/
328         *ptr++ = b64[ ( d[0] >> 2) & 0x3f ];
329         /* second char is the last 2 bits of the first byte and the first 4
330          * bits of the second byte */
331         *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)];
332         /* third char is the last 4 bits of the second byte and the first 2
333          * bits of the third byte */
334         *ptr++ = b64[ ((d[1] << 2) & 0x3c) | (d[2] >> 6 & 0x03)];
335         /* fourth char is the remaining 6 bits of the third byte */
336         *ptr++ = b64[   d[2]       & 0x3f];
337         i += 4;
338         d += 3;
339         div--;
340     }
341 
342     switch(pad_bytes)
343     {
344         case 1:
345             /* first char is the first 6 bits of the first byte*/
346             *ptr++ = b64[ ( d[0] >> 2) & 0x3f ];
347             /* second char is the last 2 bits of the first byte and the first 4
348              * bits of the second byte */
349             *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)];
350             /* third char is the last 4 bits of the second byte padded with
351              * two zeroes */
352             *ptr++ = b64[ ((d[1] << 2) & 0x3c) ];
353             /* fourth char is a = to indicate one byte of padding */
354             *ptr++ = '=';
355             break;
356         case 2:
357             /* first char is the first 6 bits of the first byte*/
358             *ptr++ = b64[ ( d[0] >> 2) & 0x3f ];
359             /* second char is the last 2 bits of the first byte padded with
360              * four zeroes*/
361             *ptr++ = b64[ ((d[0] << 4) & 0x30)];
362             /* third char is = to indicate padding */
363             *ptr++ = '=';
364             /* fourth char is = to indicate padding */
365             *ptr++ = '=';
366             break;
367     }
368     strcpyW(ptr, sep);
369 
370     return ERROR_SUCCESS;
371 }
372 
373 static BOOL BinaryToBase64W(const BYTE *pbBinary,
374  DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD *pcchString)
375 {
376     static const WCHAR crlf[] = { '\r','\n',0 }, lf[] = { '\n',0 }, empty[] = {0};
377     BOOL ret = TRUE;
378     LPCWSTR header = NULL, trailer = NULL, sep;
379     DWORD charsNeeded;
380 
381     if (dwFlags & CRYPT_STRING_NOCR)
382         sep = lf;
383     else if (dwFlags & CRYPT_STRING_NOCRLF)
384         sep = empty;
385     else
386         sep = crlf;
387     switch (dwFlags & 0x0fffffff)
388     {
389     case CRYPT_STRING_BASE64:
390         /* no header or footer */
391         break;
392     case CRYPT_STRING_BASE64HEADER:
393         header = CERT_HEADER_W;
394         trailer = CERT_TRAILER_W;
395         break;
396     case CRYPT_STRING_BASE64REQUESTHEADER:
397         header = CERT_REQUEST_HEADER_W;
398         trailer = CERT_REQUEST_TRAILER_W;
399         break;
400     case CRYPT_STRING_BASE64X509CRLHEADER:
401         header = X509_HEADER_W;
402         trailer = X509_TRAILER_W;
403         break;
404     }
405 
406     charsNeeded = 0;
407     encodeBase64W(pbBinary, cbBinary, sep, NULL, &charsNeeded);
408     if (header)
409         charsNeeded += strlenW(header) + strlenW(sep);
410     if (trailer)
411         charsNeeded += strlenW(trailer) + strlenW(sep);
412     if (charsNeeded <= *pcchString)
413     {
414         LPWSTR ptr = pszString;
415         DWORD size = charsNeeded;
416 
417         if (header)
418         {
419             strcpyW(ptr, header);
420             ptr += strlenW(ptr);
421             strcpyW(ptr, sep);
422             ptr += strlenW(sep);
423         }
424         encodeBase64W(pbBinary, cbBinary, sep, ptr, &size);
425         ptr += size - 1;
426         if (trailer)
427         {
428             strcpyW(ptr, trailer);
429             ptr += strlenW(ptr);
430             strcpyW(ptr, sep);
431         }
432         *pcchString = charsNeeded - 1;
433     }
434     else if (pszString)
435     {
436         *pcchString = charsNeeded;
437         SetLastError(ERROR_INSUFFICIENT_BUFFER);
438         ret = FALSE;
439     }
440     else
441         *pcchString = charsNeeded;
442     return ret;
443 }
444 
445 BOOL WINAPI CryptBinaryToStringW(const BYTE *pbBinary,
446  DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD *pcchString)
447 {
448     BinaryToStringWFunc encoder = NULL;
449 
450     TRACE("(%p, %d, %08x, %p, %p)\n", pbBinary, cbBinary, dwFlags, pszString,
451      pcchString);
452 
453     if (!pbBinary)
454     {
455         SetLastError(ERROR_INVALID_PARAMETER);
456         return FALSE;
457     }
458     if (!pcchString)
459     {
460         SetLastError(ERROR_INVALID_PARAMETER);
461         return FALSE;
462     }
463 
464     switch (dwFlags & 0x0fffffff)
465     {
466     case CRYPT_STRING_BASE64:
467     case CRYPT_STRING_BASE64HEADER:
468     case CRYPT_STRING_BASE64REQUESTHEADER:
469     case CRYPT_STRING_BASE64X509CRLHEADER:
470         encoder = BinaryToBase64W;
471         break;
472     case CRYPT_STRING_BINARY:
473     case CRYPT_STRING_HEX:
474     case CRYPT_STRING_HEXASCII:
475     case CRYPT_STRING_HEXADDR:
476     case CRYPT_STRING_HEXASCIIADDR:
477         FIXME("Unimplemented type %d\n", dwFlags & 0x0fffffff);
478         /* fall through */
479     default:
480         SetLastError(ERROR_INVALID_PARAMETER);
481         return FALSE;
482     }
483     return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString);
484 }
485 
486 #define BASE64_DECODE_PADDING    0x100
487 #define BASE64_DECODE_WHITESPACE 0x200
488 #define BASE64_DECODE_INVALID    0x300
489 
490 static inline int decodeBase64Byte(int c)
491 {
492     int ret = BASE64_DECODE_INVALID;
493 
494     if (c >= 'A' && c <= 'Z')
495         ret = c - 'A';
496     else if (c >= 'a' && c <= 'z')
497         ret = c - 'a' + 26;
498     else if (c >= '0' && c <= '9')
499         ret = c - '0' + 52;
500     else if (c == '+')
501         ret = 62;
502     else if (c == '/')
503         ret = 63;
504     else if (c == '=')
505         ret = BASE64_DECODE_PADDING;
506     else if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
507         ret = BASE64_DECODE_WHITESPACE;
508     return ret;
509 }
510 
511 /* Unlike CryptStringToBinaryA, cchString is guaranteed to be the length of the
512  * string to convert.
513  */
514 typedef LONG (*StringToBinaryAFunc)(LPCSTR pszString, DWORD cchString,
515  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags);
516 
517 static LONG Base64ToBinary(const void* pszString, BOOL wide, DWORD cchString,
518  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
519 {
520     DWORD cbIn, cbValid, cbOut, hasPadding;
521     BYTE block[4];
522     for (cbIn = cbValid = cbOut = hasPadding = 0; cbIn < cchString; ++cbIn)
523     {
524         int c = wide ? (int)((WCHAR*)pszString)[cbIn] : (int)((char*)pszString)[cbIn];
525         int d = decodeBase64Byte(c);
526         if (d == BASE64_DECODE_INVALID)
527             goto invalid;
528         if (d == BASE64_DECODE_WHITESPACE)
529             continue;
530 
531         /* When padding starts, data is not acceptable */
532         if (hasPadding && d != BASE64_DECODE_PADDING)
533             goto invalid;
534 
535         /* Padding after a full block (like "VVVV=") is ok and stops decoding */
536         if (d == BASE64_DECODE_PADDING && (cbValid & 3) == 0)
537             break;
538 
539         cbValid += 1;
540 
541         if (d == BASE64_DECODE_PADDING)
542         {
543             hasPadding = 1;
544             /* When padding reaches a full block, stop decoding */
545             if ((cbValid & 3) == 0)
546                 break;
547             continue;
548         }
549 
550         /* cbOut is incremented in the 4-char block as follows: "1-23" */
551         if ((cbValid & 3) != 2)
552             cbOut += 1;
553     }
554     /* Fail if the block has bad padding; omitting padding is fine */
555     if ((cbValid & 3) != 0 && hasPadding)
556         goto invalid;
557     /* Check available buffer size */
558     if (pbBinary && *pcbBinary && cbOut > *pcbBinary)
559         goto overflow;
560     /* Convert the data; this step depends on the validity checks above! */
561     if (pbBinary) for (cbIn = cbValid = cbOut = 0; cbIn < cchString; ++cbIn)
562     {
563         int c = wide ? (int)((WCHAR*)pszString)[cbIn] : (int)((char*)pszString)[cbIn];
564         int d = decodeBase64Byte(c);
565         if (d == BASE64_DECODE_WHITESPACE)
566             continue;
567         if (d == BASE64_DECODE_PADDING)
568             break;
569         block[cbValid & 3] = d;
570         cbValid += 1;
571         switch (cbValid & 3) {
572         case 1:
573             pbBinary[cbOut++] = (block[0] << 2);
574             break;
575         case 2:
576             pbBinary[cbOut-1] = (block[0] << 2) | (block[1] >> 4);
577             break;
578         case 3:
579             pbBinary[cbOut++] = (block[1] << 4) | (block[2] >> 2);
580             break;
581         case 0:
582             pbBinary[cbOut++] = (block[2] << 6) | (block[3] >> 0);
583             break;
584         }
585     }
586     *pcbBinary = cbOut;
587     if (pdwSkip)
588         *pdwSkip = 0;
589     if (pdwFlags)
590         *pdwFlags = CRYPT_STRING_BASE64;
591     return ERROR_SUCCESS;
592 overflow:
593     return ERROR_INSUFFICIENT_BUFFER;
594 invalid:
595     *pcbBinary = cbOut;
596     return ERROR_INVALID_DATA;
597 }
598 
599 static LONG Base64ToBinaryA(LPCSTR pszString, DWORD cchString,
600  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
601 {
602     return Base64ToBinary(pszString, FALSE, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags);
603 }
604 
605 static LONG Base64WithHeaderAndTrailerToBinaryA(LPCSTR pszString,
606  DWORD cchString, BYTE *pbBinary,
607  DWORD *pcbBinary, DWORD *pdwSkip)
608 {
609     LONG ret;
610     LPCSTR header = CERT_HEADER_START;
611     LPCSTR trailer = CERT_TRAILER_START;
612 
613     LPCSTR headerBegins;
614     LPCSTR dataBegins;
615     LPCSTR trailerBegins;
616     size_t dataLength;
617 
618     if ((strlen(header) + strlen(trailer)) > cchString)
619     {
620         return ERROR_INVALID_DATA;
621     }
622 
623     if (!(headerBegins = strstr(pszString, header)))
624     {
625         TRACE("Can't find %s in %s.\n", header, pszString);
626         return ERROR_INVALID_DATA;
627     }
628 
629     dataBegins = headerBegins + strlen(header);
630     if (!(dataBegins = strstr(dataBegins, CERT_DELIMITER)))
631     {
632         return ERROR_INVALID_DATA;
633     }
634     dataBegins += strlen(CERT_DELIMITER);
635     if (*dataBegins == '\r') dataBegins++;
636     if (*dataBegins == '\n') dataBegins++;
637 
638     if (!(trailerBegins = strstr(dataBegins, trailer)))
639     {
640         return ERROR_INVALID_DATA;
641     }
642     if (*(trailerBegins-1) == '\n') trailerBegins--;
643     if (*(trailerBegins-1) == '\r') trailerBegins--;
644 
645     if (pdwSkip)
646        *pdwSkip = headerBegins - pszString;
647 
648     dataLength = trailerBegins - dataBegins;
649 
650     ret = Base64ToBinaryA(dataBegins, dataLength, pbBinary, pcbBinary, NULL,
651           NULL);
652 
653     return ret;
654 }
655 
656 static LONG Base64HeaderToBinaryA(LPCSTR pszString, DWORD cchString,
657  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
658 {
659     LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString,
660      pbBinary, pcbBinary, pdwSkip);
661 
662     if (!ret && pdwFlags)
663         *pdwFlags = CRYPT_STRING_BASE64HEADER;
664     return ret;
665 }
666 
667 static LONG Base64RequestHeaderToBinaryA(LPCSTR pszString, DWORD cchString,
668  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
669 {
670     LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString,
671      pbBinary, pcbBinary, pdwSkip);
672 
673     if (!ret && pdwFlags)
674         *pdwFlags = CRYPT_STRING_BASE64REQUESTHEADER;
675     return ret;
676 }
677 
678 static LONG Base64X509HeaderToBinaryA(LPCSTR pszString, DWORD cchString,
679  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
680 {
681     LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString,
682      pbBinary, pcbBinary, pdwSkip);
683 
684     if (!ret && pdwFlags)
685         *pdwFlags = CRYPT_STRING_BASE64X509CRLHEADER;
686     return ret;
687 }
688 
689 static LONG Base64AnyToBinaryA(LPCSTR pszString, DWORD cchString,
690  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
691 {
692     LONG ret;
693 
694     ret = Base64HeaderToBinaryA(pszString, cchString, pbBinary, pcbBinary,
695      pdwSkip, pdwFlags);
696     if (ret == ERROR_INVALID_DATA)
697         ret = Base64ToBinaryA(pszString, cchString, pbBinary, pcbBinary,
698          pdwSkip, pdwFlags);
699     return ret;
700 }
701 
702 static LONG DecodeBinaryToBinaryA(LPCSTR pszString, DWORD cchString,
703  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
704 {
705     LONG ret = ERROR_SUCCESS;
706 
707     if (*pcbBinary < cchString)
708     {
709         if (!pbBinary)
710             *pcbBinary = cchString;
711         else
712         {
713             ret = ERROR_INSUFFICIENT_BUFFER;
714             *pcbBinary = cchString;
715         }
716     }
717     else
718     {
719         if (cchString)
720             memcpy(pbBinary, pszString, cchString);
721         *pcbBinary = cchString;
722     }
723     return ret;
724 }
725 
726 static LONG DecodeAnyA(LPCSTR pszString, DWORD cchString,
727  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
728 {
729     LONG ret;
730 
731     ret = Base64HeaderToBinaryA(pszString, cchString, pbBinary, pcbBinary,
732      pdwSkip, pdwFlags);
733     if (ret == ERROR_INVALID_DATA)
734         ret = Base64ToBinaryA(pszString, cchString, pbBinary, pcbBinary,
735          pdwSkip, pdwFlags);
736     if (ret == ERROR_INVALID_DATA)
737         ret = DecodeBinaryToBinaryA(pszString, cchString, pbBinary, pcbBinary,
738          pdwSkip, pdwFlags);
739     return ret;
740 }
741 
742 BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString,
743  DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary,
744  DWORD *pdwSkip, DWORD *pdwFlags)
745 {
746     StringToBinaryAFunc decoder;
747     LONG ret;
748 
749     TRACE("(%s, %d, %08x, %p, %p, %p, %p)\n", debugstr_a(pszString),
750      cchString, dwFlags, pbBinary, pcbBinary, pdwSkip, pdwFlags);
751 
752     if (!pszString)
753     {
754         SetLastError(ERROR_INVALID_PARAMETER);
755         return FALSE;
756     }
757     /* Only the bottom byte contains valid types */
758     if (dwFlags & 0xfffffff0)
759     {
760         SetLastError(ERROR_INVALID_DATA);
761         return FALSE;
762     }
763     switch (dwFlags)
764     {
765     case CRYPT_STRING_BASE64_ANY:
766         decoder = Base64AnyToBinaryA;
767         break;
768     case CRYPT_STRING_BASE64:
769         decoder = Base64ToBinaryA;
770         break;
771     case CRYPT_STRING_BASE64HEADER:
772         decoder = Base64HeaderToBinaryA;
773         break;
774     case CRYPT_STRING_BASE64REQUESTHEADER:
775         decoder = Base64RequestHeaderToBinaryA;
776         break;
777     case CRYPT_STRING_BASE64X509CRLHEADER:
778         decoder = Base64X509HeaderToBinaryA;
779         break;
780     case CRYPT_STRING_BINARY:
781         decoder = DecodeBinaryToBinaryA;
782         break;
783     case CRYPT_STRING_ANY:
784         decoder = DecodeAnyA;
785         break;
786     case CRYPT_STRING_HEX:
787     case CRYPT_STRING_HEXASCII:
788     case CRYPT_STRING_HEXADDR:
789     case CRYPT_STRING_HEXASCIIADDR:
790         FIXME("Unimplemented type %d\n", dwFlags & 0x7fffffff);
791         /* fall through */
792     default:
793         SetLastError(ERROR_INVALID_PARAMETER);
794         return FALSE;
795     }
796     if (!cchString)
797         cchString = strlen(pszString);
798     ret = decoder(pszString, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags);
799     if (ret)
800         SetLastError(ret);
801     return ret == ERROR_SUCCESS;
802 }
803 
804 /* Unlike CryptStringToBinaryW, cchString is guaranteed to be the length of the
805  * string to convert.
806  */
807 typedef LONG (*StringToBinaryWFunc)(LPCWSTR pszString, DWORD cchString,
808  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags);
809 
810 static LONG Base64ToBinaryW(LPCWSTR pszString, DWORD cchString,
811  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
812 {
813     return Base64ToBinary(pszString, TRUE, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags);
814 }
815 
816 static LONG Base64WithHeaderAndTrailerToBinaryW(LPCWSTR pszString,
817  DWORD cchString, BYTE *pbBinary,
818  DWORD *pcbBinary, DWORD *pdwSkip)
819 {
820     LONG ret;
821     LPCWSTR header = CERT_HEADER_START_W;
822     LPCWSTR trailer = CERT_TRAILER_START_W;
823 
824     LPCWSTR headerBegins;
825     LPCWSTR dataBegins;
826     LPCWSTR trailerBegins;
827     size_t dataLength;
828 
829     if ((strlenW(header) + strlenW(trailer)) > cchString)
830     {
831         return ERROR_INVALID_DATA;
832     }
833 
834     if (!(headerBegins = strstrW(pszString, header)))
835     {
836         TRACE("Can't find %s in %s.\n", debugstr_w(header), debugstr_w(pszString));
837         return ERROR_INVALID_DATA;
838     }
839 
840     dataBegins = headerBegins + strlenW(header);
841     if (!(dataBegins = strstrW(dataBegins, CERT_DELIMITER_W)))
842     {
843         return ERROR_INVALID_DATA;
844     }
845     dataBegins += strlenW(CERT_DELIMITER_W);
846     if (*dataBegins == '\r') dataBegins++;
847     if (*dataBegins == '\n') dataBegins++;
848 
849     if (!(trailerBegins = strstrW(dataBegins, trailer)))
850     {
851         return ERROR_INVALID_DATA;
852     }
853     if (*(trailerBegins-1) == '\n') trailerBegins--;
854     if (*(trailerBegins-1) == '\r') trailerBegins--;
855 
856     if (pdwSkip)
857        *pdwSkip = headerBegins - pszString;
858 
859     dataLength = trailerBegins - dataBegins;
860 
861     ret = Base64ToBinaryW(dataBegins, dataLength, pbBinary, pcbBinary, NULL,
862           NULL);
863 
864     return ret;
865 }
866 
867 static LONG Base64HeaderToBinaryW(LPCWSTR pszString, DWORD cchString,
868  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
869 {
870     LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString,
871      pbBinary, pcbBinary, pdwSkip);
872 
873     if (!ret && pdwFlags)
874         *pdwFlags = CRYPT_STRING_BASE64HEADER;
875     return ret;
876 }
877 
878 static LONG Base64RequestHeaderToBinaryW(LPCWSTR pszString, DWORD cchString,
879  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
880 {
881     LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString,
882      pbBinary, pcbBinary, pdwSkip);
883 
884     if (!ret && pdwFlags)
885         *pdwFlags = CRYPT_STRING_BASE64REQUESTHEADER;
886     return ret;
887 }
888 
889 static LONG Base64X509HeaderToBinaryW(LPCWSTR pszString, DWORD cchString,
890  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
891 {
892     LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString,
893      pbBinary, pcbBinary, pdwSkip);
894 
895     if (!ret && pdwFlags)
896         *pdwFlags = CRYPT_STRING_BASE64X509CRLHEADER;
897     return ret;
898 }
899 
900 static LONG Base64AnyToBinaryW(LPCWSTR pszString, DWORD cchString,
901  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
902 {
903     LONG ret;
904 
905     ret = Base64HeaderToBinaryW(pszString, cchString, pbBinary, pcbBinary,
906      pdwSkip, pdwFlags);
907     if (ret == ERROR_INVALID_DATA)
908         ret = Base64ToBinaryW(pszString, cchString, pbBinary, pcbBinary,
909          pdwSkip, pdwFlags);
910     return ret;
911 }
912 
913 static LONG DecodeBinaryToBinaryW(LPCWSTR pszString, DWORD cchString,
914  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
915 {
916     LONG ret = ERROR_SUCCESS;
917 
918     if (*pcbBinary < cchString)
919     {
920         if (!pbBinary)
921             *pcbBinary = cchString;
922         else
923         {
924             ret = ERROR_INSUFFICIENT_BUFFER;
925             *pcbBinary = cchString;
926         }
927     }
928     else
929     {
930         if (cchString)
931             memcpy(pbBinary, pszString, cchString * sizeof(WCHAR));
932         *pcbBinary = cchString * sizeof(WCHAR);
933     }
934     return ret;
935 }
936 
937 static LONG DecodeAnyW(LPCWSTR pszString, DWORD cchString,
938  BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags)
939 {
940     LONG ret;
941 
942     ret = Base64HeaderToBinaryW(pszString, cchString, pbBinary, pcbBinary,
943      pdwSkip, pdwFlags);
944     if (ret == ERROR_INVALID_DATA)
945         ret = Base64ToBinaryW(pszString, cchString, pbBinary, pcbBinary,
946          pdwSkip, pdwFlags);
947     if (ret == ERROR_INVALID_DATA)
948         ret = DecodeBinaryToBinaryW(pszString, cchString, pbBinary, pcbBinary,
949          pdwSkip, pdwFlags);
950     return ret;
951 }
952 
953 BOOL WINAPI CryptStringToBinaryW(LPCWSTR pszString,
954  DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary,
955  DWORD *pdwSkip, DWORD *pdwFlags)
956 {
957     StringToBinaryWFunc decoder;
958     LONG ret;
959 
960     TRACE("(%s, %d, %08x, %p, %p, %p, %p)\n", debugstr_w(pszString),
961      cchString, dwFlags, pbBinary, pcbBinary, pdwSkip, pdwFlags);
962 
963     if (!pszString)
964     {
965         SetLastError(ERROR_INVALID_PARAMETER);
966         return FALSE;
967     }
968     /* Only the bottom byte contains valid types */
969     if (dwFlags & 0xfffffff0)
970     {
971         SetLastError(ERROR_INVALID_DATA);
972         return FALSE;
973     }
974     switch (dwFlags)
975     {
976     case CRYPT_STRING_BASE64_ANY:
977         decoder = Base64AnyToBinaryW;
978         break;
979     case CRYPT_STRING_BASE64:
980         decoder = Base64ToBinaryW;
981         break;
982     case CRYPT_STRING_BASE64HEADER:
983         decoder = Base64HeaderToBinaryW;
984         break;
985     case CRYPT_STRING_BASE64REQUESTHEADER:
986         decoder = Base64RequestHeaderToBinaryW;
987         break;
988     case CRYPT_STRING_BASE64X509CRLHEADER:
989         decoder = Base64X509HeaderToBinaryW;
990         break;
991     case CRYPT_STRING_BINARY:
992         decoder = DecodeBinaryToBinaryW;
993         break;
994     case CRYPT_STRING_ANY:
995         decoder = DecodeAnyW;
996         break;
997     case CRYPT_STRING_HEX:
998     case CRYPT_STRING_HEXASCII:
999     case CRYPT_STRING_HEXADDR:
1000     case CRYPT_STRING_HEXASCIIADDR:
1001         FIXME("Unimplemented type %d\n", dwFlags & 0x7fffffff);
1002         /* fall through */
1003     default:
1004         SetLastError(ERROR_INVALID_PARAMETER);
1005         return FALSE;
1006     }
1007     if (!cchString)
1008         cchString = strlenW(pszString);
1009     ret = decoder(pszString, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags);
1010     if (ret)
1011         SetLastError(ret);
1012     return ret == ERROR_SUCCESS;
1013 }
1014