Encode/decode certs, with tests.

This commit is contained in:
Juan Lang 2005-07-14 10:14:22 +00:00 committed by Alexandre Julliard
parent 11ad6a0ac4
commit 13766712f9
2 changed files with 254 additions and 86 deletions

View File

@ -100,6 +100,9 @@ static BOOL WINAPI CRYPT_AsnEncodeOctets(DWORD dwCertEncodingType,
static BOOL WINAPI CRYPT_AsnEncodeBits(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded);
static BOOL WINAPI CRYPT_AsnEncodeBitsSwapBytes(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded);
static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded);
@ -732,6 +735,37 @@ static BOOL WINAPI CRYPT_AsnEncodePubKeyInfo(DWORD dwCertEncodingType,
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeCert(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
{
BOOL ret;
__TRY
{
const CERT_SIGNED_CONTENT_INFO *info =
(const CERT_SIGNED_CONTENT_INFO *)pvStructInfo;
struct AsnEncodeSequenceItem items[] = {
{ &info->ToBeSigned, CRYPT_CopyEncodedBlob, 0 },
{ &info->SignatureAlgorithm, CRYPT_AsnEncodeAlgorithmId, 0 },
{ &info->Signature, CRYPT_AsnEncodeBitsSwapBytes, 0 },
};
if (dwFlags & CRYPT_ENCODE_NO_SIGNATURE_BYTE_REVERSAL_FLAG)
items[2].encodeFunc = CRYPT_AsnEncodeBits;
ret = CRYPT_AsnEncodeSequence(dwCertEncodingType, items,
sizeof(items) / sizeof(items[0]), dwFlags, pEncodePara, pbEncoded,
pcbEncoded);
}
__EXCEPT(page_fault)
{
SetLastError(STATUS_ACCESS_VIOLATION);
ret = FALSE;
}
__ENDTRY
return ret;
}
/* Like in Windows, this blithely ignores the validity of the passed-in
* CERT_INFO, and just encodes it as-is. The resulting encoded data may not
* decode properly, see CRYPT_AsnDecodeCertInfo.
@ -1447,6 +1481,45 @@ static BOOL WINAPI CRYPT_AsnEncodeBits(DWORD dwCertEncodingType,
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeBitsSwapBytes(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
{
BOOL ret;
__TRY
{
const CRYPT_BIT_BLOB *blob = (const CRYPT_BIT_BLOB *)pvStructInfo;
CRYPT_BIT_BLOB newBlob = { blob->cbData, NULL, blob->cUnusedBits };
ret = TRUE;
if (newBlob.cbData)
{
newBlob.pbData = HeapAlloc(GetProcessHeap(), 0, newBlob.cbData);
if (newBlob.pbData)
{
DWORD i;
for (i = 0; i < newBlob.cbData; i++)
newBlob.pbData[newBlob.cbData - i - 1] = blob->pbData[i];
}
else
ret = FALSE;
}
if (ret)
ret = CRYPT_AsnEncodeBits(dwCertEncodingType, lpszStructType,
&newBlob, dwFlags, pEncodePara, pbEncoded, pcbEncoded);
HeapFree(GetProcessHeap(), 0, newBlob.pbData);
}
__EXCEPT(page_fault)
{
SetLastError(STATUS_ACCESS_VIOLATION);
ret = FALSE;
}
__ENDTRY
return ret;
}
static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@ -1856,6 +1929,9 @@ BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
{
switch (LOWORD(lpszStructType))
{
case (WORD)X509_CERT:
encodeFunc = CRYPT_AsnEncodeCert;
break;
case (WORD)X509_CERT_TO_BE_SIGNED:
encodeFunc = CRYPT_AsnEncodeCertInfo;
break;
@ -2129,10 +2205,11 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
&items[i].size);
if (ret)
{
/* FIXME: need to ensure alignment for sizes greater
* than the minimum size
*/
/* Account for alignment padding */
bytesNeeded += items[i].size;
if (items[i].size % sizeof(DWORD))
bytesNeeded += sizeof(DWORD) -
items[i].size % sizeof(DWORD);
ptr += 1 + nextItemLenBytes + nextItemLen;
}
else if (items[i].optional &&
@ -2160,7 +2237,7 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded)))
{
BYTE *nextData;
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
pvStructInfo = *(BYTE **)pvStructInfo;
nextData = (BYTE *)pvStructInfo + minSize;
@ -2187,8 +2264,14 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
{
if (items[i].hasPointer &&
items[i].size > items[i].minSize)
{
nextData += items[i].size -
items[i].minSize;
/* align nextData to DWORD boundaries */
if (items[i].size % sizeof(DWORD))
nextData += sizeof(DWORD) -
items[i].size % sizeof(DWORD);
}
ptr += 1 + nextItemLenBytes + nextItemLen;
}
else if (items[i].optional &&
@ -2216,33 +2299,6 @@ static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeCertVersion(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
{
BOOL ret;
if (pbEncoded[0] == (ASN_CONTEXT | ASN_CONSTRUCTOR))
{
DWORD dataLen;
if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
{
BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER,
pbEncoded + 1 + lenBytes, dataLen, dwFlags, pDecodePara,
pvStructInfo, pcbStructInfo);
}
}
else
{
SetLastError(CRYPT_E_ASN1_BADTAG);
ret = FALSE;
}
return ret;
}
/* Decodes a DER-encoded BLOB into a CRYPT_DER_BLOB struct pointed to by
* pvStructInfo. The BLOB must be non-empty, otherwise the last error is set
* to CRYPT_E_ASN1_CORRUPT.
@ -2292,6 +2348,94 @@ static BOOL WINAPI CRYPT_AsnDecodeDerBlob(DWORD dwCertEncodingType,
return ret;
}
/* Like CRYPT_AsnDecodeBitsInternal, but swaps the bytes */
static BOOL WINAPI CRYPT_AsnDecodeBitsSwapBytes(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
{
BOOL ret;
ret = CRYPT_AsnDecodeBitsInternal(dwCertEncodingType, lpszStructType,
pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo);
if (ret && pvStructInfo)
{
CRYPT_BIT_BLOB *blob = (CRYPT_BIT_BLOB *)pvStructInfo;
DWORD i;
BYTE temp;
for (i = 0; i < blob->cbData / 2; i++)
{
temp = blob->pbData[i];
blob->pbData[i] = blob->pbData[blob->cbData - i - 1];
blob->pbData[blob->cbData - i - 1] = temp;
}
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeCert(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
{
BOOL ret = TRUE;
__TRY
{
struct AsnDecodeSequenceItem items[] = {
{ offsetof(CERT_SIGNED_CONTENT_INFO, ToBeSigned),
CRYPT_AsnDecodeDerBlob, sizeof(CRYPT_DER_BLOB), FALSE, TRUE,
offsetof(CERT_SIGNED_CONTENT_INFO, ToBeSigned.pbData), 0 },
{ offsetof(CERT_SIGNED_CONTENT_INFO, SignatureAlgorithm),
CRYPT_AsnDecodeAlgorithmId, sizeof(CRYPT_ALGORITHM_IDENTIFIER),
FALSE, TRUE, offsetof(CERT_SIGNED_CONTENT_INFO,
SignatureAlgorithm.Parameters.pbData), 0 },
{ offsetof(CERT_SIGNED_CONTENT_INFO, Signature),
CRYPT_AsnDecodeBitsSwapBytes, sizeof(CRYPT_BIT_BLOB), FALSE, TRUE,
offsetof(CERT_SIGNED_CONTENT_INFO, Signature.pbData), 0 },
};
if (dwFlags & CRYPT_DECODE_NO_SIGNATURE_BYTE_REVERSAL_FLAG)
items[2].decodeFunc = CRYPT_AsnDecodeBitsInternal;
ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
pDecodePara, pvStructInfo, pcbStructInfo);
}
__EXCEPT(page_fault)
{
SetLastError(STATUS_ACCESS_VIOLATION);
ret = FALSE;
}
__ENDTRY
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeCertVersion(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
{
BOOL ret;
if (pbEncoded[0] == (ASN_CONTEXT | ASN_CONSTRUCTOR))
{
DWORD dataLen;
if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
{
BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER,
pbEncoded + 1 + lenBytes, dataLen, dwFlags, pDecodePara,
pvStructInfo, pcbStructInfo);
}
}
else
{
SetLastError(CRYPT_E_ASN1_BADTAG);
ret = FALSE;
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeValidity(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@ -2338,16 +2482,16 @@ static BOOL WINAPI CRYPT_AsnDecodeCertInfo(DWORD dwCertEncodingType,
Subject.pbData) },
{ offsetof(CERT_INFO, SubjectPublicKeyInfo), CRYPT_AsnDecodePubKeyInfo,
sizeof(CERT_PUBLIC_KEY_INFO), FALSE, TRUE, offsetof(CERT_INFO,
SubjectPublicKeyInfo.Algorithm.Parameters.pbData) },
SubjectPublicKeyInfo.Algorithm.Parameters.pbData), 0 },
{ offsetof(CERT_INFO, IssuerUniqueId), CRYPT_AsnDecodeBitsInternal,
sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CERT_INFO,
IssuerUniqueId.pbData) },
IssuerUniqueId.pbData), 0 },
{ offsetof(CERT_INFO, SubjectUniqueId), CRYPT_AsnDecodeBitsInternal,
sizeof(CRYPT_BIT_BLOB), TRUE, TRUE, offsetof(CERT_INFO,
SubjectUniqueId.pbData) },
SubjectUniqueId.pbData), 0 },
{ offsetof(CERT_INFO, cExtension), CRYPT_AsnDecodeExtensions,
sizeof(CERT_EXTENSIONS), TRUE, TRUE, offsetof(CERT_INFO,
rgExtension) },
rgExtension), 0 },
};
ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
@ -3322,57 +3466,6 @@ static BOOL WINAPI CRYPT_AsnDecodeBool(DWORD dwCertEncodingType,
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodePathConstraint(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
{
BOOL ret = TRUE;
DWORD bytesNeeded = sizeof(BOOL) + sizeof(DWORD);
if (!pvStructInfo)
*pcbStructInfo = bytesNeeded;
else if (*pcbStructInfo < bytesNeeded)
{
*pcbStructInfo = bytesNeeded;
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
else
{
if (cbEncoded)
{
if (pbEncoded[0] == ASN_INTEGER)
{
DWORD size = sizeof(DWORD);
ret = CRYPT_AsnDecodeInt(dwCertEncodingType, X509_INTEGER,
pbEncoded, cbEncoded, 0, NULL,
(BYTE *)pvStructInfo + sizeof(BOOL), &size);
if (ret)
{
cbEncoded -= 2 + pbEncoded[1];
pbEncoded += 2 + pbEncoded[1];
if (cbEncoded)
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
ret = FALSE;
}
else
*(BOOL *)pvStructInfo = TRUE;
}
}
else
{
SetLastError(CRYPT_E_ASN1_BADTAG);
ret = FALSE;
}
}
else
*(BOOL *)pvStructInfo = FALSE;
}
return ret;
}
static BOOL WINAPI CRYPT_AsnDecodeBasicConstraints2(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@ -3571,8 +3664,10 @@ static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType,
blob->cUnusedBits = *(pbEncoded + 1 +
GET_LEN_BYTES(pbEncoded[1]));
if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
{
blob->pbData = (BYTE *)pbEncoded + 2 +
GET_LEN_BYTES(pbEncoded[1]);
}
else
{
assert(blob->pbData);
@ -4368,6 +4463,9 @@ BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
{
switch (LOWORD(lpszStructType))
{
case (WORD)X509_CERT:
decodeFunc = CRYPT_AsnDecodeCert;
break;
case (WORD)X509_CERT_TO_BE_SIGNED:
decodeFunc = CRYPT_AsnDecodeCertInfo;
break;

View File

@ -1628,6 +1628,74 @@ static void test_decodeCertToBeSigned(DWORD dwEncoding)
}
}
static const BYTE hash[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd,
0xe, 0xf };
static const BYTE signedBigCert[] = {
0x30, 0x81, 0x93, 0x30, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x02, 0x06, 0x00, 0x30,
0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a,
0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61, 0x6e, 0x67, 0x00, 0x30, 0x22, 0x18, 0x0f,
0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30,
0x30, 0x5a, 0x18, 0x0f, 0x31, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30,
0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x03, 0x13, 0x0a, 0x4a, 0x75, 0x61, 0x6e, 0x20, 0x4c, 0x61,
0x6e, 0x67, 0x00, 0x30, 0x07, 0x30, 0x02, 0x06, 0x00, 0x03, 0x01, 0x00, 0xa3,
0x16, 0x30, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff,
0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30, 0x02, 0x06,
0x00, 0x03, 0x11, 0x00, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07,
0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 };
static void test_encodeCert(DWORD dwEncoding)
{
/* Note the SignatureAlgorithm must match that in the encoded cert. Note
* also that bigCert is a NULL-terminated string, so don't count its
* last byte (otherwise the signed cert won't decode.)
*/
CERT_SIGNED_CONTENT_INFO info = { { sizeof(bigCert) - 1, (BYTE *)bigCert },
{ NULL, { 0, NULL } }, { sizeof(hash), (BYTE *)hash, 0 } };
BOOL ret;
BYTE *buf = NULL;
DWORD bufSize = 0;
ret = CryptEncodeObjectEx(dwEncoding, X509_CERT, &info,
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &bufSize);
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
ok(bufSize == sizeof(signedBigCert), "Expected size %d, got %ld\n",
sizeof(signedBigCert), bufSize);
ok(!memcmp(buf, signedBigCert, bufSize), "Unexpected cert\n");
LocalFree(buf);
}
}
static void test_decodeCert(DWORD dwEncoding)
{
BOOL ret;
BYTE *buf = NULL;
DWORD size = 0;
ret = CryptDecodeObjectEx(dwEncoding, X509_CERT, signedBigCert,
sizeof(signedBigCert), CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
if (buf)
{
CERT_SIGNED_CONTENT_INFO *info = (CERT_SIGNED_CONTENT_INFO *)buf;
ok(info->ToBeSigned.cbData == sizeof(bigCert) - 1,
"Expected cert to be %d bytes, got %ld\n", sizeof(bigCert) - 1,
info->ToBeSigned.cbData);
ok(!memcmp(info->ToBeSigned.pbData, bigCert, info->ToBeSigned.cbData),
"Unexpected cert\n");
ok(info->Signature.cbData == sizeof(hash),
"Expected signature size %d, got %ld\n", sizeof(hash),
info->Signature.cbData);
ok(!memcmp(info->Signature.pbData, hash, info->Signature.cbData),
"Unexpected signature\n");
LocalFree(buf);
}
}
static void test_registerOIDFunction(void)
{
static const WCHAR bogusDll[] = { 'b','o','g','u','s','.','d','l','l',0 };
@ -1712,6 +1780,8 @@ START_TEST(encode)
test_decodePublicKeyInfo(encodings[i]);
test_encodeCertToBeSigned(encodings[i]);
test_decodeCertToBeSigned(encodings[i]);
test_encodeCert(encodings[i]);
test_decodeCert(encodings[i]);
}
test_registerOIDFunction();
}