/* * 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 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" 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 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(char 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; }