Sync to trunk (r44371)
[reactos.git] / reactos / dll / win32 / crypt32 / base64.c
index e4c2dd4..33111f2 100644 (file)
@@ -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,11 +36,32 @@ 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+/";
 
 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)
@@ -77,6 +99,7 @@ static LONG encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep,
 
     TRACE("bytes is %d, pad bytes is %d\n", bytes, pad_bytes);
     needed = bytes + pad_bytes + 1;
+    if (sep)
     needed += (needed / 64 + 1) * strlen(sep);
 
     if (needed > *out_len)
@@ -94,7 +117,7 @@ static LONG encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep,
     i = 0;
     while (div > 0)
     {
-        if (i && i % 64 == 0)
+        if (sep && i && i % 64 == 0)
         {
             strcpy(ptr, sep);
             ptr += strlen(sep);
@@ -140,6 +163,7 @@ static LONG encodeBase64A(const BYTE *in_buf, int in_len, LPCSTR sep,
             *ptr++ = '=';
             break;
     }
+    if (sep)
     strcpy(ptr, sep);
 
     return ERROR_SUCCESS;
@@ -150,14 +174,16 @@ static BOOL BinaryToBase64A(const BYTE *pbBinary,
 {
     static const char crlf[] = "\r\n", lf[] = "\n";
     BOOL ret = TRUE;
-    LPCSTR header = NULL, trailer = NULL, sep = NULL;
+    LPCSTR header = NULL, trailer = NULL, sep;
     DWORD charsNeeded;
 
     if (dwFlags & CRYPT_STRING_NOCR)
         sep = lf;
+    else if (dwFlags & CRYPT_STRING_NOCRLF)
+        sep = NULL;
     else
         sep = crlf;
-    switch (dwFlags & 0x7fffffff)
+    switch (dwFlags & 0x0fffffff)
     {
     case CRYPT_STRING_BASE64:
         /* no header or footer */
@@ -178,6 +204,7 @@ static BOOL BinaryToBase64A(const BYTE *pbBinary,
 
     charsNeeded = 0;
     encodeBase64A(pbBinary, cbBinary, sep, NULL, &charsNeeded);
+    if (sep)
     charsNeeded += strlen(sep);
     if (header)
         charsNeeded += strlen(header) + strlen(sep);
@@ -192,18 +219,24 @@ static BOOL BinaryToBase64A(const BYTE *pbBinary,
         {
             strcpy(ptr, header);
             ptr += strlen(ptr);
+            if (sep)
+            {
             strcpy(ptr, sep);
             ptr += strlen(sep);
         }
+        }
         encodeBase64A(pbBinary, cbBinary, sep, ptr, &size);
         ptr += size - 1;
         if (trailer)
         {
             strcpy(ptr, trailer);
             ptr += strlen(ptr);
+            if (sep)
+            {
             strcpy(ptr, sep);
             ptr += strlen(sep);
         }
+        }
         *pcchString = charsNeeded - 1;
     }
     else if (pszString)
@@ -222,7 +255,7 @@ BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary,
 {
     BinaryToStringAFunc encoder = NULL;
 
-    TRACE("(%p, %ld, %08lx, %p, %p)\n", pbBinary, cbBinary, dwFlags, pszString,
+    TRACE("(%p, %d, %08x, %p, %p)\n", pbBinary, cbBinary, dwFlags, pszString,
      pcchString);
 
     if (!pbBinary)
@@ -236,7 +269,7 @@ BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary,
         return FALSE;
     }
 
-    switch (dwFlags & 0x7fffffff)
+    switch (dwFlags & 0x0fffffff)
     {
     case CRYPT_STRING_BINARY:
         encoder = EncodeBinaryToBinaryA;
@@ -251,7 +284,7 @@ BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary,
     case CRYPT_STRING_HEXASCII:
     case CRYPT_STRING_HEXADDR:
     case CRYPT_STRING_HEXASCIIADDR:
-        FIXME("Unimplemented type %ld\n", dwFlags & 0x7fffffff);
+        FIXME("Unimplemented type %d\n", dwFlags & 0x0fffffff);
         /* fall through */
     default:
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -260,7 +293,210 @@ BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary,
     return encoder(pbBinary, cbBinary, dwFlags, pszString, pcchString);
 }
 
-static inline BYTE decodeBase64Byte(char c)
+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;
+    if (sep)
+        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 (sep && 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;
+    }
+    if (sep)
+        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 };
+    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 = NULL;
+    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 (sep)
+        charsNeeded += strlenW(sep);
+    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);
+            if (sep)
+            {
+                strcpyW(ptr, sep);
+                ptr += strlenW(sep);
+            }
+        }
+        encodeBase64W(pbBinary, cbBinary, sep, ptr, &size);
+        ptr += size - 1;
+        if (trailer)
+        {
+            strcpyW(ptr, trailer);
+            ptr += strlenW(ptr);
+            if (sep)
+            {
+                strcpyW(ptr, sep);
+                ptr += strlenW(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);
+}
+
+static inline BYTE decodeBase64Byte(int c)
 {
     BYTE ret;
 
@@ -523,7 +759,7 @@ BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString,
     StringToBinaryAFunc decoder;
     LONG ret;
 
-    TRACE("(%s, %ld, %08lx, %p, %p, %p, %p)\n", debugstr_a(pszString),
+    TRACE("(%s, %d, %08x, %p, %p, %p, %p)\n", debugstr_a(pszString),
      cchString, dwFlags, pbBinary, pcbBinary, pdwSkip, pdwFlags);
 
     if (!pszString)
@@ -564,7 +800,7 @@ BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString,
     case CRYPT_STRING_HEXASCII:
     case CRYPT_STRING_HEXADDR:
     case CRYPT_STRING_HEXASCIIADDR:
-        FIXME("Unimplemented type %ld\n", dwFlags & 0x7fffffff);
+        FIXME("Unimplemented type %d\n", dwFlags & 0x7fffffff);
         /* fall through */
     default:
         SetLastError(ERROR_INVALID_PARAMETER);
@@ -577,3 +813,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;
+}