|  | /* | 
|  | * base64 encoder/decoder | 
|  | * | 
|  | * Copyright 2005 by Kai Blin | 
|  | * Copyright 2006 Juan Lang | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winerror.h" | 
|  | #include "wincrypt.h" | 
|  | #include "wine/debug.h" | 
|  | #include "wine/unicode.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(crypt); | 
|  |  | 
|  | #define CERT_HEADER          "-----BEGIN CERTIFICATE-----" | 
|  | #define CERT_TRAILER         "-----END CERTIFICATE-----" | 
|  | #define CERT_REQUEST_HEADER  "-----BEGIN NEW CERTIFICATE REQUEST-----" | 
|  | #define CERT_REQUEST_TRAILER "-----END NEW CERTIFICATE REQUEST-----" | 
|  | #define X509_HEADER          "-----BEGIN X509 CRL-----" | 
|  | #define X509_TRAILER         "-----END X509 CRL-----" | 
|  |  | 
|  | static const WCHAR CERT_HEADER_W[] = { | 
|  | '-','-','-','-','-','B','E','G','I','N',' ','C','E','R','T','I','F','I','C', | 
|  | 'A','T','E','-','-','-','-','-',0 }; | 
|  | static const WCHAR CERT_TRAILER_W[] = { | 
|  | '-','-','-','-','-','E','N','D',' ','C','E','R','T','I','F','I','C','A','T', | 
|  | 'E','-','-','-','-','-',0 }; | 
|  | static const WCHAR CERT_REQUEST_HEADER_W[] = { | 
|  | '-','-','-','-','-','B','E','G','I','N',' ','N','E','W',' ','C','E','R','T', | 
|  | 'I','F','I','C','A','T','E','R','E','Q','U','E','S','T','-','-','-','-','-',0 }; | 
|  | static const WCHAR CERT_REQUEST_TRAILER_W[] = { | 
|  | '-','-','-','-','-','E','N','D',' ','N','E','W',' ','C','E','R','T','I','F', | 
|  | 'I','C','A','T','E','R','E','Q','U','E','S','T','-','-','-','-','-',0 }; | 
|  | static const WCHAR X509_HEADER_W[] = { | 
|  | '-','-','-','-','-','B','E','G','I','N',' ','X','5','0','9',' ','C','R','L', | 
|  | '-','-','-','-','-',0 }; | 
|  | static const WCHAR X509_TRAILER_W[] = { | 
|  | '-','-','-','-','-','E','N','D',' ','X','5','0','9',' ','C','R','L','-','-', | 
|  | '-','-','-',0 }; | 
|  |  | 
|  | static const char b64[] = | 
|  | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | 
|  |  | 
|  | typedef BOOL (*BinaryToStringAFunc)(const BYTE *pbBinary, | 
|  | DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString); | 
|  |  | 
|  | static BOOL EncodeBinaryToBinaryA(const BYTE *pbBinary, | 
|  | DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString) | 
|  | { | 
|  | BOOL ret = TRUE; | 
|  |  | 
|  | if (*pcchString < cbBinary) | 
|  | { | 
|  | if (!pszString) | 
|  | *pcchString = cbBinary; | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | *pcchString = cbBinary; | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (cbBinary) | 
|  | memcpy(pszString, pbBinary, cbBinary); | 
|  | *pcchString = cbBinary; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep, | 
|  | char* out_buf, DWORD *out_len) | 
|  | { | 
|  | int div, i; | 
|  | const BYTE *d = in_buf; | 
|  | int bytes = (in_len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0; | 
|  | DWORD needed; | 
|  | LPSTR ptr; | 
|  |  | 
|  | TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes); | 
|  | needed = bytes + pad_bytes + 1; | 
|  | needed += (needed / 64 + 1) * strlen(sep); | 
|  |  | 
|  | if (needed > *out_len) | 
|  | { | 
|  | *out_len = needed; | 
|  | return ERROR_INSUFFICIENT_BUFFER; | 
|  | } | 
|  | else | 
|  | *out_len = needed; | 
|  |  | 
|  | /* Three bytes of input give 4 chars of output */ | 
|  | div = in_len / 3; | 
|  |  | 
|  | ptr = out_buf; | 
|  | i = 0; | 
|  | while (div > 0) | 
|  | { | 
|  | if (i && i % 64 == 0) | 
|  | { | 
|  | strcpy(ptr, sep); | 
|  | ptr += strlen(sep); | 
|  | } | 
|  | /* first char is the first 6 bits of the first byte*/ | 
|  | *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; | 
|  | /* second char is the last 2 bits of the first byte and the first 4 | 
|  | * bits of the second byte */ | 
|  | *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; | 
|  | /* third char is the last 4 bits of the second byte and the first 2 | 
|  | * bits of the third byte */ | 
|  | *ptr++ = b64[ ((d[1] << 2) & 0x3c) | (d[2] >> 6 & 0x03)]; | 
|  | /* fourth char is the remaining 6 bits of the third byte */ | 
|  | *ptr++ = b64[   d[2]       & 0x3f]; | 
|  | i += 4; | 
|  | d += 3; | 
|  | div--; | 
|  | } | 
|  |  | 
|  | switch(pad_bytes) | 
|  | { | 
|  | case 1: | 
|  | /* first char is the first 6 bits of the first byte*/ | 
|  | *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; | 
|  | /* second char is the last 2 bits of the first byte and the first 4 | 
|  | * bits of the second byte */ | 
|  | *ptr++ = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; | 
|  | /* third char is the last 4 bits of the second byte padded with | 
|  | * two zeroes */ | 
|  | *ptr++ = b64[ ((d[1] << 2) & 0x3c) ]; | 
|  | /* fourth char is a = to indicate one byte of padding */ | 
|  | *ptr++ = '='; | 
|  | break; | 
|  | case 2: | 
|  | /* first char is the first 6 bits of the first byte*/ | 
|  | *ptr++ = b64[ ( d[0] >> 2) & 0x3f ]; | 
|  | /* second char is the last 2 bits of the first byte padded with | 
|  | * four zeroes*/ | 
|  | *ptr++ = b64[ ((d[0] << 4) & 0x30)]; | 
|  | /* third char is = to indicate padding */ | 
|  | *ptr++ = '='; | 
|  | /* fourth char is = to indicate padding */ | 
|  | *ptr++ = '='; | 
|  | break; | 
|  | } | 
|  | strcpy(ptr, sep); | 
|  |  | 
|  | return ERROR_SUCCESS; | 
|  | } | 
|  |  | 
|  | static BOOL BinaryToBase64A(const BYTE *pbBinary, | 
|  | DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString) | 
|  | { | 
|  | static const char crlf[] = "\r\n", lf[] = "\n"; | 
|  | BOOL ret = TRUE; | 
|  | LPCSTR header = NULL, trailer = NULL, sep = NULL; | 
|  | DWORD charsNeeded; | 
|  |  | 
|  | if (dwFlags & CRYPT_STRING_NOCR) | 
|  | sep = lf; | 
|  | else | 
|  | sep = crlf; | 
|  | switch (dwFlags & 0x7fffffff) | 
|  | { | 
|  | case CRYPT_STRING_BASE64: | 
|  | /* no header or footer */ | 
|  | break; | 
|  | case CRYPT_STRING_BASE64HEADER: | 
|  | header = CERT_HEADER; | 
|  | trailer = CERT_TRAILER; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64REQUESTHEADER: | 
|  | header = CERT_REQUEST_HEADER; | 
|  | trailer = CERT_REQUEST_TRAILER; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64X509CRLHEADER: | 
|  | header = X509_HEADER; | 
|  | trailer = X509_TRAILER; | 
|  | break; | 
|  | } | 
|  |  | 
|  | charsNeeded = 0; | 
|  | encodeBase64A(pbBinary, cbBinary, sep, NULL, &charsNeeded); | 
|  | charsNeeded += strlen(sep); | 
|  | if (header) | 
|  | charsNeeded += strlen(header) + strlen(sep); | 
|  | if (trailer) | 
|  | charsNeeded += strlen(trailer) + strlen(sep); | 
|  | if (charsNeeded <= *pcchString) | 
|  | { | 
|  | LPSTR ptr = pszString; | 
|  | DWORD size = charsNeeded; | 
|  |  | 
|  | if (header) | 
|  | { | 
|  | strcpy(ptr, header); | 
|  | ptr += strlen(ptr); | 
|  | strcpy(ptr, sep); | 
|  | ptr += strlen(sep); | 
|  | } | 
|  | encodeBase64A(pbBinary, cbBinary, sep, ptr, &size); | 
|  | ptr += size - 1; | 
|  | if (trailer) | 
|  | { | 
|  | strcpy(ptr, trailer); | 
|  | ptr += strlen(ptr); | 
|  | strcpy(ptr, sep); | 
|  | ptr += strlen(sep); | 
|  | } | 
|  | *pcchString = charsNeeded - 1; | 
|  | } | 
|  | else if (pszString) | 
|  | { | 
|  | *pcchString = charsNeeded; | 
|  | SetLastError(ERROR_INSUFFICIENT_BUFFER); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | *pcchString = charsNeeded; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary, | 
|  | DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString) | 
|  | { | 
|  | BinaryToStringAFunc encoder = NULL; | 
|  |  | 
|  | TRACE("(%p, %d, %08x, %p, %p)\n", pbBinary, cbBinary, dwFlags, pszString, | 
|  | pcchString); | 
|  |  | 
|  | if (!pbBinary) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | if (!pcchString) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | switch (dwFlags & 0x7fffffff) | 
|  | { | 
|  | case CRYPT_STRING_BINARY: | 
|  | encoder = EncodeBinaryToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64: | 
|  | case CRYPT_STRING_BASE64HEADER: | 
|  | case CRYPT_STRING_BASE64REQUESTHEADER: | 
|  | case CRYPT_STRING_BASE64X509CRLHEADER: | 
|  | encoder = BinaryToBase64A; | 
|  | break; | 
|  | case CRYPT_STRING_HEX: | 
|  | case CRYPT_STRING_HEXASCII: | 
|  | case CRYPT_STRING_HEXADDR: | 
|  | case CRYPT_STRING_HEXASCIIADDR: | 
|  | FIXME("Unimplemented type %d\n", dwFlags & 0x7fffffff); | 
|  | /* fall through */ | 
|  | default: | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString); | 
|  | } | 
|  |  | 
|  | static inline BYTE decodeBase64Byte(int c) | 
|  | { | 
|  | BYTE ret; | 
|  |  | 
|  | if (c >= 'A' && c <= 'Z') | 
|  | ret = c - 'A'; | 
|  | else if (c >= 'a' && c <= 'z') | 
|  | ret = c - 'a' + 26; | 
|  | else if (c >= '0' && c <= '9') | 
|  | ret = c - '0' + 52; | 
|  | else if (c == '+') | 
|  | ret = 62; | 
|  | else if (c == '/') | 
|  | ret = 63; | 
|  | else | 
|  | ret = 64; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG decodeBase64Block(const char *in_buf, int in_len, | 
|  | const char **nextBlock, PBYTE out_buf, DWORD *out_len) | 
|  | { | 
|  | int len = in_len, i; | 
|  | const char *d = in_buf; | 
|  | int  ip0, ip1, ip2, ip3; | 
|  |  | 
|  | if (len < 4) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | i = 0; | 
|  | if (d[2] == '=') | 
|  | { | 
|  | if ((ip0 = decodeBase64Byte(d[0])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip1 = decodeBase64Byte(d[1])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | if (out_buf) | 
|  | out_buf[i] = (ip0 << 2) | (ip1 >> 4); | 
|  | i++; | 
|  | } | 
|  | else if (d[3] == '=') | 
|  | { | 
|  | if ((ip0 = decodeBase64Byte(d[0])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip1 = decodeBase64Byte(d[1])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip2 = decodeBase64Byte(d[2])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | if (out_buf) | 
|  | { | 
|  | out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); | 
|  | out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); | 
|  | } | 
|  | i += 2; | 
|  | } | 
|  | else | 
|  | { | 
|  | if ((ip0 = decodeBase64Byte(d[0])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip1 = decodeBase64Byte(d[1])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip2 = decodeBase64Byte(d[2])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip3 = decodeBase64Byte(d[3])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | if (out_buf) | 
|  | { | 
|  | out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); | 
|  | out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); | 
|  | out_buf[i + 2] = (ip2 << 6) |  ip3; | 
|  | } | 
|  | i += 3; | 
|  | } | 
|  | if (len >= 6 && d[4] == '\r' && d[5] == '\n') | 
|  | *nextBlock = d + 6; | 
|  | else if (len >= 5 && d[4] == '\n') | 
|  | *nextBlock = d + 5; | 
|  | else if (len >= 4 && d[4]) | 
|  | *nextBlock = d + 4; | 
|  | else | 
|  | *nextBlock = NULL; | 
|  | *out_len = i; | 
|  | return ERROR_SUCCESS; | 
|  | } | 
|  |  | 
|  | /* Unlike CryptStringToBinaryA, cchString is guaranteed to be the length of the | 
|  | * string to convert. | 
|  | */ | 
|  | typedef LONG (*StringToBinaryAFunc)(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags); | 
|  |  | 
|  | static LONG Base64ToBinaryA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = ERROR_SUCCESS; | 
|  | const char *nextBlock; | 
|  | DWORD outLen = 0; | 
|  |  | 
|  | nextBlock = pszString; | 
|  | while (nextBlock && !ret) | 
|  | { | 
|  | DWORD len = 0; | 
|  |  | 
|  | ret = decodeBase64Block(nextBlock, cchString - (nextBlock - pszString), | 
|  | &nextBlock, pbBinary ? pbBinary + outLen : NULL, &len); | 
|  | if (!ret) | 
|  | outLen += len; | 
|  | if (cchString - (nextBlock - pszString) <= 0) | 
|  | nextBlock = NULL; | 
|  | } | 
|  | *pcbBinary = outLen; | 
|  | if (!ret) | 
|  | { | 
|  | if (pdwSkip) | 
|  | *pdwSkip = 0; | 
|  | if (pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64; | 
|  | } | 
|  | else if (ret == ERROR_INSUFFICIENT_BUFFER) | 
|  | { | 
|  | if (!pbBinary) | 
|  | ret = ERROR_SUCCESS; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64WithHeaderAndTrailerToBinaryA(LPCSTR pszString, | 
|  | DWORD cchString, LPCSTR header, LPCSTR trailer, BYTE *pbBinary, | 
|  | DWORD *pcbBinary, DWORD *pdwSkip) | 
|  | { | 
|  | LONG ret; | 
|  | LPCSTR ptr; | 
|  |  | 
|  | if (cchString > strlen(header) + strlen(trailer) | 
|  | && (ptr = strstr(pszString, header)) != NULL) | 
|  | { | 
|  | LPCSTR trailerSpot = pszString + cchString - strlen(trailer); | 
|  |  | 
|  | if (pszString[cchString - 1] == '\n') | 
|  | { | 
|  | cchString--; | 
|  | trailerSpot--; | 
|  | } | 
|  | if (pszString[cchString - 1] == '\r') | 
|  | { | 
|  | cchString--; | 
|  | trailerSpot--; | 
|  | } | 
|  | if (!strncmp(trailerSpot, trailer, strlen(trailer))) | 
|  | { | 
|  | if (pdwSkip) | 
|  | *pdwSkip = ptr - pszString; | 
|  | ptr += strlen(header); | 
|  | if (*ptr == '\r') ptr++; | 
|  | if (*ptr == '\n') ptr++; | 
|  | cchString -= ptr - pszString + strlen(trailer); | 
|  | ret = Base64ToBinaryA(ptr, cchString, pbBinary, pcbBinary, NULL, | 
|  | NULL); | 
|  | } | 
|  | else | 
|  | ret = ERROR_INVALID_DATA; | 
|  | } | 
|  | else | 
|  | ret = ERROR_INVALID_DATA; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64HeaderToBinaryA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString, | 
|  | CERT_HEADER, CERT_TRAILER, pbBinary, pcbBinary, pdwSkip); | 
|  |  | 
|  | if (!ret && pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64HEADER; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64RequestHeaderToBinaryA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString, | 
|  | CERT_REQUEST_HEADER, CERT_REQUEST_TRAILER, pbBinary, pcbBinary, pdwSkip); | 
|  |  | 
|  | if (!ret && pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64REQUESTHEADER; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64X509HeaderToBinaryA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString, | 
|  | X509_HEADER, X509_TRAILER, pbBinary, pcbBinary, pdwSkip); | 
|  |  | 
|  | if (!ret && pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64X509CRLHEADER; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64AnyToBinaryA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret; | 
|  |  | 
|  | ret = Base64HeaderToBinaryA(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | if (ret == ERROR_INVALID_DATA) | 
|  | ret = Base64ToBinaryA(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG DecodeBinaryToBinaryA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = ERROR_SUCCESS; | 
|  |  | 
|  | if (*pcbBinary < cchString) | 
|  | { | 
|  | if (!pbBinary) | 
|  | *pcbBinary = cchString; | 
|  | else | 
|  | { | 
|  | ret = ERROR_INSUFFICIENT_BUFFER; | 
|  | *pcbBinary = cchString; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (cchString) | 
|  | memcpy(pbBinary, pszString, cchString); | 
|  | *pcbBinary = cchString; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG DecodeAnyA(LPCSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret; | 
|  |  | 
|  | ret = Base64HeaderToBinaryA(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | if (ret == ERROR_INVALID_DATA) | 
|  | ret = Base64ToBinaryA(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | if (ret == ERROR_INVALID_DATA) | 
|  | ret = DecodeBinaryToBinaryA(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString, | 
|  | DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary, | 
|  | DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | StringToBinaryAFunc decoder; | 
|  | LONG ret; | 
|  |  | 
|  | TRACE("(%s, %d, %08x, %p, %p, %p, %p)\n", debugstr_a(pszString), | 
|  | cchString, dwFlags, pbBinary, pcbBinary, pdwSkip, pdwFlags); | 
|  |  | 
|  | if (!pszString) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | /* Only the bottom byte contains valid types */ | 
|  | if (dwFlags & 0xfffffff0) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | return FALSE; | 
|  | } | 
|  | switch (dwFlags) | 
|  | { | 
|  | case CRYPT_STRING_BASE64_ANY: | 
|  | decoder = Base64AnyToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64: | 
|  | decoder = Base64ToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64HEADER: | 
|  | decoder = Base64HeaderToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64REQUESTHEADER: | 
|  | decoder = Base64RequestHeaderToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64X509CRLHEADER: | 
|  | decoder = Base64X509HeaderToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_BINARY: | 
|  | decoder = DecodeBinaryToBinaryA; | 
|  | break; | 
|  | case CRYPT_STRING_ANY: | 
|  | decoder = DecodeAnyA; | 
|  | break; | 
|  | case CRYPT_STRING_HEX: | 
|  | case CRYPT_STRING_HEXASCII: | 
|  | case CRYPT_STRING_HEXADDR: | 
|  | case CRYPT_STRING_HEXASCIIADDR: | 
|  | FIXME("Unimplemented type %d\n", dwFlags & 0x7fffffff); | 
|  | /* fall through */ | 
|  | default: | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | if (!cchString) | 
|  | cchString = strlen(pszString); | 
|  | ret = decoder(pszString, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags); | 
|  | if (ret) | 
|  | SetLastError(ret); | 
|  | return (ret == ERROR_SUCCESS) ? TRUE : FALSE; | 
|  | } | 
|  |  | 
|  | static LONG decodeBase64BlockW(const WCHAR *in_buf, int in_len, | 
|  | const WCHAR **nextBlock, PBYTE out_buf, DWORD *out_len) | 
|  | { | 
|  | int len = in_len, i; | 
|  | const WCHAR *d = in_buf; | 
|  | int  ip0, ip1, ip2, ip3; | 
|  |  | 
|  | if (len < 4) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | i = 0; | 
|  | if (d[2] == '=') | 
|  | { | 
|  | if ((ip0 = decodeBase64Byte(d[0])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip1 = decodeBase64Byte(d[1])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | if (out_buf) | 
|  | out_buf[i] = (ip0 << 2) | (ip1 >> 4); | 
|  | i++; | 
|  | } | 
|  | else if (d[3] == '=') | 
|  | { | 
|  | if ((ip0 = decodeBase64Byte(d[0])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip1 = decodeBase64Byte(d[1])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip2 = decodeBase64Byte(d[2])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | if (out_buf) | 
|  | { | 
|  | out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); | 
|  | out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); | 
|  | } | 
|  | i += 2; | 
|  | } | 
|  | else | 
|  | { | 
|  | if ((ip0 = decodeBase64Byte(d[0])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip1 = decodeBase64Byte(d[1])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip2 = decodeBase64Byte(d[2])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  | if ((ip3 = decodeBase64Byte(d[3])) > 63) | 
|  | return ERROR_INVALID_DATA; | 
|  |  | 
|  | if (out_buf) | 
|  | { | 
|  | out_buf[i + 0] = (ip0 << 2) | (ip1 >> 4); | 
|  | out_buf[i + 1] = (ip1 << 4) | (ip2 >> 2); | 
|  | out_buf[i + 2] = (ip2 << 6) |  ip3; | 
|  | } | 
|  | i += 3; | 
|  | } | 
|  | if (len >= 6 && d[4] == '\r' && d[5] == '\n') | 
|  | *nextBlock = d + 6; | 
|  | else if (len >= 5 && d[4] == '\n') | 
|  | *nextBlock = d + 5; | 
|  | else if (len >= 4 && d[4]) | 
|  | *nextBlock = d + 4; | 
|  | else | 
|  | *nextBlock = NULL; | 
|  | *out_len = i; | 
|  | return ERROR_SUCCESS; | 
|  | } | 
|  |  | 
|  | /* Unlike CryptStringToBinaryW, cchString is guaranteed to be the length of the | 
|  | * string to convert. | 
|  | */ | 
|  | typedef LONG (*StringToBinaryWFunc)(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags); | 
|  |  | 
|  | static LONG Base64ToBinaryW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = ERROR_SUCCESS; | 
|  | const WCHAR *nextBlock; | 
|  | DWORD outLen = 0; | 
|  |  | 
|  | nextBlock = pszString; | 
|  | while (nextBlock && !ret) | 
|  | { | 
|  | DWORD len = 0; | 
|  |  | 
|  | ret = decodeBase64BlockW(nextBlock, cchString - (nextBlock - pszString), | 
|  | &nextBlock, pbBinary ? pbBinary + outLen : NULL, &len); | 
|  | if (!ret) | 
|  | outLen += len; | 
|  | if (cchString - (nextBlock - pszString) <= 0) | 
|  | nextBlock = NULL; | 
|  | } | 
|  | *pcbBinary = outLen; | 
|  | if (!ret) | 
|  | { | 
|  | if (pdwSkip) | 
|  | *pdwSkip = 0; | 
|  | if (pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64; | 
|  | } | 
|  | else if (ret == ERROR_INSUFFICIENT_BUFFER) | 
|  | { | 
|  | if (!pbBinary) | 
|  | ret = ERROR_SUCCESS; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64WithHeaderAndTrailerToBinaryW(LPCWSTR pszString, | 
|  | DWORD cchString, LPCWSTR header, LPCWSTR trailer, BYTE *pbBinary, | 
|  | DWORD *pcbBinary, DWORD *pdwSkip) | 
|  | { | 
|  | LONG ret; | 
|  | LPCWSTR ptr; | 
|  |  | 
|  | if (cchString > strlenW(header) + strlenW(trailer) | 
|  | && (ptr = strstrW(pszString, header)) != NULL) | 
|  | { | 
|  | LPCWSTR trailerSpot = pszString + cchString - strlenW(trailer); | 
|  |  | 
|  | if (pszString[cchString - 1] == '\n') | 
|  | { | 
|  | cchString--; | 
|  | trailerSpot--; | 
|  | } | 
|  | if (pszString[cchString - 1] == '\r') | 
|  | { | 
|  | cchString--; | 
|  | trailerSpot--; | 
|  | } | 
|  | if (!strncmpW(trailerSpot, trailer, strlenW(trailer))) | 
|  | { | 
|  | if (pdwSkip) | 
|  | *pdwSkip = ptr - pszString; | 
|  | ptr += strlenW(header); | 
|  | if (*ptr == '\r') ptr++; | 
|  | if (*ptr == '\n') ptr++; | 
|  | cchString -= ptr - pszString + strlenW(trailer); | 
|  | ret = Base64ToBinaryW(ptr, cchString, pbBinary, pcbBinary, NULL, | 
|  | NULL); | 
|  | } | 
|  | else | 
|  | ret = ERROR_INVALID_DATA; | 
|  | } | 
|  | else | 
|  | ret = ERROR_INVALID_DATA; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64HeaderToBinaryW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString, | 
|  | CERT_HEADER_W, CERT_TRAILER_W, pbBinary, pcbBinary, pdwSkip); | 
|  |  | 
|  | if (!ret && pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64HEADER; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64RequestHeaderToBinaryW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString, | 
|  | CERT_REQUEST_HEADER_W, CERT_REQUEST_TRAILER_W, pbBinary, pcbBinary, | 
|  | pdwSkip); | 
|  |  | 
|  | if (!ret && pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64REQUESTHEADER; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64X509HeaderToBinaryW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString, | 
|  | X509_HEADER_W, X509_TRAILER_W, pbBinary, pcbBinary, pdwSkip); | 
|  |  | 
|  | if (!ret && pdwFlags) | 
|  | *pdwFlags = CRYPT_STRING_BASE64X509CRLHEADER; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG Base64AnyToBinaryW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret; | 
|  |  | 
|  | ret = Base64HeaderToBinaryW(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | if (ret == ERROR_INVALID_DATA) | 
|  | ret = Base64ToBinaryW(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG DecodeBinaryToBinaryW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret = ERROR_SUCCESS; | 
|  |  | 
|  | if (*pcbBinary < cchString) | 
|  | { | 
|  | if (!pbBinary) | 
|  | *pcbBinary = cchString; | 
|  | else | 
|  | { | 
|  | ret = ERROR_INSUFFICIENT_BUFFER; | 
|  | *pcbBinary = cchString; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (cchString) | 
|  | memcpy(pbBinary, pszString, cchString * sizeof(WCHAR)); | 
|  | *pcbBinary = cchString * sizeof(WCHAR); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static LONG DecodeAnyW(LPCWSTR pszString, DWORD cchString, | 
|  | BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | LONG ret; | 
|  |  | 
|  | ret = Base64HeaderToBinaryW(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | if (ret == ERROR_INVALID_DATA) | 
|  | ret = Base64ToBinaryW(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | if (ret == ERROR_INVALID_DATA) | 
|  | ret = DecodeBinaryToBinaryW(pszString, cchString, pbBinary, pcbBinary, | 
|  | pdwSkip, pdwFlags); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | BOOL WINAPI CryptStringToBinaryW(LPCWSTR pszString, | 
|  | DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary, | 
|  | DWORD *pdwSkip, DWORD *pdwFlags) | 
|  | { | 
|  | StringToBinaryWFunc decoder; | 
|  | LONG ret; | 
|  |  | 
|  | TRACE("(%s, %d, %08x, %p, %p, %p, %p)\n", debugstr_w(pszString), | 
|  | cchString, dwFlags, pbBinary, pcbBinary, pdwSkip, pdwFlags); | 
|  |  | 
|  | if (!pszString) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | /* Only the bottom byte contains valid types */ | 
|  | if (dwFlags & 0xfffffff0) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | return FALSE; | 
|  | } | 
|  | switch (dwFlags) | 
|  | { | 
|  | case CRYPT_STRING_BASE64_ANY: | 
|  | decoder = Base64AnyToBinaryW; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64: | 
|  | decoder = Base64ToBinaryW; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64HEADER: | 
|  | decoder = Base64HeaderToBinaryW; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64REQUESTHEADER: | 
|  | decoder = Base64RequestHeaderToBinaryW; | 
|  | break; | 
|  | case CRYPT_STRING_BASE64X509CRLHEADER: | 
|  | decoder = Base64X509HeaderToBinaryW; | 
|  | break; | 
|  | case CRYPT_STRING_BINARY: | 
|  | decoder = DecodeBinaryToBinaryW; | 
|  | break; | 
|  | case CRYPT_STRING_ANY: | 
|  | decoder = DecodeAnyW; | 
|  | break; | 
|  | case CRYPT_STRING_HEX: | 
|  | case CRYPT_STRING_HEXASCII: | 
|  | case CRYPT_STRING_HEXADDR: | 
|  | case CRYPT_STRING_HEXASCIIADDR: | 
|  | FIXME("Unimplemented type %d\n", dwFlags & 0x7fffffff); | 
|  | /* fall through */ | 
|  | default: | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | if (!cchString) | 
|  | cchString = strlenW(pszString); | 
|  | ret = decoder(pszString, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags); | 
|  | if (ret) | 
|  | SetLastError(ret); | 
|  | return (ret == ERROR_SUCCESS) ? TRUE : FALSE; | 
|  | } |