| /* | |
| * 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_HEADER_START "-----BEGIN " | |
| #define CERT_DELIMITER "-----" | |
| #define CERT_TRAILER "-----END CERTIFICATE-----" | |
| #define CERT_TRAILER_START "-----END " | |
| #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_HEADER_START_W[] = { | |
| '-','-','-','-','-','B','E','G','I','N',' ',0 }; | |
| static const WCHAR CERT_DELIMITER_W[] = { | |
| '-','-','-','-','-',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_TRAILER_START_W[] = { | |
| '-','-','-','-','-','E','N','D',' ',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); | |
| typedef BOOL (*BinaryToStringWFunc)(const BYTE *pbBinary, | |
| DWORD cbBinary, DWORD dwFlags, LPWSTR 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; | |
| DWORD charsNeeded; | |
| if (dwFlags & CRYPT_STRING_NOCR) | |
| sep = lf; | |
| else if (dwFlags & CRYPT_STRING_NOCRLF) | |
| sep = ""; | |
| else | |
| sep = crlf; | |
| switch (dwFlags & 0x0fffffff) | |
| { | |
| 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); | |
| 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); | |
| } | |
| *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 & 0x0fffffff) | |
| { | |
| 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 & 0x0fffffff); | |
| /* fall through */ | |
| default: | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString); | |
| } | |
| static LONG encodeBase64W(const BYTE *in_buf, int in_len, LPCWSTR sep, | |
| WCHAR* 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; | |
| LPWSTR ptr; | |
| TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes); | |
| needed = bytes + pad_bytes + 1; | |
| needed += (needed / 64 + 1) * strlenW(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) | |
| { | |
| strcpyW(ptr, sep); | |
| ptr += strlenW(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; | |
| } | |
| strcpyW(ptr, sep); | |
| return ERROR_SUCCESS; | |
| } | |
| static BOOL BinaryToBase64W(const BYTE *pbBinary, | |
| DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD *pcchString) | |
| { | |
| static const WCHAR crlf[] = { '\r','\n',0 }, lf[] = { '\n',0 }, empty[] = {0}; | |
| BOOL ret = TRUE; | |
| LPCWSTR header = NULL, trailer = NULL, sep; | |
| DWORD charsNeeded; | |
| if (dwFlags & CRYPT_STRING_NOCR) | |
| sep = lf; | |
| else if (dwFlags & CRYPT_STRING_NOCRLF) | |
| sep = empty; | |
| else | |
| sep = crlf; | |
| switch (dwFlags & 0x0fffffff) | |
| { | |
| case CRYPT_STRING_BASE64: | |
| /* no header or footer */ | |
| break; | |
| case CRYPT_STRING_BASE64HEADER: | |
| header = CERT_HEADER_W; | |
| trailer = CERT_TRAILER_W; | |
| break; | |
| case CRYPT_STRING_BASE64REQUESTHEADER: | |
| header = CERT_REQUEST_HEADER_W; | |
| trailer = CERT_REQUEST_TRAILER_W; | |
| break; | |
| case CRYPT_STRING_BASE64X509CRLHEADER: | |
| header = X509_HEADER_W; | |
| trailer = X509_TRAILER_W; | |
| break; | |
| } | |
| charsNeeded = 0; | |
| encodeBase64W(pbBinary, cbBinary, sep, NULL, &charsNeeded); | |
| if (header) | |
| charsNeeded += strlenW(header) + strlenW(sep); | |
| if (trailer) | |
| charsNeeded += strlenW(trailer) + strlenW(sep); | |
| if (charsNeeded <= *pcchString) | |
| { | |
| LPWSTR ptr = pszString; | |
| DWORD size = charsNeeded; | |
| if (header) | |
| { | |
| strcpyW(ptr, header); | |
| ptr += strlenW(ptr); | |
| strcpyW(ptr, sep); | |
| ptr += strlenW(sep); | |
| } | |
| encodeBase64W(pbBinary, cbBinary, sep, ptr, &size); | |
| ptr += size - 1; | |
| if (trailer) | |
| { | |
| strcpyW(ptr, trailer); | |
| ptr += strlenW(ptr); | |
| strcpyW(ptr, sep); | |
| } | |
| *pcchString = charsNeeded - 1; | |
| } | |
| else if (pszString) | |
| { | |
| *pcchString = charsNeeded; | |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); | |
| ret = FALSE; | |
| } | |
| else | |
| *pcchString = charsNeeded; | |
| return ret; | |
| } | |
| BOOL WINAPI CryptBinaryToStringW(const BYTE *pbBinary, | |
| DWORD cbBinary, DWORD dwFlags, LPWSTR pszString, DWORD *pcchString) | |
| { | |
| BinaryToStringWFunc 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 & 0x0fffffff) | |
| { | |
| case CRYPT_STRING_BASE64: | |
| case CRYPT_STRING_BASE64HEADER: | |
| case CRYPT_STRING_BASE64REQUESTHEADER: | |
| case CRYPT_STRING_BASE64X509CRLHEADER: | |
| encoder = BinaryToBase64W; | |
| break; | |
| case CRYPT_STRING_BINARY: | |
| case CRYPT_STRING_HEX: | |
| case CRYPT_STRING_HEXASCII: | |
| case CRYPT_STRING_HEXADDR: | |
| case CRYPT_STRING_HEXASCIIADDR: | |
| FIXME("Unimplemented type %d\n", dwFlags & 0x0fffffff); | |
| /* fall through */ | |
| default: | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString); | |
| } | |
| #define BASE64_DECODE_PADDING 0x100 | |
| #define BASE64_DECODE_WHITESPACE 0x200 | |
| #define BASE64_DECODE_INVALID 0x300 | |
| static inline int decodeBase64Byte(int c) | |
| { | |
| int ret = BASE64_DECODE_INVALID; | |
| 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 if (c == '=') | |
| ret = BASE64_DECODE_PADDING; | |
| else if (c == ' ' || c == '\t' || c == '\r' || c == '\n') | |
| ret = BASE64_DECODE_WHITESPACE; | |
| return ret; | |
| } | |
| /* 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 Base64ToBinary(const void* pszString, BOOL wide, DWORD cchString, | |
| BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | |
| { | |
| DWORD cbIn, cbValid, cbOut, hasPadding; | |
| BYTE block[4]; | |
| for (cbIn = cbValid = cbOut = hasPadding = 0; cbIn < cchString; ++cbIn) | |
| { | |
| int c = wide ? (int)((WCHAR*)pszString)[cbIn] : (int)((char*)pszString)[cbIn]; | |
| int d = decodeBase64Byte(c); | |
| if (d == BASE64_DECODE_INVALID) | |
| goto invalid; | |
| if (d == BASE64_DECODE_WHITESPACE) | |
| continue; | |
| /* When padding starts, data is not acceptable */ | |
| if (hasPadding && d != BASE64_DECODE_PADDING) | |
| goto invalid; | |
| /* Padding after a full block (like "VVVV=") is ok and stops decoding */ | |
| if (d == BASE64_DECODE_PADDING && (cbValid & 3) == 0) | |
| break; | |
| cbValid += 1; | |
| if (d == BASE64_DECODE_PADDING) | |
| { | |
| hasPadding = 1; | |
| /* When padding reaches a full block, stop decoding */ | |
| if ((cbValid & 3) == 0) | |
| break; | |
| continue; | |
| } | |
| /* cbOut is incremented in the 4-char block as follows: "1-23" */ | |
| if ((cbValid & 3) != 2) | |
| cbOut += 1; | |
| } | |
| /* Fail if the block has bad padding; omitting padding is fine */ | |
| if ((cbValid & 3) != 0 && hasPadding) | |
| goto invalid; | |
| /* Check available buffer size */ | |
| if (pbBinary && *pcbBinary && cbOut > *pcbBinary) | |
| goto overflow; | |
| /* Convert the data; this step depends on the validity checks above! */ | |
| if (pbBinary) for (cbIn = cbValid = cbOut = 0; cbIn < cchString; ++cbIn) | |
| { | |
| int c = wide ? (int)((WCHAR*)pszString)[cbIn] : (int)((char*)pszString)[cbIn]; | |
| int d = decodeBase64Byte(c); | |
| if (d == BASE64_DECODE_WHITESPACE) | |
| continue; | |
| if (d == BASE64_DECODE_PADDING) | |
| break; | |
| block[cbValid & 3] = d; | |
| cbValid += 1; | |
| switch (cbValid & 3) { | |
| case 1: | |
| pbBinary[cbOut++] = (block[0] << 2); | |
| break; | |
| case 2: | |
| pbBinary[cbOut-1] = (block[0] << 2) | (block[1] >> 4); | |
| break; | |
| case 3: | |
| pbBinary[cbOut++] = (block[1] << 4) | (block[2] >> 2); | |
| break; | |
| case 0: | |
| pbBinary[cbOut++] = (block[2] << 6) | (block[3] >> 0); | |
| break; | |
| } | |
| } | |
| *pcbBinary = cbOut; | |
| if (pdwSkip) | |
| *pdwSkip = 0; | |
| if (pdwFlags) | |
| *pdwFlags = CRYPT_STRING_BASE64; | |
| return ERROR_SUCCESS; | |
| overflow: | |
| return ERROR_INSUFFICIENT_BUFFER; | |
| invalid: | |
| *pcbBinary = cbOut; | |
| return ERROR_INVALID_DATA; | |
| } | |
| static LONG Base64ToBinaryA(LPCSTR pszString, DWORD cchString, | |
| BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | |
| { | |
| return Base64ToBinary(pszString, FALSE, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags); | |
| } | |
| static LONG Base64WithHeaderAndTrailerToBinaryA(LPCSTR pszString, | |
| DWORD cchString, BYTE *pbBinary, | |
| DWORD *pcbBinary, DWORD *pdwSkip) | |
| { | |
| LONG ret; | |
| LPCSTR header = CERT_HEADER_START; | |
| LPCSTR trailer = CERT_TRAILER_START; | |
| LPCSTR headerBegins; | |
| LPCSTR dataBegins; | |
| LPCSTR trailerBegins; | |
| size_t dataLength; | |
| if ((strlen(header) + strlen(trailer)) > cchString) | |
| { | |
| return ERROR_INVALID_DATA; | |
| } | |
| if (!(headerBegins = strstr(pszString, header))) | |
| { | |
| TRACE("Can't find %s in %s.\n", header, pszString); | |
| return ERROR_INVALID_DATA; | |
| } | |
| dataBegins = headerBegins + strlen(header); | |
| if (!(dataBegins = strstr(dataBegins, CERT_DELIMITER))) | |
| { | |
| return ERROR_INVALID_DATA; | |
| } | |
| dataBegins += strlen(CERT_DELIMITER); | |
| if (*dataBegins == '\r') dataBegins++; | |
| if (*dataBegins == '\n') dataBegins++; | |
| if (!(trailerBegins = strstr(dataBegins, trailer))) | |
| { | |
| return ERROR_INVALID_DATA; | |
| } | |
| if (*(trailerBegins-1) == '\n') trailerBegins--; | |
| if (*(trailerBegins-1) == '\r') trailerBegins--; | |
| if (pdwSkip) | |
| *pdwSkip = headerBegins - pszString; | |
| dataLength = trailerBegins - dataBegins; | |
| ret = Base64ToBinaryA(dataBegins, dataLength, pbBinary, pcbBinary, NULL, | |
| NULL); | |
| return ret; | |
| } | |
| static LONG Base64HeaderToBinaryA(LPCSTR pszString, DWORD cchString, | |
| BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | |
| { | |
| LONG ret = Base64WithHeaderAndTrailerToBinaryA(pszString, cchString, | |
| 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, | |
| 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, | |
| 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; | |
| } | |
| /* 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) | |
| { | |
| return Base64ToBinary(pszString, TRUE, cchString, pbBinary, pcbBinary, pdwSkip, pdwFlags); | |
| } | |
| static LONG Base64WithHeaderAndTrailerToBinaryW(LPCWSTR pszString, | |
| DWORD cchString, BYTE *pbBinary, | |
| DWORD *pcbBinary, DWORD *pdwSkip) | |
| { | |
| LONG ret; | |
| LPCWSTR header = CERT_HEADER_START_W; | |
| LPCWSTR trailer = CERT_TRAILER_START_W; | |
| LPCWSTR headerBegins; | |
| LPCWSTR dataBegins; | |
| LPCWSTR trailerBegins; | |
| size_t dataLength; | |
| if ((strlenW(header) + strlenW(trailer)) > cchString) | |
| { | |
| return ERROR_INVALID_DATA; | |
| } | |
| if (!(headerBegins = strstrW(pszString, header))) | |
| { | |
| TRACE("Can't find %s in %s.\n", debugstr_w(header), debugstr_w(pszString)); | |
| return ERROR_INVALID_DATA; | |
| } | |
| dataBegins = headerBegins + strlenW(header); | |
| if (!(dataBegins = strstrW(dataBegins, CERT_DELIMITER_W))) | |
| { | |
| return ERROR_INVALID_DATA; | |
| } | |
| dataBegins += strlenW(CERT_DELIMITER_W); | |
| if (*dataBegins == '\r') dataBegins++; | |
| if (*dataBegins == '\n') dataBegins++; | |
| if (!(trailerBegins = strstrW(dataBegins, trailer))) | |
| { | |
| return ERROR_INVALID_DATA; | |
| } | |
| if (*(trailerBegins-1) == '\n') trailerBegins--; | |
| if (*(trailerBegins-1) == '\r') trailerBegins--; | |
| if (pdwSkip) | |
| *pdwSkip = headerBegins - pszString; | |
| dataLength = trailerBegins - dataBegins; | |
| ret = Base64ToBinaryW(dataBegins, dataLength, pbBinary, pcbBinary, NULL, | |
| NULL); | |
| return ret; | |
| } | |
| static LONG Base64HeaderToBinaryW(LPCWSTR pszString, DWORD cchString, | |
| BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) | |
| { | |
| LONG ret = Base64WithHeaderAndTrailerToBinaryW(pszString, cchString, | |
| 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, | |
| 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, | |
| 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; | |
| } |