Implement asn.1 encoding/decoding of times, with tests.
This commit is contained in:
parent
926e35532c
commit
cdc6772017
|
@ -25,6 +25,10 @@
|
||||||
#include "snmp.h"
|
#include "snmp.h"
|
||||||
#include "wine/debug.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);
|
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
|
||||||
|
|
||||||
static const WCHAR szDllName[] = { 'D','l','l',0 };
|
static const WCHAR szDllName[] = { 'D','l','l',0 };
|
||||||
|
@ -313,6 +317,29 @@ BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
|
||||||
return ret;
|
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,
|
static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
|
||||||
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
|
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
|
||||||
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
|
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
|
||||||
|
@ -356,27 +383,11 @@ static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
|
||||||
bytesNeeded = 2 + significantBytes;
|
bytesNeeded = 2 + significantBytes;
|
||||||
if (pad)
|
if (pad)
|
||||||
bytesNeeded++;
|
bytesNeeded++;
|
||||||
if (!pbEncoded || bytesNeeded > *pcbEncoded)
|
if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded,
|
||||||
{
|
bytesNeeded))
|
||||||
*pcbEncoded = bytesNeeded;
|
|
||||||
SetLastError(ERROR_MORE_DATA);
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
|
||||||
if (!pbEncoded)
|
|
||||||
{
|
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
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 = *(BYTE **)pbEncoded;
|
||||||
}
|
|
||||||
*pbEncoded++ = ASN_INTEGER;
|
*pbEncoded++ = ASN_INTEGER;
|
||||||
if (pad)
|
if (pad)
|
||||||
{
|
{
|
||||||
|
@ -390,6 +401,81 @@ static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
|
||||||
return TRUE;
|
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 *,
|
typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *,
|
||||||
DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *);
|
DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *);
|
||||||
|
|
||||||
|
@ -411,23 +497,32 @@ BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
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 (!HIWORD(lpszStructType))
|
||||||
{
|
{
|
||||||
if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING
|
switch (LOWORD(lpszStructType))
|
||||||
|| (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) ==
|
|
||||||
PKCS_7_ASN_ENCODING)
|
|
||||||
{
|
{
|
||||||
switch (LOWORD(lpszStructType))
|
case (WORD)X509_INTEGER:
|
||||||
{
|
encodeFunc = CRYPT_AsnEncodeInt;
|
||||||
case (WORD)X509_INTEGER:
|
break;
|
||||||
encodeFunc = CRYPT_AsnEncodeInt;
|
case (WORD)X509_CHOICE_OF_TIME:
|
||||||
break;
|
encodeFunc = CRYPT_AsnEncodeChoiceOfTime;
|
||||||
default:
|
break;
|
||||||
FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
|
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)
|
if (!encodeFunc)
|
||||||
encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
|
encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
|
||||||
lpszStructType, "CryptEncodeObjectEx", &lib);
|
lpszStructType, "CryptEncodeObjectEx", &lib);
|
||||||
|
@ -482,12 +577,33 @@ BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
|
||||||
return ret;
|
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,
|
static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
|
||||||
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
|
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
|
||||||
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
|
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
|
||||||
{
|
{
|
||||||
int val, i;
|
int val, i;
|
||||||
BOOL ret;
|
|
||||||
|
|
||||||
if (!pbEncoded || !cbEncoded)
|
if (!pbEncoded || !cbEncoded)
|
||||||
{
|
{
|
||||||
|
@ -527,30 +643,192 @@ static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
|
||||||
val <<= 8;
|
val <<= 8;
|
||||||
val |= pbEncoded[2 + i];
|
val |= pbEncoded[2 + i];
|
||||||
}
|
}
|
||||||
|
if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
|
||||||
|
pcbStructInfo, sizeof(int)))
|
||||||
|
return FALSE;
|
||||||
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
|
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)
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
*(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(sizeof(int));
|
return FALSE;
|
||||||
else
|
|
||||||
*(BYTE **)pvStructInfo = LocalAlloc(0, sizeof(int));
|
|
||||||
if (!*(BYTE **)pvStructInfo)
|
|
||||||
return FALSE;
|
|
||||||
memcpy(*(BYTE **)pvStructInfo, &val, sizeof(int));
|
|
||||||
ret = TRUE;
|
|
||||||
}
|
}
|
||||||
else if (*pcbStructInfo < sizeof(int))
|
if (!pvStructInfo)
|
||||||
{
|
{
|
||||||
*pcbStructInfo = sizeof(int);
|
*pcbStructInfo = sizeof(FILETIME);
|
||||||
SetLastError(ERROR_MORE_DATA);
|
SetLastError(ERROR_MORE_DATA);
|
||||||
ret = FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
else
|
if (pbEncoded[0] != ASN_UTCTIME)
|
||||||
{
|
{
|
||||||
*pcbStructInfo = sizeof(int);
|
SetLastError(CRYPT_E_ASN1_BADTAG);
|
||||||
memcpy(pvStructInfo, &val, sizeof(int));
|
return FALSE;
|
||||||
ret = TRUE;
|
|
||||||
}
|
}
|
||||||
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 *,
|
typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *,
|
||||||
|
@ -574,23 +852,32 @@ BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
|
||||||
SetLastError(ERROR_INVALID_PARAMETER);
|
SetLastError(ERROR_INVALID_PARAMETER);
|
||||||
return FALSE;
|
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 (!HIWORD(lpszStructType))
|
||||||
{
|
{
|
||||||
if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) == X509_ASN_ENCODING
|
switch (LOWORD(lpszStructType))
|
||||||
|| (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) ==
|
|
||||||
PKCS_7_ASN_ENCODING)
|
|
||||||
{
|
{
|
||||||
switch (LOWORD(lpszStructType))
|
case (WORD)X509_INTEGER:
|
||||||
{
|
decodeFunc = CRYPT_AsnDecodeInt;
|
||||||
case (WORD)X509_INTEGER:
|
break;
|
||||||
decodeFunc = CRYPT_AsnDecodeInt;
|
case (WORD)X509_CHOICE_OF_TIME:
|
||||||
break;
|
decodeFunc = CRYPT_AsnDecodeChoiceOfTime;
|
||||||
default:
|
break;
|
||||||
FIXME("%d: unimplemented\n", LOWORD(lpszStructType));
|
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)
|
if (!decodeFunc)
|
||||||
decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
|
decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType,
|
||||||
lpszStructType, "CryptDecodeObjectEx", &lib);
|
lpszStructType, "CryptDecodeObjectEx", &lib);
|
||||||
|
|
|
@ -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
|
* 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 void test_registerOIDFunction(void)
|
||||||
{
|
{
|
||||||
static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
|
static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
|
||||||
|
@ -202,5 +354,7 @@ START_TEST(encode)
|
||||||
{
|
{
|
||||||
test_encodeint();
|
test_encodeint();
|
||||||
test_decodeint();
|
test_decodeint();
|
||||||
|
test_encodeFiletime();
|
||||||
|
test_decodeFiletime();
|
||||||
test_registerOIDFunction();
|
test_registerOIDFunction();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue