/* * 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 #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 (pszString) { if (*pcchString < cbBinary) { SetLastError(ERROR_INSUFFICIENT_BUFFER); ret = FALSE; } else if (cbBinary) memcpy(pszString, pbBinary, cbBinary); } else *pcchString = cbBinary; return ret; } static DWORD stradd(LPSTR ptr, LPCSTR end, LPCSTR s, DWORD slen) { if (ptr + slen > end) slen = end - ptr; memcpy(ptr, s, slen); return slen; } static DWORD 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; LPSTR ptr; LPCSTR end; char chunk[4]; if (!out_buf) { TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes); *out_len = bytes + pad_bytes; *out_len += (*out_len / 64 + (*out_len % 64 ? 1 : 0)) * strlen(sep) + 1; return 0; } /* Three bytes of input give 4 chars of output */ div = in_len / 3; ptr = out_buf; end = ptr + *out_len; i = 0; while (div > 0 && ptr < end) { if (i && i % 64 == 0) ptr += stradd(ptr, end, sep, strlen(sep)); /* first char is the first 6 bits of the first byte*/ chunk[0] = 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 */ chunk[1] = 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 */ chunk[2] = b64[ ((d[1] << 2) & 0x3c) | (d[2] >> 6 & 0x03)]; /* fourth char is the remaining 6 bits of the third byte */ chunk[3] = b64[ d[2] & 0x3f]; ptr += stradd(ptr, end, chunk, 4); i += 4; d += 3; div--; } switch(pad_bytes) { case 1: /* first char is the first 6 bits of the first byte*/ chunk[0] = 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 */ chunk[1] = b64[ ((d[0] << 4) & 0x30) | (d[1] >> 4 & 0x0f)]; /* third char is the last 4 bits of the second byte padded with * two zeroes */ chunk[2] = b64[ ((d[1] << 2) & 0x3c) ]; /* fourth char is a = to indicate one byte of padding */ chunk[3] = '='; ptr += stradd(ptr, end, chunk, 4); break; case 2: /* first char is the first 6 bits of the first byte*/ chunk[0] = b64[ ( d[0] >> 2) & 0x3f ]; /* second char is the last 2 bits of the first byte padded with * four zeroes*/ chunk[1] = b64[ ((d[0] << 4) & 0x30)]; /* third char is = to indicate padding */ chunk[2] = '='; /* fourth char is = to indicate padding */ chunk[3] = '='; ptr += stradd(ptr, end, chunk, 4); break; } ptr += stradd(ptr, end, sep, strlen(sep)); return ptr - out_buf; } 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 (pszString) { LPSTR ptr = pszString; DWORD size = *pcchString; LPSTR end = ptr + size; if (header) { ptr += stradd(ptr, end, header, strlen(header)); ptr += stradd(ptr, end, sep, strlen(sep)); size = end - ptr; } ptr += encodeBase64A(pbBinary, cbBinary, sep, ptr, &size); if (trailer) { ptr += stradd(ptr, end, trailer, strlen(trailer)); ptr += stradd(ptr, end, sep, strlen(sep)); } if (ptr < end) *ptr = '\0'; if (charsNeeded <= *pcchString) { *pcchString = charsNeeded - 1; } else { *pcchString = charsNeeded; SetLastError(ERROR_MORE_DATA); 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 BOOL EncodeBinaryToBinaryW(const BYTE *in_buf, DWORD in_len, DWORD flags, WCHAR *out_buf, DWORD *out_len) { BOOL ret = TRUE; if (out_buf) { if (*out_len < in_len) { SetLastError(ERROR_INSUFFICIENT_BUFFER); ret = FALSE; } else if (in_len) memcpy(out_buf, in_buf, in_len); } else *out_len = in_len; return ret; } 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; needed += (needed / 64 + (needed % 64 ? 1 : 0)) * strlenW(sep); needed++; 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 (pszString) { 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 { *pcchString = charsNeeded; SetLastError(ERROR_MORE_DATA); ret = FALSE; } } else *pcchString = charsNeeded; return ret; } static BOOL BinaryToHexW(const BYTE *bin, DWORD nbin, DWORD flags, LPWSTR str, DWORD *nstr) { static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; DWORD needed; if (flags & CRYPT_STRING_NOCRLF) needed = 0; else if (flags & CRYPT_STRING_NOCR) needed = 1; else needed = 2; needed += nbin * 2 + 1; if (needed > *nstr) { SetLastError(ERROR_MORE_DATA); return FALSE; } *nstr = needed; if (!str) return TRUE; while (nbin--) { *str++ = hex[(*bin >> 4) & 0xf]; *str++ = hex[*bin & 0xf]; bin++; } if (flags & CRYPT_STRING_NOCR) *str++ = '\n'; else if (!(flags & CRYPT_STRING_NOCRLF)) { *str++ = '\r'; *str++ = '\n'; } *str = 0; *nstr = needed - 1; return TRUE; } 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_BINARY: encoder = EncodeBinaryToBinaryW; break; case CRYPT_STRING_BASE64: case CRYPT_STRING_BASE64HEADER: case CRYPT_STRING_BASE64REQUESTHEADER: case CRYPT_STRING_BASE64X509CRLHEADER: encoder = BinaryToBase64W; break; case CRYPT_STRING_HEXRAW: encoder = BinaryToHexW; 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); } #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, debugstr_an(pszString, cchString)); 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_an(pszString, cchString ? cchString : -1), 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_wn(pszString, cchString)); 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_wn(pszString, cchString ? cchString : -1), 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; }