From a104479da1757fb225662676f6c4df221e17b9ee Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Thu, 11 Dec 2008 16:49:16 -0800 Subject: [PATCH] crypt32: Implement CryptStringToBinaryW. --- dlls/crypt32/base64.c | 324 +++++++++++++++++++++++++++++++++++++- dlls/crypt32/crypt32.spec | 2 +- 2 files changed, 323 insertions(+), 3 deletions(-) diff --git a/dlls/crypt32/base64.c b/dlls/crypt32/base64.c index 72cfe13e203..17e05d97530 100644 --- a/dlls/crypt32/base64.c +++ b/dlls/crypt32/base64.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -25,6 +25,7 @@ #include "winerror.h" #include "wincrypt.h" #include "wine/debug.h" +#include "wine/unicode.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); @@ -35,6 +36,25 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt); #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+/"; @@ -260,7 +280,7 @@ BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary, return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString); } -static inline BYTE decodeBase64Byte(char c) +static inline BYTE decodeBase64Byte(int c) { BYTE ret; @@ -577,3 +597,303 @@ BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString, 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; +} diff --git a/dlls/crypt32/crypt32.spec b/dlls/crypt32/crypt32.spec index 5c0c9a02824..c06bca1debc 100644 --- a/dlls/crypt32/crypt32.spec +++ b/dlls/crypt32/crypt32.spec @@ -99,7 +99,7 @@ @ stdcall CryptBinaryToStringA(ptr long long ptr ptr) @ stub CryptBinaryToStringW # (ptr long long ptr ptr) @ stdcall CryptStringToBinaryA(str long long ptr ptr ptr ptr) -@ stub CryptStringToBinaryW # (wstr long long ptr ptr ptr ptr) +@ stdcall CryptStringToBinaryW (wstr long long ptr ptr ptr ptr) @ stdcall CryptAcquireContextU(ptr wstr wstr long long) advapi32.CryptAcquireContextW @ stdcall CryptAcquireCertificatePrivateKey(ptr long ptr ptr ptr ptr) @ stub CryptCloseAsyncHandle