From 13766712f96d9e955091474c3456647915637c95 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Thu, 14 Jul 2005 10:14:22 +0000 Subject: [PATCH] Encode/decode certs, with tests. --- dlls/crypt32/encode.c | 270 ++++++++++++++++++++++++------------ dlls/crypt32/tests/encode.c | 70 ++++++++++ 2 files changed, 254 insertions(+), 86 deletions(-) diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c index 81dd471e6dd..57cdc91ddbf 100644 --- a/dlls/crypt32/encode.c +++ b/dlls/crypt32/encode.c @@ -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; diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c index 50e9be6982b..4df3d70e250 100644 --- a/dlls/crypt32/tests/encode.c +++ b/dlls/crypt32/tests/encode.c @@ -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(); }