diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c index 2332e6573e5..85b1ecb0cea 100644 --- a/dlls/crypt32/encode.c +++ b/dlls/crypt32/encode.c @@ -25,6 +25,10 @@ #include "snmp.h" #include "wine/debug.h" +/* a few asn.1 tags we need */ +#define ASN_UTCTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17) +#define ASN_GENERALTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18) + WINE_DEFAULT_DEBUG_CHANNEL(crypt); static const WCHAR szDllName[] = { 'D','l','l',0 }; @@ -313,6 +317,29 @@ BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, return ret; } +static BOOL CRYPT_EncodeEnsureSpace(DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded, + DWORD bytesNeeded) +{ + if (!pbEncoded || (!(dwFlags & CRYPT_ENCODE_ALLOC_FLAG) && + bytesNeeded > *pcbEncoded)) + { + *pcbEncoded = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + { + if (pEncodePara && pEncodePara->pfnAlloc) + *(BYTE **)pbEncoded = pEncodePara->pfnAlloc(bytesNeeded); + else + *(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded); + if (!*(BYTE **)pbEncoded) + return FALSE; + } + return TRUE; +} + static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType, LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) @@ -356,27 +383,11 @@ static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType, bytesNeeded = 2 + significantBytes; if (pad) bytesNeeded++; - if (!pbEncoded || bytesNeeded > *pcbEncoded) - { - *pcbEncoded = bytesNeeded; - SetLastError(ERROR_MORE_DATA); + if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded, + bytesNeeded)) return FALSE; - } - if (!pbEncoded) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) - { - if (pEncodePara && pEncodePara->pfnAlloc) - *(BYTE **)pbEncoded = pEncodePara->pfnAlloc(bytesNeeded); - else - *(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded); - if (!*(BYTE **)pbEncoded) - return FALSE; pbEncoded = *(BYTE **)pbEncoded; - } *pbEncoded++ = ASN_INTEGER; if (pad) { @@ -390,6 +401,81 @@ static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType, return TRUE; } +static BOOL WINAPI CRYPT_AsnEncodeUtcTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + SYSTEMTIME sysTime; + /* sorry, magic number: enough for tag, len, YYMMDDHHMMSSZ\0. I use a + * temporary buffer because the output buffer is not NULL-terminated. + */ + char buf[16]; + static const DWORD bytesNeeded = sizeof(buf) - 1; + + if (!pvStructInfo) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + /* Sanity check the year, this is a two-digit year format */ + if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime)) + return FALSE; + if (sysTime.wYear < 1950 || sysTime.wYear > 2050) + { + SetLastError(CRYPT_E_BAD_ENCODE); + return FALSE; + } + if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded, + bytesNeeded)) + return FALSE; + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + buf[0] = ASN_UTCTIME; + buf[1] = bytesNeeded - 2; + snprintf(buf + 2, sizeof(buf) - 2, "%02d%02d%02d%02d%02d%02dZ", + sysTime.wYear >= 2000 ? sysTime.wYear - 2000 : sysTime.wYear - 1900, + sysTime.wDay, sysTime.wMonth, sysTime.wHour, sysTime.wMinute, + sysTime.wSecond); + memcpy(pbEncoded, buf, bytesNeeded); + return TRUE; +} + +static BOOL WINAPI CRYPT_AsnEncodeChoiceOfTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + SYSTEMTIME sysTime; + /* sorry, magic number: enough for tag, len, YYYYMMDDHHMMSSZ\0. I use a + * temporary buffer because the output buffer is not NULL-terminated. + */ + char buf[18]; + static const DWORD bytesNeeded = sizeof(buf) - 1; + + if (!pvStructInfo) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + /* Check the year, if it's in the UTCTime range call that encode func */ + if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime)) + return FALSE; + if (sysTime.wYear >= 1950 && sysTime.wYear <= 2050) + return CRYPT_AsnEncodeUtcTime(dwCertEncodingType, lpszStructType, + pvStructInfo, dwFlags, pEncodePara, pbEncoded, pcbEncoded); + if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded, + bytesNeeded)) + return FALSE; + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + buf[0] = ASN_GENERALTIME; + buf[1] = bytesNeeded - 2; + snprintf(buf + 2, sizeof(buf) - 2, "%04d%02d%02d%02d%02d%02dZ", + sysTime.wYear, sysTime.wDay, sysTime.wMonth, sysTime.wHour, + sysTime.wMinute, sysTime.wSecond); + memcpy(pbEncoded, buf, bytesNeeded); + return TRUE; +} + typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *, DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *); @@ -411,23 +497,32 @@ BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING + && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } if (!HIWORD(lpszStructType)) { - if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING - || (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) == - PKCS_7_ASN_ENCODING) + switch (LOWORD(lpszStructType)) { - switch (LOWORD(lpszStructType)) - { - case (WORD)X509_INTEGER: - encodeFunc = CRYPT_AsnEncodeInt; - break; - default: - FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); - } + case (WORD)X509_INTEGER: + encodeFunc = CRYPT_AsnEncodeInt; + break; + case (WORD)X509_CHOICE_OF_TIME: + encodeFunc = CRYPT_AsnEncodeChoiceOfTime; + break; + case (WORD)PKCS_UTC_TIME: + encodeFunc = CRYPT_AsnEncodeUtcTime; + break; + default: + FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); } } + else if (!strcmp(lpszStructType, szOID_RSA_signingTime)) + encodeFunc = CRYPT_AsnEncodeUtcTime; if (!encodeFunc) encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType, lpszStructType, "CryptEncodeObjectEx", &lib); @@ -482,12 +577,33 @@ BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, return ret; } +static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD bytesNeeded) +{ + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + { + if (pDecodePara && pDecodePara->pfnAlloc) + *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(bytesNeeded); + else + *(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded); + if (!*(BYTE **)pvStructInfo) + return FALSE; + } + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + return TRUE; +} + static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) { int val, i; - BOOL ret; if (!pbEncoded || !cbEncoded) { @@ -527,30 +643,192 @@ static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType, val <<= 8; val |= pbEncoded[2 + i]; } + if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo, sizeof(int))) + return FALSE; if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + *pcbStructInfo = sizeof(int); + memcpy(pvStructInfo, &val, sizeof(int)); + return TRUE; +} + +#define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \ + do { \ + BYTE i; \ + \ + (word) = 0; \ + for (i = 0; (len) > 0 && i < (numDigits); i++, (len)--) \ + { \ + if (!isdigit(*(pbEncoded))) \ + { \ + SetLastError(CRYPT_E_ASN1_CORRUPT); \ + return FALSE; \ + } \ + (word) *= 10; \ + (word) += *(pbEncoded)++ - '0'; \ + } \ + } while (0) + +static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + SYSTEMTIME sysTime = { 0 }; + BYTE len; + + if (!pbEncoded || !cbEncoded) { - if (pDecodePara && pDecodePara->pfnAlloc) - *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(sizeof(int)); - else - *(BYTE **)pvStructInfo = LocalAlloc(0, sizeof(int)); - if (!*(BYTE **)pvStructInfo) - return FALSE; - memcpy(*(BYTE **)pvStructInfo, &val, sizeof(int)); - ret = TRUE; + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; } - else if (*pcbStructInfo < sizeof(int)) + if (!pvStructInfo) { - *pcbStructInfo = sizeof(int); + *pcbStructInfo = sizeof(FILETIME); SetLastError(ERROR_MORE_DATA); - ret = FALSE; + return FALSE; } - else + if (pbEncoded[0] != ASN_UTCTIME) { - *pcbStructInfo = sizeof(int); - memcpy(pvStructInfo, &val, sizeof(int)); - ret = TRUE; + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; } - return ret; + len = pbEncoded[1]; + /* FIXME: magic # */ + if (len < 10) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + pbEncoded += 2; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wYear); + if (sysTime.wYear >= 50) + sysTime.wYear += 1900; + else + sysTime.wYear += 2000; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute); + if (len > 0) + { + if (len >= 2 && isdigit(*pbEncoded) && isdigit(*(pbEncoded + 1))) + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wSecond); + else if (isdigit(*pbEncoded)) + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 1, sysTime.wSecond); + if (len > 0) + { + /* FIXME: get timezone, for now assuming UTC (no adjustment) */ + } + } + + if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo, sizeof(FILETIME))) + return FALSE; + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + *pcbStructInfo = sizeof(FILETIME); + return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo); +} + +static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + SYSTEMTIME sysTime = { 0 }; + BYTE len; + + if (!pbEncoded || !cbEncoded) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if (!pvStructInfo) + { + *pcbStructInfo = sizeof(FILETIME); + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + if (pbEncoded[0] == ASN_UTCTIME) + return CRYPT_AsnDecodeUtcTime(dwCertEncodingType, lpszStructType, + pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo); + if (pbEncoded[0] != ASN_GENERALTIME) + { + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + len = pbEncoded[1]; + /* FIXME: magic # */ + if (len < 10) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + pbEncoded += 2; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 4, sysTime.wYear); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay); + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour); + if (len > 0) + { + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute); + if (len > 0) + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wSecond); + if (len > 0 && (*pbEncoded == '.' || *pbEncoded == ',')) + { + BYTE digits; + + pbEncoded++; + len--; + digits = min(len, 3); /* workaround macro weirdness */ + CRYPT_TIME_GET_DIGITS(pbEncoded, len, digits, + sysTime.wMilliseconds); + } + if (len >= 5 && (*pbEncoded == '+' || *pbEncoded == '-')) + { + WORD hours, minutes; + BYTE sign = *pbEncoded++; + + len--; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours); + if (hours >= 24) + return CRYPT_E_ASN1_CORRUPT; + CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes); + if (minutes >= 60) + return CRYPT_E_ASN1_CORRUPT; + if (sign == '+') + { + sysTime.wHour += hours; + sysTime.wMinute += minutes; + } + else + { + if (hours > sysTime.wHour) + { + sysTime.wDay--; + sysTime.wHour = 24 - (hours - sysTime.wHour); + } + else + sysTime.wHour -= hours; + if (minutes > sysTime.wMinute) + { + sysTime.wHour--; + sysTime.wMinute = 60 - (minutes - sysTime.wMinute); + } + else + sysTime.wMinute -= minutes; + } + } + } + + if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo, sizeof(FILETIME))) + return FALSE; + if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) + pvStructInfo = *(BYTE **)pvStructInfo; + *pcbStructInfo = sizeof(FILETIME); + return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo); } typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *, @@ -574,23 +852,32 @@ BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING + && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING) + { + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; + } if (!HIWORD(lpszStructType)) { - if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING - || (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) == - PKCS_7_ASN_ENCODING) + switch (LOWORD(lpszStructType)) { - switch (LOWORD(lpszStructType)) - { - case (WORD)X509_INTEGER: - decodeFunc = CRYPT_AsnDecodeInt; - break; - default: - FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); - } + case (WORD)X509_INTEGER: + decodeFunc = CRYPT_AsnDecodeInt; + break; + case (WORD)X509_CHOICE_OF_TIME: + decodeFunc = CRYPT_AsnDecodeChoiceOfTime; + break; + case (WORD)PKCS_UTC_TIME: + decodeFunc = CRYPT_AsnDecodeUtcTime; + break; + default: + FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); } } + else if (!strcmp(lpszStructType, szOID_RSA_signingTime)) + decodeFunc = CRYPT_AsnDecodeUtcTime; if (!decodeFunc) decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType, lpszStructType, "CryptDecodeObjectEx", &lib); diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c index df789862dd5..bb883bbf661 100644 --- a/dlls/crypt32/tests/encode.c +++ b/dlls/crypt32/tests/encode.c @@ -1,5 +1,5 @@ /* - * Unit test suite for crypt32.dll's CryptEncodeObjectEx + * Unit test suite for crypt32.dll's CryptEncodeObjectEx/CryptDecodeObjectEx * * Copyright 2005 Juan Lang * @@ -144,6 +144,158 @@ static void test_decodeint(void) } } +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; + + 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++) + { + FILETIME ft; + SYSTEMTIME sysTime; + DWORD size = sizeof(ft); + BOOL ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME, + bogusTimes[i], bogusTimes[i][1] + 2, 0, NULL, &ft, &size); + + if (ret) + { + ret = FileTimeToSystemTime(&ft, &sysTime); + printf("%02d %02d %04d %02d:%02d.%02d\n", sysTime.wMonth, + sysTime.wDay, sysTime.wYear, sysTime.wHour, sysTime.wMinute, + sysTime.wSecond); + } + 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 }; @@ -202,5 +354,7 @@ START_TEST(encode) { test_encodeint(); test_decodeint(); + test_encodeFiletime(); + test_decodeFiletime(); test_registerOIDFunction(); }