Commit 4d7e1e4b authored by Juan Lang's avatar Juan Lang Committed by Alexandre Julliard

- encode/decode X509_NAMEs for simple string encodings, with tests

- more tests and some fixes to other types
parent 112df735
......@@ -18,6 +18,7 @@
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
#include "wincrypt.h"
......@@ -26,6 +27,10 @@
#include "wine/debug.h"
/* a few asn.1 tags we need */
#define ASN_SETOF (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x11)
#define ASN_NUMERICSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x12)
#define ASN_PRINTABLESTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x13)
#define ASN_IA5STRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x16)
#define ASN_UTCTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17)
#define ASN_GENERALTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18)
......@@ -336,6 +341,8 @@ static BOOL CRYPT_EncodeEnsureSpace(DWORD dwFlags,
*(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded);
if (!*(BYTE **)pbEncoded)
ret = FALSE;
else
*pcbEncoded = bytesNeeded;
}
else if (bytesNeeded > *pcbEncoded)
{
......@@ -346,6 +353,358 @@ static BOOL CRYPT_EncodeEnsureSpace(DWORD dwFlags,
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeOid(DWORD dwCertEncodingType,
LPCSTR pszObjId, BYTE *pbEncoded, DWORD *pcbEncoded)
{
DWORD bytesNeeded = 2;
BOOL ret = TRUE;
int firstPos = 0;
BYTE firstByte = 0;
if (pszObjId)
{
const char *ptr;
int val1, val2;
if (sscanf(pszObjId, "%d.%d.%n", &val1, &val2, &firstPos) != 2)
{
SetLastError(CRYPT_E_ASN1_ERROR);
return FALSE;
}
bytesNeeded++;
firstByte = val1 * 40 + val2;
ptr = pszObjId + firstPos;
while (ret && *ptr)
{
int pos;
/* note I assume each component is at most 32-bits long in base 2 */
if (sscanf(ptr, "%d%n", &val1, &pos) == 1)
{
if (val1 >= 0x10000000)
bytesNeeded += 5;
else if (val1 >= 0x200000)
bytesNeeded += 4;
else if (val1 >= 0x4000)
bytesNeeded += 3;
else if (val1 >= 0x80)
bytesNeeded += 2;
else
bytesNeeded += 1;
ptr += pos;
if (*ptr == '.')
ptr++;
}
else
{
SetLastError(CRYPT_E_ASN1_ERROR);
return FALSE;
}
}
}
if (pbEncoded)
{
if (*pbEncoded < bytesNeeded)
{
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
*pbEncoded++ = ASN_OBJECTIDENTIFIER;
*pbEncoded++ = bytesNeeded - 2;
if (pszObjId)
{
const char *ptr;
int val, pos;
*pbEncoded++ = firstByte;
ptr = pszObjId + firstPos;
while (ret && *ptr)
{
sscanf(ptr, "%d%n", &val, &pos);
{
unsigned char outBytes[5];
int numBytes, i;
if (val >= 0x10000000)
numBytes = 5;
else if (val >= 0x200000)
numBytes = 4;
else if (val >= 0x4000)
numBytes = 3;
else if (val >= 0x80)
numBytes = 2;
else
numBytes = 1;
for (i = numBytes; i > 0; i--)
{
outBytes[i - 1] = val & 0x7f;
val >>= 7;
}
for (i = 0; i < numBytes - 1; i++)
*pbEncoded++ = outBytes[i] | 0x80;
*pbEncoded++ = outBytes[i];
ptr += pos;
if (*ptr == '.')
ptr++;
}
}
}
}
}
*pcbEncoded = bytesNeeded;
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeNameValue(DWORD dwCertEncodingType,
CERT_NAME_VALUE *value, BYTE *pbEncoded, DWORD *pcbEncoded)
{
BYTE tag;
DWORD bytesNeeded;
BOOL ret = TRUE;
switch (value->dwValueType)
{
case CERT_RDN_NUMERIC_STRING:
tag = ASN_NUMERICSTRING;
bytesNeeded = 2 + value->Value.cbData;
break;
case CERT_RDN_PRINTABLE_STRING:
tag = ASN_PRINTABLESTRING;
bytesNeeded = 2 + value->Value.cbData;
break;
case CERT_RDN_IA5_STRING:
tag = ASN_IA5STRING;
bytesNeeded = 2 + value->Value.cbData;
break;
case CERT_RDN_ANY_TYPE:
/* explicitly disallowed */
SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
return FALSE;
default:
FIXME("String type %ld unimplemented\n", value->dwValueType);
return FALSE;
}
if (pbEncoded)
{
if (*pcbEncoded < bytesNeeded)
{
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
*pbEncoded++ = tag;
*pbEncoded++ = bytesNeeded - 2;
switch (value->dwValueType)
{
case CERT_RDN_NUMERIC_STRING:
case CERT_RDN_PRINTABLE_STRING:
case CERT_RDN_IA5_STRING:
memcpy(pbEncoded, value->Value.pbData, value->Value.cbData);
}
}
}
*pcbEncoded = bytesNeeded;
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeRdnAttr(DWORD dwCertEncodingType,
CERT_RDN_ATTR *attr, BYTE *pbEncoded, DWORD *pcbEncoded)
{
DWORD bytesNeeded, size;
BOOL ret;
bytesNeeded = 2; /* tag and len */
ret = CRYPT_AsnEncodeOid(dwCertEncodingType, attr->pszObjId, NULL, &size);
if (ret)
{
bytesNeeded += size;
/* hack: a CERT_RDN_ATTR is identical to a CERT_NAME_VALUE beginning
* with dwValueType, so "cast" it to get its encoded size
*/
ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType,
(CERT_NAME_VALUE *)&attr->dwValueType, NULL, &size);
if (ret)
{
bytesNeeded += size;
if (pbEncoded)
{
if (*pcbEncoded < bytesNeeded)
{
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
*pbEncoded++ = ASN_CONSTRUCTOR | ASN_SEQUENCE;
*pbEncoded++ = bytesNeeded - 2;
size = bytesNeeded - 2;
ret = CRYPT_AsnEncodeOid(dwCertEncodingType, attr->pszObjId,
pbEncoded, &size);
if (ret)
{
pbEncoded += size;
size = bytesNeeded - 2 - size;
ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType,
(CERT_NAME_VALUE *)&attr->dwValueType, pbEncoded,
&size);
}
}
}
*pcbEncoded = bytesNeeded;
}
}
return ret;
}
static int BLOBComp(const void *l, const void *r)
{
CRYPT_DER_BLOB *a = (CRYPT_DER_BLOB *)l, *b = (CRYPT_DER_BLOB *)r;
int ret;
if (!(ret = memcmp(a->pbData, b->pbData, min(a->cbData, b->cbData))))
ret = a->cbData - b->cbData;
return ret;
}
/* This encodes as a SET OF, which in DER must be lexicographically sorted.
*/
static BOOL WINAPI CRYPT_AsnEncodeRdn(DWORD dwCertEncodingType, CERT_RDN *rdn,
BYTE *pbEncoded, DWORD *pcbEncoded)
{
DWORD bytesNeeded, i;
BOOL ret;
CRYPT_DER_BLOB *blobs = NULL;
ret = TRUE;
if (rdn->cRDNAttr)
{
if (!rdn->rgRDNAttr)
{
SetLastError(STATUS_ACCESS_VIOLATION);
ret = FALSE;
}
else
{
blobs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
rdn->cRDNAttr * sizeof(CRYPT_DER_BLOB));
if (!blobs)
ret = FALSE;
}
}
bytesNeeded = 2; /* tag and len */
for (i = 0; ret && i < rdn->cRDNAttr; i++)
{
ret = CRYPT_AsnEncodeRdnAttr(dwCertEncodingType, &rdn->rgRDNAttr[i],
NULL, &blobs[i].cbData);
if (ret)
bytesNeeded += blobs[i].cbData;
}
if (ret)
{
if (pbEncoded)
{
if (*pcbEncoded < bytesNeeded)
{
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
for (i = 0; ret && i < rdn->cRDNAttr; i++)
{
blobs[i].pbData = HeapAlloc(GetProcessHeap(), 0,
blobs[i].cbData);
if (!blobs[i].pbData)
ret = FALSE;
else
ret = CRYPT_AsnEncodeRdnAttr(dwCertEncodingType,
&rdn->rgRDNAttr[i], blobs[i].pbData, &blobs[i].cbData);
}
if (ret)
{
qsort(blobs, rdn->cRDNAttr, sizeof(CRYPT_DER_BLOB),
BLOBComp);
*pbEncoded++ = ASN_CONSTRUCTOR | ASN_SETOF;
*pbEncoded++ = (BYTE)bytesNeeded - 2;
for (i = 0; ret && i < rdn->cRDNAttr; i++)
{
memcpy(pbEncoded, blobs[i].pbData, blobs[i].cbData);
pbEncoded += blobs[i].cbData;
}
}
}
}
*pcbEncoded = bytesNeeded;
}
if (blobs)
{
for (i = 0; i < rdn->cRDNAttr; i++)
HeapFree(GetProcessHeap(), 0, blobs[i].pbData);
HeapFree(GetProcessHeap(), 0, blobs);
}
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeName(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
{
CERT_NAME_INFO *info = (CERT_NAME_INFO *)pvStructInfo;
DWORD bytesNeeded, size, i;
BOOL ret;
if (!pvStructInfo)
{
SetLastError(STATUS_ACCESS_VIOLATION);
return FALSE;
}
if (info->cRDN && !info->rgRDN)
{
SetLastError(STATUS_ACCESS_VIOLATION);
return FALSE;
}
TRACE("encoding name with %ld RDNs\n", info->cRDN);
bytesNeeded = 2; /* tag and len */
ret = TRUE;
for (i = 0; ret && i < info->cRDN; i++)
{
ret = CRYPT_AsnEncodeRdn(dwCertEncodingType, &info->rgRDN[i], NULL,
&size);
if (ret)
bytesNeeded += size;
}
if (ret)
{
if (!pbEncoded)
{
*pcbEncoded = bytesNeeded;
return TRUE;
}
if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded,
pcbEncoded, bytesNeeded))
return FALSE;
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
pbEncoded = *(BYTE **)pbEncoded;
*pbEncoded++ = ASN_CONSTRUCTOR | ASN_SEQUENCE;
*pbEncoded++ = (BYTE)bytesNeeded - 2;
for (i = 0; ret && i < info->cRDN; i++)
{
size = bytesNeeded;
ret = CRYPT_AsnEncodeRdn(dwCertEncodingType, &info->rgRDN[i],
pbEncoded, &size);
if (ret)
{
pbEncoded += size;
bytesNeeded -= size;
}
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
......@@ -356,7 +715,7 @@ static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
if (!pvStructInfo)
{
SetLastError(ERROR_INVALID_PARAMETER);
SetLastError(STATUS_ACCESS_VIOLATION);
return FALSE;
}
......@@ -425,7 +784,7 @@ static BOOL WINAPI CRYPT_AsnEncodeUtcTime(DWORD dwCertEncodingType,
if (!pvStructInfo)
{
SetLastError(ERROR_INVALID_PARAMETER);
SetLastError(STATUS_ACCESS_VIOLATION);
return FALSE;
}
/* Sanity check the year, this is a two-digit year format */
......@@ -469,7 +828,7 @@ static BOOL WINAPI CRYPT_AsnEncodeGeneralizedTime(DWORD dwCertEncodingType,
if (!pvStructInfo)
{
SetLastError(ERROR_INVALID_PARAMETER);
SetLastError(STATUS_ACCESS_VIOLATION);
return FALSE;
}
if (!pbEncoded)
......@@ -502,7 +861,7 @@ static BOOL WINAPI CRYPT_AsnEncodeChoiceOfTime(DWORD dwCertEncodingType,
if (!pvStructInfo)
{
SetLastError(ERROR_INVALID_PARAMETER);
SetLastError(STATUS_ACCESS_VIOLATION);
return FALSE;
}
/* Check the year, if it's in the UTCTime range call that encode func */
......@@ -551,6 +910,9 @@ BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
{
switch (LOWORD(lpszStructType))
{
case (WORD)X509_NAME:
encodeFunc = CRYPT_AsnEncodeName;
break;
case (WORD)X509_INTEGER:
encodeFunc = CRYPT_AsnEncodeInt;
break;
......@@ -639,6 +1001,8 @@ static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags,
*(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded);
if (!*(BYTE **)pvStructInfo)
ret = FALSE;
else
*pcbStructInfo = bytesNeeded;
}
else if (*pcbStructInfo < bytesNeeded)
{
......@@ -649,6 +1013,468 @@ static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags,
return ret;
}
/* FIXME: honor the CRYPT_DECODE_SHARE_OID_FLAG. */
static BOOL WINAPI CRYPT_AsnDecodeOid(DWORD dwCertEncodingType,
const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, LPSTR pszObjId,
DWORD *pcbObjId)
{
BOOL ret = TRUE;
DWORD bytesNeeded;
/* cbEncoded is an upper bound on the number of bytes, not the actual
* count: check the count for sanity.
*/
if (cbEncoded <= 1 || pbEncoded[1] > cbEncoded - 2)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
if (pbEncoded[0] != ASN_OBJECTIDENTIFIER)
{
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
if (pbEncoded[1])
{
/* The largest possible string for the first two components is 2.175
* (= 2 * 40 + 175 = 255), so this is big enough.
*/
char firstTwo[6];
const BYTE *ptr;
snprintf(firstTwo, sizeof(firstTwo), "%d.%d", pbEncoded[2] / 40,
pbEncoded[2] - (pbEncoded[2] / 40) * 40);
bytesNeeded = strlen(firstTwo) + 1;
for (ptr = pbEncoded + 3; ret && ptr - pbEncoded - 2 < pbEncoded[1]; )
{
/* large enough for ".4000000" */
char str[9];
int val = 0;
while (ptr - pbEncoded - 2 < pbEncoded[1] && (*ptr & 0x80))
{
val <<= 7;
val |= *ptr & 0x7f;
ptr++;
}
if (ptr - pbEncoded - 2 >= pbEncoded[1] || (*ptr & 0x80))
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
ret = FALSE;
}
else
{
val <<= 7;
val |= *ptr++;
snprintf(str, sizeof(str), ".%d", val);
bytesNeeded += strlen(str);
}
}
if (!pszObjId)
*pcbObjId = bytesNeeded;
else if (*pcbObjId < bytesNeeded)
{
*pcbObjId = bytesNeeded;
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
sprintf(pszObjId, "%d.%d", pbEncoded[2] / 40,
pbEncoded[2] - (pbEncoded[2] / 40) * 40);
pszObjId += strlen(pszObjId);
for (ptr = pbEncoded + 3; ret && ptr - pbEncoded - 2 < pbEncoded[1];
)
{
int val = 0;
while (ptr - pbEncoded - 2 < pbEncoded[1] && (*ptr & 0x80))
{
val <<= 7;
val |= *ptr & 0x7f;
ptr++;
}
val <<= 7;
val |= *ptr++;
sprintf(pszObjId, ".%d", val);
pszObjId += strlen(pszObjId);
}
}
}
else
bytesNeeded = 0;
*pcbObjId = bytesNeeded;
return ret;
}
/* Warning: this assumes the address of value->Value.pbData is already set, in
* order to avoid overwriting memory. (In some cases, it may change it, if it
* doesn't copy anything to memory.) Be sure to set it correctly!
*/
static BOOL WINAPI CRYPT_AsnDecodeNameValue(DWORD dwCertEncodingType,
const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, CERT_NAME_VALUE *value,
DWORD *pcbValue)
{
DWORD bytesNeeded;
BOOL ret = TRUE;
/* cbEncoded is an upper bound on the number of bytes, not the actual
* count: check the count for sanity.
*/
if (cbEncoded <= 1 || pbEncoded[1] > cbEncoded - 2)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
switch (pbEncoded[0])
{
case ASN_NUMERICSTRING:
case ASN_PRINTABLESTRING:
case ASN_IA5STRING:
break;
default:
FIXME("Unimplemented string type %02x\n", pbEncoded[0]);
SetLastError(OSS_UNIMPLEMENTED);
return FALSE;
}
bytesNeeded = sizeof(CERT_NAME_VALUE);
if (pbEncoded[1])
{
switch (pbEncoded[0])
{
case ASN_NUMERICSTRING:
case ASN_PRINTABLESTRING:
case ASN_IA5STRING:
if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
bytesNeeded += pbEncoded[1];
break;
}
}
if (!value)
{
*pcbValue = bytesNeeded;
return TRUE;
}
if (*pcbValue < bytesNeeded)
{
*pcbValue = bytesNeeded;
SetLastError(ERROR_MORE_DATA);
return FALSE;
}
*pcbValue = bytesNeeded;
switch (pbEncoded[0])
{
case ASN_NUMERICSTRING:
value->dwValueType = CERT_RDN_NUMERIC_STRING;
break;
case ASN_PRINTABLESTRING:
value->dwValueType = CERT_RDN_PRINTABLE_STRING;
break;
case ASN_IA5STRING:
value->dwValueType = CERT_RDN_IA5_STRING;
break;
}
if (pbEncoded[1])
{
switch (pbEncoded[0])
{
case ASN_NUMERICSTRING:
case ASN_PRINTABLESTRING:
case ASN_IA5STRING:
value->Value.cbData = pbEncoded[1];
if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
value->Value.pbData = (BYTE *)pbEncoded + 2;
else
{
if (!value->Value.pbData)
{
SetLastError(CRYPT_E_ASN1_INTERNAL);
ret = FALSE;
}
else
memcpy(value->Value.pbData, pbEncoded + 2, pbEncoded[1]);
}
break;
}
}
else
{
value->Value.cbData = 0;
value->Value.pbData = NULL;
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeRdnAttr(DWORD dwCertEncodingType,
const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, CERT_RDN_ATTR *attr,
DWORD *pcbAttr)
{
BOOL ret = TRUE;
DWORD bytesNeeded, size;
/* cbEncoded is an upper bound on the number of bytes, not the actual
* count: check the count for sanity. It must be at least 6, two for the
* tag and length for the RDN_ATTR, two for the OID, and two for the string.
*/
if (cbEncoded < 6 || pbEncoded[1] < 4 || pbEncoded[1] > cbEncoded - 2)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
if (pbEncoded[0] != (ASN_CONSTRUCTOR | ASN_SEQUENCE))
{
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
bytesNeeded = sizeof(CERT_RDN_ATTR);
ret = CRYPT_AsnDecodeOid(dwCertEncodingType, pbEncoded + 2,
cbEncoded - 2, dwFlags, NULL, &size);
if (ret)
{
/* ugly: need to know the size of the next element of the sequence,
* so get it directly
*/
BYTE objIdLen = pbEncoded[3];
bytesNeeded += size;
/* hack: like encoding, this takes advantage of the fact that the rest
* of the structure is identical to a CERT_NAME_VALUE.
*/
ret = CRYPT_AsnDecodeNameValue(dwCertEncodingType, pbEncoded + 4 +
objIdLen, cbEncoded - 4 - objIdLen, dwFlags, NULL, &size);
if (ret)
{
bytesNeeded += size;
if (!attr)
*pcbAttr = bytesNeeded;
else if (*pcbAttr < bytesNeeded)
{
*pcbAttr = bytesNeeded;
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
BYTE *originalData = attr->Value.pbData;
*pcbAttr = bytesNeeded;
/* strange: decode the value first, because it has a counted
* size, and we can store the OID after it. Keep track of the
* original data pointer, we'll need to know whether it was
* changed.
*/
size = bytesNeeded;
ret = CRYPT_AsnDecodeNameValue(dwCertEncodingType,
pbEncoded + 4 + objIdLen, cbEncoded - 4 - objIdLen,
dwFlags, (CERT_NAME_VALUE *)&attr->dwValueType, &size);
if (ret)
{
if (objIdLen)
{
/* if the data were copied to the original location,
* the OID goes after. Otherwise it goes in the
* spot originally reserved for the data.
*/
if (attr->Value.pbData == originalData)
attr->pszObjId = (LPSTR)(attr->Value.pbData +
attr->Value.cbData);
else
attr->pszObjId = originalData;
size = bytesNeeded - size;
ret = CRYPT_AsnDecodeOid(dwCertEncodingType,
pbEncoded + 2, cbEncoded - 2, dwFlags, attr->pszObjId,
&size);
}
else
attr->pszObjId = NULL;
}
}
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeRdn(DWORD dwCertEncodingType,
const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, CERT_RDN *rdn,
DWORD *pcbRdn)
{
BOOL ret = TRUE;
DWORD bytesNeeded, cRDNAttr = 0;
/* cbEncoded is an upper bound on the number of bytes, not the actual
* count: check the count for sanity.
*/
if (cbEncoded <= 1 || pbEncoded[1] > cbEncoded - 2)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
if (pbEncoded[0] != (ASN_CONSTRUCTOR | ASN_SETOF))
{
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
bytesNeeded = sizeof(CERT_RDN);
if (pbEncoded[1])
{
const BYTE *ptr;
DWORD size;
for (ptr = pbEncoded + 2; ret && ptr - pbEncoded - 2 < pbEncoded[1]; )
{
ret = CRYPT_AsnDecodeRdnAttr(dwCertEncodingType, ptr,
cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
if (ret)
{
cRDNAttr++;
bytesNeeded += size;
ptr += ptr[1] + 2;
}
}
}
if (ret)
{
if (!rdn)
{
*pcbRdn = bytesNeeded;
return TRUE;
}
if (*pcbRdn < bytesNeeded)
{
*pcbRdn = bytesNeeded;
SetLastError(ERROR_MORE_DATA);
return FALSE;
}
*pcbRdn = bytesNeeded;
rdn->cRDNAttr = cRDNAttr;
if (rdn->cRDNAttr == 0)
rdn->rgRDNAttr = NULL;
else
{
DWORD size, i;
BYTE *nextData;
const BYTE *ptr;
rdn->rgRDNAttr = (CERT_RDN_ATTR *)((BYTE *)rdn + sizeof(CERT_RDN));
nextData = (BYTE *)rdn->rgRDNAttr +
rdn->cRDNAttr * sizeof(CERT_RDN_ATTR);
for (i = 0, ptr = pbEncoded + 2; ret && i < cRDNAttr &&
ptr - pbEncoded - 2 < pbEncoded[1]; i++)
{
rdn->rgRDNAttr[i].Value.pbData = nextData;
size = bytesNeeded;
ret = CRYPT_AsnDecodeRdnAttr(dwCertEncodingType, ptr,
cbEncoded - (ptr - pbEncoded), dwFlags, &rdn->rgRDNAttr[i],
&size);
if (ret)
{
bytesNeeded -= size;
/* If dwFlags & CRYPT_DECODE_NOCOPY_FLAG, the data may not
* have been copied.
*/
if (rdn->rgRDNAttr[i].Value.pbData == nextData)
nextData += rdn->rgRDNAttr[i].Value.cbData;
/* Ugly: the OID, if copied, is stored in memory after the
* value, so increment by its string length if it's set and
* points here.
*/
if ((const BYTE *)rdn->rgRDNAttr[i].pszObjId == nextData)
nextData += strlen(rdn->rgRDNAttr[i].pszObjId) + 1;
ptr += ptr[1] + 2;
}
}
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeName(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
{
BOOL ret = TRUE;
DWORD bytesNeeded, cRDN = 0;
if (!pbEncoded || !cbEncoded)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (pbEncoded[0] != (ASN_CONSTRUCTOR | ASN_SEQUENCEOF))
{
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
if (cbEncoded <= 1)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
bytesNeeded = sizeof(CERT_NAME_INFO);
if (pbEncoded[1])
{
const BYTE *ptr;
DWORD size;
for (ptr = pbEncoded + 2; ret && ptr - pbEncoded - 2 < pbEncoded[1]; )
{
ret = CRYPT_AsnDecodeRdn(dwCertEncodingType, ptr,
cbEncoded - (ptr - pbEncoded), dwFlags, NULL, &size);
if (ret)
{
cRDN++;
bytesNeeded += size;
ptr += ptr[1] + 2;
}
}
}
if (ret)
{
CERT_NAME_INFO *info;
if (!pvStructInfo)
{
*pcbStructInfo = bytesNeeded;
return TRUE;
}
if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
pcbStructInfo, bytesNeeded))
return FALSE;
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
pvStructInfo = *(BYTE **)pvStructInfo;
info = (CERT_NAME_INFO *)pvStructInfo;
info->cRDN = cRDN;
if (info->cRDN == 0)
info->rgRDN = NULL;
else
{
DWORD size, i;
BYTE *nextData;
const BYTE *ptr;
info->rgRDN = (CERT_RDN *)((BYTE *)pvStructInfo +
sizeof(CERT_NAME_INFO));
nextData = (BYTE *)info->rgRDN + info->cRDN * sizeof(CERT_RDN);
for (i = 0, ptr = pbEncoded + 2; ret && i < cRDN &&
ptr - pbEncoded - 2 < pbEncoded[1]; i++)
{
info->rgRDN[i].rgRDNAttr = (CERT_RDN_ATTR *)nextData;
size = bytesNeeded;
ret = CRYPT_AsnDecodeRdn(dwCertEncodingType, ptr,
cbEncoded - (ptr - pbEncoded), dwFlags, &info->rgRDN[i],
&size);
if (ret)
{
nextData += size;
bytesNeeded -= size;
ptr += ptr[1] + 2;
}
}
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
......@@ -657,7 +1483,7 @@ static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
if (!pbEncoded || !cbEncoded)
{
SetLastError(ERROR_INVALID_PARAMETER);
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
if (!pvStructInfo)
......@@ -670,6 +1496,11 @@ static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
if (cbEncoded <= 1)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
if (pbEncoded[1] == 0)
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
......@@ -719,12 +1550,68 @@ static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
} \
} while (0)
static BOOL CRYPT_AsnDecodeTimeZone(const BYTE *pbEncoded, DWORD len,
SYSTEMTIME *sysTime)
{
BOOL ret = TRUE;
if (len >= 3 && (*pbEncoded == '+' || *pbEncoded == '-'))
{
WORD hours, minutes = 0;
BYTE sign = *pbEncoded++;
len--;
CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours);
if (hours >= 24)
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
ret = FALSE;
goto end;
}
if (len >= 2)
{
CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes);
if (minutes >= 60)
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
ret = FALSE;
goto end;
}
}
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;
}
}
end:
return ret;
}
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;
BOOL ret = TRUE;
if (!pbEncoded || !cbEncoded)
{
......@@ -741,6 +1628,11 @@ static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType,
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
if (cbEncoded <= 1)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
len = pbEncoded[1];
/* FIXME: magic # */
if (len < 10)
......@@ -764,19 +1656,22 @@ static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType,
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) */
ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len, &sysTime);
}
}
if (ret)
{
if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
pcbStructInfo, sizeof(FILETIME)))
return FALSE;
ret = FALSE;
else
{
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
pvStructInfo = *(BYTE **)pvStructInfo;
*pcbStructInfo = sizeof(FILETIME);
return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo);
ret = SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo);
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeGeneralizedTime(DWORD dwCertEncodingType,
......@@ -785,6 +1680,7 @@ static BOOL WINAPI CRYPT_AsnDecodeGeneralizedTime(DWORD dwCertEncodingType,
{
SYSTEMTIME sysTime = { 0 };
BYTE len;
BOOL ret = TRUE;
if (!pbEncoded || !cbEncoded)
{
......@@ -801,6 +1697,11 @@ static BOOL WINAPI CRYPT_AsnDecodeGeneralizedTime(DWORD dwCertEncodingType,
SetLastError(CRYPT_E_ASN1_BADTAG);
return FALSE;
}
if (cbEncoded <= 1)
{
SetLastError(CRYPT_E_ASN1_EOD);
return FALSE;
}
len = pbEncoded[1];
/* FIXME: magic # */
if (len < 10)
......@@ -828,50 +1729,22 @@ static BOOL WINAPI CRYPT_AsnDecodeGeneralizedTime(DWORD dwCertEncodingType,
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);
ret = CRYPT_AsnDecodeTimeZone(pbEncoded, len, &sysTime);
}
else
sysTime.wHour -= hours;
if (minutes > sysTime.wMinute)
if (ret)
{
sysTime.wHour--;
sysTime.wMinute = 60 - (minutes - sysTime.wMinute);
}
else
sysTime.wMinute -= minutes;
}
}
}
if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
pcbStructInfo, sizeof(FILETIME)))
return FALSE;
ret = FALSE;
else
{
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
pvStructInfo = *(BYTE **)pvStructInfo;
*pcbStructInfo = sizeof(FILETIME);
return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo);
ret = SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo);
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType,
......@@ -940,6 +1813,9 @@ BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
{
switch (LOWORD(lpszStructType))
{
case (WORD)X509_NAME:
decodeFunc = CRYPT_AsnDecodeName;
break;
case (WORD)X509_INTEGER:
decodeFunc = CRYPT_AsnDecodeInt;
break;
......
......@@ -42,7 +42,7 @@ static const struct encodedInt ints[] = {
{ 0xbaddf00d, { 2, 4, 0xba, 0xdd, 0xf0, 0x0d } },
};
static void test_encodeint(void)
static void test_encodeint(DWORD dwEncoding)
{
DWORD bufSize = 0;
int i;
......@@ -60,33 +60,35 @@ static void test_encodeint(void)
/* 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());
ret = CryptEncodeObjectEx(dwEncoding, X509_INTEGER, NULL, 0, NULL, NULL,
&bufSize);
ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
"Expected STATUS_ACCESS_VIOLATION, got %08lx\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);
ret = CryptEncodeObjectEx(dwEncoding, 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);
ret = CryptEncodeObjectEx(dwEncoding, X509_INTEGER, &ints[i].val,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
ok(ret, "CryptEncodeObjectEx failed: %ld\n", GetLastError());
if (buf)
{
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 void test_decodeint(DWORD dwEncoding)
{
static const char bigInt[] = { 2, 5, 0xff, 0xfe, 0xff, 0xfe, 0xff };
static const char testStr[] = { 16, 4, 't', 'e', 's', 't' };
static const char testStr[] = { 0x16, 4, 't', 'e', 's', 't' };
BYTE *buf = NULL;
DWORD bufSize = 0;
int i;
......@@ -101,36 +103,33 @@ static void test_decodeint(void)
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 NULL integer buffer */
ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER, NULL, 0, 0, NULL, NULL,
&bufSize);
ok(!ret && GetLastError() == CRYPT_E_ASN1_EOD,
"Expected CRYPT_E_ASN1_EOD, got %08lx\n", GetLastError());
/* 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);
ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER, bigInt, bigInt[1] + 2,
CRYPT_DECODE_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);
ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER, testStr, testStr[1] + 2,
CRYPT_DECODE_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,
ret = CryptDecodeObjectEx(dwEncoding, 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);
ret = CryptDecodeObjectEx(dwEncoding, X509_INTEGER,
(BYTE *)&ints[i].encoded, ints[i].encoded[1] + 2,
CRYPT_DECODE_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);
......@@ -150,7 +149,7 @@ struct encodedFiletime
BYTE *encodedTime;
};
static void testTimeEncoding(LPCSTR encoding,
static void testTimeEncoding(DWORD dwEncoding, LPCSTR structType,
const struct encodedFiletime *time)
{
FILETIME ft = { 0 };
......@@ -160,15 +159,12 @@ static void testTimeEncoding(LPCSTR encoding,
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,
ret = CryptEncodeObjectEx(dwEncoding, structType, &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 ||
if (structType == X509_CHOICE_OF_TIME ||
(time->sysTime.wYear >= 1950 && time->sysTime.wYear <= 2050))
{
ok(ret, "CryptEncodeObjectEx failed: %ld (0x%08lx)\n", GetLastError(),
......@@ -191,7 +187,7 @@ static void testTimeEncoding(LPCSTR encoding,
"Expected CRYPT_E_BAD_ENCODE, got 0x%08lx\n", GetLastError());
}
static void testTimeDecoding(LPCSTR encoding,
static void testTimeDecoding(DWORD dwEncoding, LPCSTR structType,
const struct encodedFiletime *time)
{
FILETIME ft1 = { 0 }, ft2 = { 0 };
......@@ -200,15 +196,12 @@ static void testTimeDecoding(LPCSTR encoding,
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,
ret = CryptDecodeObjectEx(dwEncoding, structType, 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 ||
if (structType == X509_CHOICE_OF_TIME ||
(time->sysTime.wYear >= 1950 && time->sysTime.wYear <= 2050))
{
ok(ret, "CryptDecodeObjectEx failed: %ld (0x%08lx)\n", GetLastError(),
......@@ -227,19 +220,19 @@ static const struct encodedFiletime times[] = {
{ { 2145, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x0f" "21450606161000Z" },
};
static void test_encodeFiletime(void)
static void test_encodeFiletime(DWORD dwEncoding)
{
DWORD i;
for (i = 0; i < sizeof(times) / sizeof(times[0]); i++)
{
testTimeEncoding(X509_CHOICE_OF_TIME, &times[i]);
testTimeEncoding(PKCS_UTC_TIME, &times[i]);
testTimeEncoding(szOID_RSA_signingTime, &times[i]);
testTimeEncoding(dwEncoding, X509_CHOICE_OF_TIME, &times[i]);
testTimeEncoding(dwEncoding, PKCS_UTC_TIME, &times[i]);
testTimeEncoding(dwEncoding, szOID_RSA_signingTime, &times[i]);
}
}
static void test_decodeFiletime(void)
static void test_decodeFiletime(DWORD dwEncoding)
{
static const struct encodedFiletime otherTimes[] = {
{ { 1945, 6, 1, 6, 16, 10, 0, 0 }, "\x18" "\x13" "19450606161000.000Z" },
......@@ -249,6 +242,11 @@ static void test_decodeFiletime(void)
{ { 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" },
{ { 2045, 6, 1, 6, 16, 10, 0, 0 }, "\x17" "\x0b" "4506061610Z" },
{ { 2045, 6, 1, 6, 17, 10, 0, 0 }, "\x17" "\x0d" "4506061610+01" },
{ { 2045, 6, 1, 6, 15, 10, 0, 0 }, "\x17" "\x0d" "4506061610-01" },
{ { 2045, 6, 1, 6, 17, 10, 0, 0 }, "\x17" "\x0f" "4506061610+0100" },
{ { 2045, 6, 1, 6, 15, 10, 0, 0 }, "\x17" "\x0f" "4506061610-0100" },
};
/* An oddball case that succeeds in Windows, but doesn't seem correct
{ { 2145, 6, 1, 2, 11, 31, 0, 0 }, "\x18" "\x13" "21450606161000-9999" },
......@@ -270,33 +268,322 @@ static void test_decodeFiletime(void)
ret = SystemTimeToFileTime(&times[0].sysTime, &ft1);
ok(ret, "SystemTimeToFileTime failed: %ld\n", GetLastError());
size = 1;
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
ret = CryptDecodeObjectEx(dwEncoding, 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, &times[i]);
testTimeDecoding(PKCS_UTC_TIME, &times[i]);
testTimeDecoding(szOID_RSA_signingTime, &times[i]);
testTimeDecoding(dwEncoding, X509_CHOICE_OF_TIME, &times[i]);
testTimeDecoding(dwEncoding, PKCS_UTC_TIME, &times[i]);
testTimeDecoding(dwEncoding, szOID_RSA_signingTime, &times[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]);
testTimeDecoding(dwEncoding, X509_CHOICE_OF_TIME, &otherTimes[i]);
testTimeDecoding(dwEncoding, PKCS_UTC_TIME, &otherTimes[i]);
testTimeDecoding(dwEncoding, 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,
ret = CryptDecodeObjectEx(dwEncoding, 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());
}
}
struct EncodedName
{
CERT_RDN_ATTR attr;
BYTE *encoded;
};
static const char commonName[] = "Juan Lang";
static const char surName[] = "Lang";
static const char bogusIA5[] = "\x80";
static const char bogusPrintable[] = "~";
static const char bogusNumeric[] = "A";
static const struct EncodedName names[] = {
{ { szOID_COMMON_NAME, CERT_RDN_PRINTABLE_STRING,
{ sizeof(commonName), (BYTE *)commonName } },
"\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x13\x0aJuan Lang" },
{ { szOID_COMMON_NAME, CERT_RDN_IA5_STRING,
{ sizeof(commonName), (BYTE *)commonName } },
"\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x16\x0aJuan Lang" },
{ { szOID_SUR_NAME, CERT_RDN_IA5_STRING,
{ sizeof(surName), (BYTE *)surName } },
"\x30\x10\x31\x0e\x30\x0c\x06\x03\x55\x04\x04\x16\x05Lang" },
{ { NULL, CERT_RDN_PRINTABLE_STRING,
{ sizeof(commonName), (BYTE *)commonName } },
"\x30\x12\x31\x10\x30\x0e\x06\x00\x13\x0aJuan Lang" },
/* The following test isn't a very good one, because it doesn't encode any
* Japanese characters. I'm leaving it out for now.
{ { szOID_COMMON_NAME, CERT_RDN_T61_STRING,
{ sizeof(commonName), (BYTE *)commonName } },
"\x30\x15\x31\x13\x30\x11\x06\x03\x55\x04\x03\x14\x0aJuan Lang" },
*/
/* The following tests succeed under Windows, but really should fail,
* they contain characters that are illegal for the encoding. I'm
* including them to justify my lazy encoding.
*/
{ { szOID_COMMON_NAME, CERT_RDN_IA5_STRING,
{ sizeof(bogusIA5), (BYTE *)bogusIA5 } },
"\x30\x0d\x31\x0b\x30\x09\x06\x03\x55\x04\x03\x16\x02\x80" },
{ { szOID_COMMON_NAME, CERT_RDN_PRINTABLE_STRING,
{ sizeof(bogusPrintable), (BYTE *)bogusPrintable } },
"\x30\x0d\x31\x0b\x30\x09\x06\x03\x55\x04\x03\x13\x02\x7e" },
{ { szOID_COMMON_NAME, CERT_RDN_NUMERIC_STRING,
{ sizeof(bogusNumeric), (BYTE *)bogusNumeric } },
"\x30\x0d\x31\x0b\x30\x09\x06\x03\x55\x04\x03\x12\x02\x41" },
};
static const BYTE emptyName[] = { 0x30, 0 };
static const BYTE emptyRDNs[] = { 0x30, 0x02, 0x31, 0 };
static const BYTE twoRDNs[] = "\x30\x23\x31\x21\x30\x0c\x06\x03\x55\x04\x04"
"\x13\x05\x4c\x61\x6e\x67\x00\x30\x11\x06\x03\x55\x04\x03"
"\x13\x0a\x4a\x75\x61\x6e\x20\x4c\x61\x6e\x67";
static void test_encodeName(DWORD dwEncoding)
{
CERT_RDN_ATTR attrs[2];
CERT_RDN rdn;
CERT_NAME_INFO info;
BYTE *buf = NULL;
DWORD size = 0, i;
BOOL ret;
/* Test with NULL pvStructInfo */
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, NULL,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
"Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
/* Test with empty CERT_NAME_INFO */
info.cRDN = 0;
info.rgRDN = NULL;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
ok(!memcmp(buf, emptyName, sizeof(emptyName)),
"Got unexpected encoding for empty name\n");
LocalFree(buf);
}
/* Test with bogus CERT_RDN */
info.cRDN = 1;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
"Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
/* Test with empty CERT_RDN */
rdn.cRDNAttr = 0;
rdn.rgRDNAttr = NULL;
info.cRDN = 1;
info.rgRDN = &rdn;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
ok(!memcmp(buf, emptyRDNs, sizeof(emptyRDNs)),
"Got unexpected encoding for empty RDN array\n");
LocalFree(buf);
}
/* Test with bogus attr array */
rdn.cRDNAttr = 1;
rdn.rgRDNAttr = NULL;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
"Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
/* oddly, a bogus OID is accepted by Windows XP; not testing.
attrs[0].pszObjId = "bogus";
attrs[0].dwValueType = CERT_RDN_PRINTABLE_STRING;
attrs[0].Value.cbData = sizeof(commonName);
attrs[0].Value.pbData = (BYTE *)commonName;
rdn.cRDNAttr = 1;
rdn.rgRDNAttr = attrs;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(!ret, "Expected failure, got success\n");
*/
/* Check with two CERT_RDN_ATTRs. Note DER encoding forces the order of
* the encoded attributes to be swapped.
*/
attrs[0].pszObjId = szOID_COMMON_NAME;
attrs[0].dwValueType = CERT_RDN_PRINTABLE_STRING;
attrs[0].Value.cbData = sizeof(commonName);
attrs[0].Value.pbData = (BYTE *)commonName;
attrs[1].pszObjId = szOID_SUR_NAME;
attrs[1].dwValueType = CERT_RDN_PRINTABLE_STRING;
attrs[1].Value.cbData = sizeof(surName);
attrs[1].Value.pbData = (BYTE *)surName;
rdn.cRDNAttr = 2;
rdn.rgRDNAttr = attrs;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
ok(!memcmp(buf, twoRDNs, sizeof(twoRDNs)),
"Got unexpected encoding for two RDN array\n");
LocalFree(buf);
}
/* CERT_RDN_ANY_TYPE is too vague for X509_NAMEs, check the return */
rdn.cRDNAttr = 1;
attrs[0].dwValueType = CERT_RDN_ANY_TYPE;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
"Expected ERROR_INVALID_PARAMETER, got %08lx\n", GetLastError());
for (i = 0; i < sizeof(names) / sizeof(names[0]); i++)
{
rdn.cRDNAttr = 1;
rdn.rgRDNAttr = (CERT_RDN_ATTR *)&names[i].attr;
ret = CryptEncodeObjectEx(dwEncoding, X509_NAME, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
ok(size == names[i].encoded[1] + 2, "Expected size %d, got %ld\n",
names[i].encoded[1] + 2, size);
ok(!memcmp(buf, names[i].encoded, names[i].encoded[1] + 2),
"Got unexpected encoding\n");
LocalFree(buf);
}
}
}
static void compareNames(const CERT_NAME_INFO *expected,
const CERT_NAME_INFO *got)
{
ok(got->cRDN == expected->cRDN, "Expected %ld RDNs, got %ld\n",
expected->cRDN, got->cRDN);
if (expected->cRDN)
{
ok(got->rgRDN[0].cRDNAttr == expected->rgRDN[0].cRDNAttr,
"Expected %ld RDN attrs, got %ld\n", expected->rgRDN[0].cRDNAttr,
got->rgRDN[0].cRDNAttr);
if (expected->rgRDN[0].cRDNAttr)
{
if (expected->rgRDN[0].rgRDNAttr[0].pszObjId &&
strlen(expected->rgRDN[0].rgRDNAttr[0].pszObjId))
{
ok(got->rgRDN[0].rgRDNAttr[0].pszObjId != NULL,
"Expected OID %s, got NULL\n",
expected->rgRDN[0].rgRDNAttr[0].pszObjId);
if (got->rgRDN[0].rgRDNAttr[0].pszObjId)
ok(!strcmp(got->rgRDN[0].rgRDNAttr[0].pszObjId,
expected->rgRDN[0].rgRDNAttr[0].pszObjId),
"Got unexpected OID %s, expected %s\n",
got->rgRDN[0].rgRDNAttr[0].pszObjId,
expected->rgRDN[0].rgRDNAttr[0].pszObjId);
}
ok(got->rgRDN[0].rgRDNAttr[0].Value.cbData ==
expected->rgRDN[0].rgRDNAttr[0].Value.cbData,
"Unexpected data size, got %ld, expected %ld\n",
got->rgRDN[0].rgRDNAttr[0].Value.cbData,
expected->rgRDN[0].rgRDNAttr[0].Value.cbData);
if (expected->rgRDN[0].rgRDNAttr[0].Value.pbData)
ok(!memcmp(got->rgRDN[0].rgRDNAttr[0].Value.pbData,
expected->rgRDN[0].rgRDNAttr[0].Value.pbData,
expected->rgRDN[0].rgRDNAttr[0].Value.cbData),
"Unexpected value\n");
}
}
}
static void test_decodeName(DWORD dwEncoding)
{
int i;
BYTE *buf = NULL;
DWORD bufSize = 0;
BOOL ret;
CERT_RDN rdn;
CERT_NAME_INFO info = { 1, &rdn };
for (i = 0; i < sizeof(names) / sizeof(names[0]); i++)
{
/* When the output buffer is NULL, this always succeeds */
SetLastError(0xdeadbeef);
ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, names[i].encoded,
names[i].encoded[1] + 2, 0, NULL, NULL, &bufSize);
ok(ret && GetLastError() == NOERROR,
"Expected success and NOERROR, got %08lx\n", GetLastError());
ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, names[i].encoded,
names[i].encoded[1] + 2,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
(BYTE *)&buf, &bufSize);
ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
rdn.cRDNAttr = 1;
rdn.rgRDNAttr = (CERT_RDN_ATTR *)&names[i].attr;
if (buf)
{
compareNames((CERT_NAME_INFO *)buf, &info);
LocalFree(buf);
}
}
/* test empty name */
bufSize = 0;
ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, emptyName,
emptyName[1] + 2,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
(BYTE *)&buf, &bufSize);
ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
/* Interestingly, in Windows, if cRDN is 0, rgRGN may not be NULL. My
* decoder works the same way, so only test the count.
*/
if (buf)
{
ok(bufSize == sizeof(CERT_NAME_INFO),
"Expected bufSize %d, got %ld\n", sizeof(CERT_NAME_INFO), bufSize);
ok(((CERT_NAME_INFO *)buf)->cRDN == 0,
"Expected 0 RDNs in empty info, got %ld\n",
((CERT_NAME_INFO *)buf)->cRDN);
LocalFree(buf);
}
/* test empty RDN */
bufSize = 0;
ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, emptyRDNs,
emptyRDNs[1] + 2,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
(BYTE *)&buf, &bufSize);
ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
CERT_NAME_INFO *info = (CERT_NAME_INFO *)buf;
ok(bufSize == sizeof(CERT_NAME_INFO) + sizeof(CERT_RDN) &&
info->cRDN == 1 && info->rgRDN && info->rgRDN[0].cRDNAttr == 0,
"Got unexpected value for empty RDN\n");
LocalFree(buf);
}
/* test two RDN attrs */
bufSize = 0;
ret = CryptDecodeObjectEx(dwEncoding, X509_NAME, twoRDNs,
twoRDNs[1] + 2,
CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_SHARE_OID_STRING_FLAG, NULL,
(BYTE *)&buf, &bufSize);
ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
CERT_RDN_ATTR attrs[] = {
{ szOID_SUR_NAME, CERT_RDN_PRINTABLE_STRING, { sizeof(surName),
(BYTE *)surName } },
{ szOID_COMMON_NAME, CERT_RDN_PRINTABLE_STRING, { sizeof(commonName),
(BYTE *)commonName } },
};
rdn.cRDNAttr = sizeof(attrs) / sizeof(attrs[0]);
rdn.rgRDNAttr = attrs;
compareNames((CERT_NAME_INFO *)buf, &info);
LocalFree(buf);
}
}
static void test_registerOIDFunction(void)
{
static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
......@@ -353,9 +640,18 @@ static void test_registerOIDFunction(void)
START_TEST(encode)
{
test_encodeint();
test_decodeint();
test_encodeFiletime();
test_decodeFiletime();
static const DWORD encodings[] = { X509_ASN_ENCODING, PKCS_7_ASN_ENCODING,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING };
DWORD i;
for (i = 0; i < sizeof(encodings) / sizeof(encodings[0]); i++)
{
test_encodeint(encodings[i]);
test_decodeint(encodings[i]);
test_encodeFiletime(encodings[i]);
test_decodeFiletime(encodings[i]);
test_encodeName(encodings[i]);
test_decodeName(encodings[i]);
}
test_registerOIDFunction();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment