From: Aaro Altonen Subject: [PATCH v3 2/2] crypt32: Add implementation for CRYPT_STRING_HEX format Message-Id: Date: Fri, 27 Mar 2020 08:27:32 +0200 In-Reply-To: <20200327062732.129231-1-a.altonen@hotmail.com> References: <20200327062732.129231-1-a.altonen@hotmail.com> Add CRYPT_STRING_HEX format implementation for CryptStringToBinaryW() Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48487 Signed-off-by: Aaro Altonen --- v2: - remove wcstok() and string duplication entirely - properly check wchars (thanks Nikolay) v3: - If string length is zero, return ERROR_INVADLID_PARAMETER --- dlls/crypt32/base64.c | 77 +++++++++++++++++++++++++++++++++++++ dlls/crypt32/tests/base64.c | 76 ++++++++++++++++++------------------ 2 files changed, 115 insertions(+), 38 deletions(-) diff --git a/dlls/crypt32/base64.c b/dlls/crypt32/base64.c index 73619421ab..0a4ffa17a4 100644 --- a/dlls/crypt32/base64.c +++ b/dlls/crypt32/base64.c @@ -1036,6 +1036,81 @@ static LONG DecodeAnyW(LPCWSTR pszString, DWORD cchString, return ret; } +static BYTE char_to_byte(WCHAR c) +{ + BYTE lb = c & 0xff; + + /* upper byte is allowed to be 0xff */ + if (c >> 8 != 0xff && c >> 8 != 0x00) + return 0xff; + + if (lb >= '0' && lb <= '9') + return lb - '0'; + if (lb >= 'a' && lb <= 'f') + return lb - 'a' + 10; + if (lb >= 'A' && lb <= 'F') + return lb - 'A' + 10; + + return 0xff; +} + +static LONG DecodeHexToBinaryW(const WCHAR *instr, DWORD slen, + BYTE *out, DWORD *outlen, DWORD *skip, DWORD *flags) +{ + BYTE un, ln; + const WCHAR delim[] = { ' ', '\n', '\r', '\t' }; + const WCHAR *ptr = instr; + LONG ret = ERROR_SUCCESS; + DWORD i = 0, processed = 0; + + if (!outlen || !slen) + return ERROR_INVALID_PARAMETER; + + if (skip) + *skip = 0; + + while (i < slen) { + while (strchrW(delim, *ptr) && *ptr) ptr++, i++; + + if (i == slen) + break; + + /* uneven amount of data */ + if (i + 2 > slen) + { + if (!out) + *outlen = 0; + return ERROR_INVALID_DATA; + } + + un = char_to_byte(*ptr); + ln = char_to_byte(*(ptr + 1)); + + if (un == 0xff || ln == 0xff) + { + if (!out) + *outlen = 0; + return ERROR_INVALID_DATA; + } + + if (processed >= *outlen) + { + if (out) + return ERROR_MORE_DATA; + ret = ERROR_MORE_DATA; + } + else if (out) + out[processed] = (un << 4) | ln; + + processed++; + ptr += 2; + i += 2; + } + + *outlen = processed; + return ret; +} + BOOL WINAPI CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) @@ -1081,6 +1156,8 @@ BOOL WINAPI CryptStringToBinaryW(LPCWSTR pszString, decoder = DecodeAnyW; break; case CRYPT_STRING_HEX: + decoder = DecodeHexToBinaryW; + break; case CRYPT_STRING_HEXASCII: case CRYPT_STRING_HEXADDR: case CRYPT_STRING_HEXASCIIADDR: diff --git a/dlls/crypt32/tests/base64.c b/dlls/crypt32/tests/base64.c index 981a7a0065..3b9b87a0c1 100644 --- a/dlls/crypt32/tests/base64.c +++ b/dlls/crypt32/tests/base64.c @@ -823,8 +823,8 @@ static void testStringToBinaryW(void) data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 0, CRYPT_STRING_HEX, NULL, &data_len, &skip_, &flags); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 0, "Got %u, expected 0\n", data_len); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(data_len == 0, "Got %u, expected 0\n", data_len); /* length is uneven -> 13 */ SetLastError(0xdeadbeef); @@ -832,8 +832,8 @@ static void testStringToBinaryW(void) data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 3, CRYPT_STRING_HEX, NULL, &data_len, &skip_, &flags); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 0, "Got %u, expected 0\n", data_len); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(data_len == 0, "Got %u, expected 0\n", data_len); /* invalid 0x prefix -> 13 */ SetLastError(0xdeadbeef); @@ -841,8 +841,8 @@ static void testStringToBinaryW(void) data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 0, CRYPT_STRING_HEX, NULL, &data_len, &skip_, &flags); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 0, "Got %u, expected 0\n", data_len); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(data_len == 0, "Got %u, expected 0\n", data_len); /* invalid characters -> 13 */ SetLastError(0xdeadbeef); @@ -850,8 +850,8 @@ static void testStringToBinaryW(void) data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 0, CRYPT_STRING_HEX, NULL, &data_len, NULL, NULL); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 0, "Got %u, expected 0\n", data_len); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(data_len == 0, "Got %u, expected 0\n", data_len); /* insufficient buffer -> 234 */ SetLastError(0xdeadbeef); @@ -864,18 +864,18 @@ static void testStringToBinaryW(void) input = L"213c73796d6c696e6b3efffe"; ret = CryptStringToBinaryW(input, 24, CRYPT_STRING_HEX, out, &data_len, &skip_, &flags); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_MORE_DATA, "Got %d, expected 234\n", GetLastError()); + ok(GetLastError() == ERROR_MORE_DATA, "Got %d, expected 234\n", GetLastError()); ok(data_len == 4, "Got %u, expected 4\n", data_len); - todo_wine ok(!memcmp(out, expected, 4), "Invalid output from CryptStringToBinaryW()!\n"); + ok(!memcmp(out, expected, 4), "Invalid output from CryptStringToBinaryW()!\n"); /* valid data */ SetLastError(0xdeadbeef); input = L"213c73796d6c696e6b3efffe"; data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 24, CRYPT_STRING_HEX, NULL, &data_len, &skip_, &flags); - todo_wine ok(ret, "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); - todo_wine ok(data_len == 12, "Got %u, expected 12\n", data_len); + ok(ret, "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); + ok(data_len == 12, "Got %u, expected 12\n", data_len); /* valid data with white spaces */ SetLastError(0xdeadbeef); @@ -883,9 +883,9 @@ static void testStringToBinaryW(void) data_len = 0xdeadbeef; SetLastError(0xdeadbeef); ret = CryptStringToBinaryW(input, 25, CRYPT_STRING_HEX, NULL, &data_len, &skip_, &flags); - todo_wine ok(ret, "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef, "Got %d, expected 0xdeadbeef\n", GetLastError()); - todo_wine ok(data_len == 7, "Got %u, expected 7\n", data_len); + ok(ret, "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef, "Got %d, expected 0xdeadbeef\n", GetLastError()); + ok(data_len == 7, "Got %u, expected 7\n", data_len); /* valid data with white spaces but spacing breaks the valid data into invalid chunks */ SetLastError(0xdeadbeef); @@ -893,8 +893,8 @@ static void testStringToBinaryW(void) data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 0, CRYPT_STRING_HEX, NULL, &data_len, &skip_, &flags); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 0, "Got %u, expected 0\n", data_len); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(data_len == 0, "Got %u, expected 0\n", data_len); /* if "input" contains both valid and invalid data and "out" is valid, "out" shall contain all valid bytes * until an invalid sequence is reached */ @@ -904,7 +904,7 @@ static void testStringToBinaryW(void) input = L"21 3 c ff"; ret = CryptStringToBinaryW(input, 0, CRYPT_STRING_HEX, out, &data_len, &skip_, &flags); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); ok(data_len == 4, "Got %u, expected 4\n", data_len); /* valid data */ @@ -916,19 +916,19 @@ static void testStringToBinaryW(void) input = L"213c73796d6c696e6b3efffe"; data_len = 256; ret = CryptStringToBinaryW(input, 24, CRYPT_STRING_HEX, out, &data_len, &skip_, &flags); - todo_wine ok(ret, "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); - todo_wine ok(data_len == 12, "Got %u, expected 12\n", data_len); - todo_wine ok(!memcmp(out, expected, 12), "Invalid output from CryptStringToBinaryW()!\n"); + ok(ret, "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); + ok(data_len == 12, "Got %u, expected 12\n", data_len); + ok(!memcmp(out, expected, 12), "Invalid output from CryptStringToBinaryW()!\n"); /* invalid data but length small enough that it's never detected */ SetLastError(0xdeadbeef); input = L"abcdefhhh"; data_len = 0xdeadbeef; ret = CryptStringToBinaryW(input, 4, CRYPT_STRING_HEX, NULL, &data_len, NULL, NULL); - todo_wine ok(ret, "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); - todo_wine ok(data_len == 2, "Got %u, expected 2\n", data_len); + ok(ret, "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); + ok(data_len == 2, "Got %u, expected 2\n", data_len); /* invalid data but length small enough that it's never detected, with whitespaces */ SetLastError(0xdeadbeef); @@ -936,24 +936,24 @@ static void testStringToBinaryW(void) input = L"\t\t21 fe f f f"; data_len = 256; ret = CryptStringToBinaryW(input, 5, CRYPT_STRING_HEX, out, &data_len, &skip_, &flags); - todo_wine ok(ret, "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); - todo_wine ok(data_len == 1, "Got %u, expected 1\n", data_len); + ok(ret, "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); + ok(data_len == 1, "Got %u, expected 1\n", data_len); SetLastError(0xdeadbeef); data_len = 0xdeadbeef; ret = CryptStringToBinaryW(winput1, 6, CRYPT_STRING_HEX, NULL, &data_len, NULL, NULL); - todo_wine ok(ret || broken(!ret), "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == 122), + ok(ret || broken(!ret), "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef || broken(GetLastError() == 122), "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 3 || broken(data_len == 0xdeadbeef), "Got %u, expected 3\n", data_len); + ok(data_len == 3 || broken(data_len == 0xdeadbeef), "Got %u, expected 3\n", data_len); SetLastError(0xdeadbeef); data_len = 0xdeadbeef; ret = CryptStringToBinaryW(winput2, 6, CRYPT_STRING_HEX, NULL, &data_len, NULL, NULL); ok(!ret, "Got %u, expected zero\n", ret); - todo_wine ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); - todo_wine ok(data_len == 0, "Got %u, expected 0\n", data_len); + ok(GetLastError() == ERROR_INVALID_DATA, "Got %d, expected 13\n", GetLastError()); + ok(data_len == 0, "Got %u, expected 0\n", data_len); /* valid data but parse only the first 6 bytes (12 chars) */ SetLastError(0xdeadbeef); @@ -963,10 +963,10 @@ static void testStringToBinaryW(void) input = L"213c73796d6c696e6b3efffe"; data_len = 256; ret = CryptStringToBinaryW(input, 12, CRYPT_STRING_HEX, out, &data_len, &skip_, &flags); - todo_wine ok(ret, "Got %u, expected one\n", ret); - todo_wine ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); - todo_wine ok(data_len == 6, "Got %u, expected 6\n", data_len); - todo_wine ok(!memcmp(out, expected, 6), "Invalid output from CryptStringToBinaryW()!\n"); + ok(ret, "Got %u, expected one\n", ret); + ok(GetLastError() == 0xdeadbeef, "Got %x, expected 0xdeadbeef\n", GetLastError()); + ok(data_len == 6, "Got %u, expected 6\n", data_len); + ok(!memcmp(out, expected, 6), "Invalid output from CryptStringToBinaryW()!\n"); } START_TEST(base64) -- 2.25.2