362 lines
14 KiB
C
362 lines
14 KiB
C
/*
|
|
* Unit test suite for crypt32.dll's CryptEncodeObjectEx/CryptDecodeObjectEx
|
|
*
|
|
* Copyright 2005 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <windef.h>
|
|
#include <winbase.h>
|
|
#include <winerror.h>
|
|
#include <wincrypt.h>
|
|
|
|
#include "wine/test.h"
|
|
|
|
struct encodedInt
|
|
{
|
|
int val;
|
|
BYTE encoded[6];
|
|
};
|
|
|
|
static const struct encodedInt ints[] = {
|
|
{ 1, { 2, 1, 1 } },
|
|
{ 127, { 2, 1, 0x7f } },
|
|
{ 128, { 2, 2, 0x00, 0x80 } },
|
|
{ 256, { 2, 2, 0x01, 0x00 } },
|
|
{ -128, { 2, 1, 0x80 } },
|
|
{ -129, { 2, 2, 0xff, 0x7f } },
|
|
{ 0xbaddf00d, { 2, 4, 0xba, 0xdd, 0xf0, 0x0d } },
|
|
};
|
|
|
|
static void test_encodeint(void)
|
|
{
|
|
DWORD bufSize = 0;
|
|
int i;
|
|
BOOL ret;
|
|
|
|
/* CryptEncodeObjectEx with NULL bufSize crashes..
|
|
ret = CryptEncodeObjectEx(3, X509_INTEGER, &ints[0].val, 0, NULL, NULL,
|
|
NULL);
|
|
*/
|
|
/* check bogus encoding */
|
|
ret = CryptEncodeObjectEx(0, X509_INTEGER, &ints[0].val, 0, NULL, NULL,
|
|
&bufSize);
|
|
ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
|
|
"Expected ERROR_FILE_NOT_FOUND, got %ld\n", GetLastError());
|
|
/* check with NULL integer buffer. Windows XP incorrectly returns an
|
|
* NTSTATUS.
|
|
*/
|
|
ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_INTEGER, NULL, 0, NULL,
|
|
NULL, &bufSize);
|
|
ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() ==
|
|
STATUS_ACCESS_VIOLATION), "Unexpected error code %ld\n", GetLastError());
|
|
for (i = 0; i < sizeof(ints) / sizeof(ints[0]); i++)
|
|
{
|
|
BYTE *buf = NULL;
|
|
|
|
ret = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_INTEGER, &ints[i].val, 0, NULL, NULL, &bufSize);
|
|
ok(ret, "Expected success, got %ld\n", GetLastError());
|
|
ret = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_INTEGER, &ints[i].val, CRYPT_ENCODE_ALLOC_FLAG, NULL,
|
|
(BYTE *)&buf, &bufSize);
|
|
ok(ret, "CryptEncodeObjectEx failed: %ld\n", GetLastError());
|
|
ok(buf[0] == 2, "Got unexpected type %d for integer (expected 2)\n",
|
|
buf[0]);
|
|
ok(!memcmp(buf + 1, ints[i].encoded + 1, ints[i].encoded[1] + 1),
|
|
"Encoded value of 0x%08x didn't match expected\n", ints[i].val);
|
|
LocalFree(buf);
|
|
}
|
|
}
|
|
|
|
static void test_decodeint(void)
|
|
{
|
|
static const char bigInt[] = { 2, 5, 0xff, 0xfe, 0xff, 0xfe, 0xff };
|
|
static const char testStr[] = { 16, 4, 't', 'e', 's', 't' };
|
|
BYTE *buf = NULL;
|
|
DWORD bufSize = 0;
|
|
int i;
|
|
BOOL ret;
|
|
|
|
/* CryptDecodeObjectEx with NULL bufSize crashes..
|
|
ret = CryptDecodeObjectEx(3, X509_INTEGER, &ints[0].encoded,
|
|
ints[0].encoded[1] + 2, 0, NULL, NULL, NULL);
|
|
*/
|
|
/* check bogus encoding */
|
|
ret = CryptDecodeObjectEx(3, X509_INTEGER, (BYTE *)&ints[0].encoded,
|
|
ints[0].encoded[1] + 2, 0, NULL, NULL, &bufSize);
|
|
ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
|
|
"Expected ERROR_FILE_NOT_FOUND, got %ld\n", GetLastError());
|
|
/* check with NULL integer buffer. Windows XP returns an apparently random
|
|
* error code (0x01c567df).
|
|
*/
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_INTEGER, NULL, 0, 0,
|
|
NULL, NULL, &bufSize);
|
|
ok(!ret, "Expected failure, got success\n");
|
|
/* check with a valid, but too large, integer */
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_INTEGER, bigInt, bigInt[1] + 2, CRYPT_ENCODE_ALLOC_FLAG, NULL,
|
|
(BYTE *)&buf, &bufSize);
|
|
ok(!ret && GetLastError() == CRYPT_E_ASN1_LARGE,
|
|
"Expected CRYPT_E_ASN1_LARGE, got %ld\n", GetLastError());
|
|
/* check with a DER-encoded string */
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_INTEGER, testStr, testStr[1] + 2, CRYPT_ENCODE_ALLOC_FLAG, NULL,
|
|
(BYTE *)&buf, &bufSize);
|
|
ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG,
|
|
"Expected CRYPT_E_ASN1_BADTAG, got %ld\n", GetLastError());
|
|
for (i = 0; i < sizeof(ints) / sizeof(ints[0]); i++)
|
|
{
|
|
/* When the output buffer is NULL, this always succeeds */
|
|
SetLastError(0xdeadbeef);
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_INTEGER,
|
|
(BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2, 0, NULL, NULL,
|
|
&bufSize);
|
|
ok(ret && GetLastError() == NOERROR,
|
|
"Expected success and NOERROR, got %ld\n", GetLastError());
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
X509_INTEGER, (BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2,
|
|
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
|
|
ok(ret, "CryptDecodeObjectEx failed: %ld\n", GetLastError());
|
|
ok(bufSize == sizeof(int), "Expected size %d, got %ld\n", sizeof(int),
|
|
bufSize);
|
|
ok(buf != NULL, "Expected allocated buffer\n");
|
|
if (buf)
|
|
{
|
|
ok(!memcmp(buf, &ints[i].val, bufSize), "Expected %d, got %d\n",
|
|
ints[i].val, *(int *)buf);
|
|
LocalFree(buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct encodedFiletime
|
|
{
|
|
SYSTEMTIME sysTime;
|
|
BYTE *encodedTime;
|
|
};
|
|
|
|
static void testTimeEncoding(LPCSTR encoding,
|
|
const struct encodedFiletime *time)
|
|
{
|
|
FILETIME ft = { 0 };
|
|
BYTE *buf = NULL;
|
|
DWORD bufSize = 0;
|
|
BOOL ret;
|
|
|
|
ret = SystemTimeToFileTime(&time->sysTime, &ft);
|
|
ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
|
|
/* No test case, but both X509_ASN_ENCODING and PKCS_7_ASN_ENCODING have
|
|
* the same effect for time encodings.
|
|
*/
|
|
ret = CryptEncodeObjectEx(X509_ASN_ENCODING, encoding, &ft,
|
|
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
|
|
/* years other than 1950-2050 are not allowed for encodings other than
|
|
* X509_CHOICE_OF_TIME.
|
|
*/
|
|
if (encoding == X509_CHOICE_OF_TIME ||
|
|
(time->sysTime.wYear >= 1950 && time->sysTime.wYear <= 2050))
|
|
{
|
|
ok(ret, "CryptEncodeObjectEx failed: %ld (0x%08lx)\n", GetLastError(),
|
|
GetLastError());
|
|
ok(buf != NULL, "Expected an allocated buffer\n");
|
|
if (buf)
|
|
{
|
|
ok(buf[0] == time->encodedTime[0],
|
|
"Expected type 0x%02x, got 0x%02x\n", time->encodedTime[0],
|
|
buf[0]);
|
|
ok(buf[1] == time->encodedTime[1], "Expected %d bytes, got %ld\n",
|
|
time->encodedTime[1], bufSize);
|
|
ok(!memcmp(time->encodedTime + 2, buf + 2, time->encodedTime[1]),
|
|
"Got unexpected value for time encoding\n");
|
|
LocalFree(buf);
|
|
}
|
|
}
|
|
else
|
|
ok(!ret && GetLastError() == CRYPT_E_BAD_ENCODE,
|
|
"Expected CRYPT_E_BAD_ENCODE, got 0x%08lx\n", GetLastError());
|
|
}
|
|
|
|
static void testTimeDecoding(LPCSTR encoding,
|
|
const struct encodedFiletime *time)
|
|
{
|
|
FILETIME ft1 = { 0 }, ft2 = { 0 };
|
|
DWORD size = sizeof(ft2);
|
|
BOOL ret;
|
|
|
|
ret = SystemTimeToFileTime(&time->sysTime, &ft1);
|
|
ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
|
|
/* No test case, but both X509_ASN_ENCODING and PKCS_7_ASN_ENCODING have
|
|
* the same effect for time encodings.
|
|
*/
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, encoding, time->encodedTime,
|
|
time->encodedTime[1] + 2, 0, NULL, &ft2, &size);
|
|
/* years other than 1950-2050 are not allowed for encodings other than
|
|
* X509_CHOICE_OF_TIME.
|
|
*/
|
|
if (encoding == X509_CHOICE_OF_TIME ||
|
|
(time->sysTime.wYear >= 1950 && time->sysTime.wYear <= 2050))
|
|
{
|
|
ok(ret, "CryptDecodeObjectEx failed: %ld (0x%08lx)\n", GetLastError(),
|
|
GetLastError());
|
|
ok(!memcmp(&ft1, &ft2, sizeof(ft1)),
|
|
"Got unexpected value for time decoding\n");
|
|
}
|
|
else
|
|
ok(!ret && GetLastError() == CRYPT_E_ASN1_BADTAG,
|
|
"Expected CRYPT_E_ASN1_BADTAG, got 0x%08lx\n", GetLastError());
|
|
}
|
|
|
|
static const struct encodedFiletime times[] = {
|
|
{ { 2005, 6, 1, 6, 16, 10, 0, 0 }, "\x17" "\x0d" "050606161000Z" },
|
|
{ { 1945, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x0f" "19450606161000Z" },
|
|
{ { 2145, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x0f" "21450606161000Z" },
|
|
};
|
|
|
|
static void test_encodeFiletime(void)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < sizeof(times) / sizeof(times[0]); i++)
|
|
{
|
|
testTimeEncoding(X509_CHOICE_OF_TIME, ×[i]);
|
|
testTimeEncoding(PKCS_UTC_TIME, ×[i]);
|
|
testTimeEncoding(szOID_RSA_signingTime, ×[i]);
|
|
}
|
|
}
|
|
|
|
static void test_decodeFiletime(void)
|
|
{
|
|
static const struct encodedFiletime otherTimes[] = {
|
|
{ { 1945, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x13" "19450606161000.000Z" },
|
|
{ { 1945, 6, 1, 6, 16, 10, 0, 999 }, "\x18" "\x13" "19450606161000.999Z" },
|
|
{ { 1945, 6, 1, 6, 17, 10, 0, 0 }, "\x18" "\x13" "19450606161000+0100" },
|
|
{ { 1945, 6, 1, 6, 15, 10, 0, 0 }, "\x18" "\x13" "19450606161000-0100" },
|
|
{ { 1945, 6, 1, 6, 14, 55, 0, 0 }, "\x18" "\x13" "19450606161000-0115" },
|
|
{ { 2145, 6, 1, 6, 16, 0, 0, 0 }, "\x18" "\x0a" "2145060616" },
|
|
{ { 2045, 6, 1, 6, 16, 10, 0, 0 }, "\x17" "\x0a" "4506061610" },
|
|
};
|
|
/* An oddball case that succeeds in Windows, but doesn't seem correct
|
|
{ { 2145, 6, 1, 2, 11, 31, 0, 0 }, "\x18" "\x13" "21450606161000-9999" },
|
|
*/
|
|
static const char *bogusTimes[] = {
|
|
/* oddly, this succeeds on Windows, with year 2765
|
|
"\x18" "\x0f" "21r50606161000Z",
|
|
*/
|
|
"\x17" "\x08" "45060616",
|
|
"\x18" "\x0f" "aaaaaaaaaaaaaaZ",
|
|
"\x18" "\x04" "2145",
|
|
"\x18" "\x08" "21450606",
|
|
};
|
|
DWORD i, size;
|
|
FILETIME ft1 = { 0 }, ft2 = { 0 };
|
|
BOOL ret;
|
|
|
|
/* Check bogus length with non-NULL buffer */
|
|
ret = SystemTimeToFileTime(×[0].sysTime, &ft1);
|
|
ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
|
|
size = 1;
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
|
|
times[0].encodedTime, times[0].encodedTime[1] + 2, 0, NULL, &ft2, &size);
|
|
ok(!ret && GetLastError() == ERROR_MORE_DATA,
|
|
"Expected ERROR_MORE_DATA, got %ld\n", GetLastError());
|
|
/* Normal tests */
|
|
for (i = 0; i < sizeof(times) / sizeof(times[0]); i++)
|
|
{
|
|
testTimeDecoding(X509_CHOICE_OF_TIME, ×[i]);
|
|
testTimeDecoding(PKCS_UTC_TIME, ×[i]);
|
|
testTimeDecoding(szOID_RSA_signingTime, ×[i]);
|
|
}
|
|
for (i = 0; i < sizeof(otherTimes) / sizeof(otherTimes[0]); i++)
|
|
{
|
|
testTimeDecoding(X509_CHOICE_OF_TIME, &otherTimes[i]);
|
|
testTimeDecoding(PKCS_UTC_TIME, &otherTimes[i]);
|
|
testTimeDecoding(szOID_RSA_signingTime, &otherTimes[i]);
|
|
}
|
|
for (i = 0; i < sizeof(bogusTimes) / sizeof(bogusTimes[0]); i++)
|
|
{
|
|
size = sizeof(ft1);
|
|
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
|
|
bogusTimes[i], bogusTimes[i][1] + 2, 0, NULL, &ft1, &size);
|
|
ok(!ret && GetLastError() == CRYPT_E_ASN1_CORRUPT,
|
|
"Expected CRYPT_E_ASN1_CORRUPT, got %08lx\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
static void test_registerOIDFunction(void)
|
|
{
|
|
static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
|
|
BOOL ret;
|
|
|
|
/* oddly, this succeeds under WinXP; the function name key is merely
|
|
* omitted. This may be a side effect of the registry code, I don't know.
|
|
* I don't check it because I doubt anyone would depend on it.
|
|
ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, NULL,
|
|
"1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
|
|
*/
|
|
/* On windows XP, GetLastError is incorrectly being set with an HRESULT,
|
|
* HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)
|
|
*/
|
|
ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "foo", NULL, bogusDll,
|
|
NULL);
|
|
ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() ==
|
|
HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)),
|
|
"Expected ERROR_INVALID_PARAMETER: %ld\n", GetLastError());
|
|
/* This has no effect, but "succeeds" on XP */
|
|
ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "foo",
|
|
"1.2.3.4.5.6.7.8.9.10", NULL, NULL);
|
|
ok(ret, "Expected pseudo-success, got %ld\n", GetLastError());
|
|
ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "CryptDllEncodeObject",
|
|
"1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
|
|
ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
|
|
ret = CryptUnregisterOIDFunction(X509_ASN_ENCODING, "CryptDllEncodeObject",
|
|
"1.2.3.4.5.6.7.8.9.10");
|
|
ok(ret, "CryptUnregisterOIDFunction failed: %ld\n", GetLastError());
|
|
ret = CryptRegisterOIDFunction(X509_ASN_ENCODING, "bogus",
|
|
"1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
|
|
ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
|
|
ret = CryptUnregisterOIDFunction(X509_ASN_ENCODING, "bogus",
|
|
"1.2.3.4.5.6.7.8.9.10");
|
|
ok(ret, "CryptUnregisterOIDFunction failed: %ld\n", GetLastError());
|
|
/* This has no effect */
|
|
ret = CryptRegisterOIDFunction(PKCS_7_ASN_ENCODING, "CryptDllEncodeObject",
|
|
"1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
|
|
ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
|
|
/* Check with bogus encoding type: */
|
|
ret = CryptRegisterOIDFunction(0, "CryptDllEncodeObject",
|
|
"1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
|
|
ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
|
|
/* This is written with value 3 verbatim. Thus, the encoding type isn't
|
|
* (for now) treated as a mask.
|
|
*/
|
|
ret = CryptRegisterOIDFunction(3, "CryptDllEncodeObject",
|
|
"1.2.3.4.5.6.7.8.9.10", bogusDll, NULL);
|
|
ok(ret, "CryptRegisterOIDFunction failed: %ld\n", GetLastError());
|
|
ret = CryptUnregisterOIDFunction(3, "CryptDllEncodeObject",
|
|
"1.2.3.4.5.6.7.8.9.10");
|
|
ok(ret, "CryptUnregisterOIDFunction failed: %ld\n", GetLastError());
|
|
}
|
|
|
|
START_TEST(encode)
|
|
{
|
|
test_encodeint();
|
|
test_decodeint();
|
|
test_encodeFiletime();
|
|
test_decodeFiletime();
|
|
test_registerOIDFunction();
|
|
}
|