Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/crypt32/decode.c | 243 ++++++++++++++++++++++++++++++++++++ dlls/crypt32/tests/encode.c | 58 +++++++++ 2 files changed, 301 insertions(+)
diff --git a/dlls/crypt32/decode.c b/dlls/crypt32/decode.c index be36d3ef7b8..762d1b54661 100644 --- a/dlls/crypt32/decode.c +++ b/dlls/crypt32/decode.c @@ -6287,6 +6287,246 @@ static BOOL WINAPI CRYPT_AsnDecodeOCSPBasicSignedResponse(DWORD dwCertEncodingTy return ret; }
+static BOOL CRYPT_AsnDecodeOCSPHashAlgorithm(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + DWORD dataLen; + BOOL ret; + + if (!cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + if (pbEncoded[0] != ASN_SEQUENCEOF) + { + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + DWORD bytesDecoded; + if (dataLen) + { + ret = CRYPT_AsnDecodeAlgorithmId(pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes, dwFlags, + pvStructInfo, pcbStructInfo, &bytesDecoded); + if (ret) *pcbDecoded = bytesDecoded + lenBytes + 1; + } + } + return ret; +} + +static BOOL CRYPT_AsnDecodeOCSPNextUpdate(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + DWORD dataLen; + BOOL ret; + + if (!cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + if (pbEncoded[0] != (ASN_CONTEXT | ASN_CONSTRUCTOR)) + { + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + DWORD bytesDecoded; + if (dataLen) + { + ret = CRYPT_AsnDecodeGeneralizedTime(pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes, dwFlags, + pvStructInfo, pcbStructInfo, &bytesDecoded); + if (ret) *pcbDecoded = bytesDecoded + lenBytes + 1; + } + } + return ret; +} + +static BOOL CRYPT_AsnDecodeOCSPBasicResponseEntry(const BYTE *pbEncoded, DWORD cbEncoded, + DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded) +{ + struct AsnDecodeSequenceItem items[] = { + { ASN_SEQUENCEOF, offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.HashAlgorithm.pszObjId), + CRYPT_AsnDecodeOCSPHashAlgorithm, sizeof(CRYPT_ALGORITHM_IDENTIFIER), FALSE, TRUE, + offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.HashAlgorithm.pszObjId), 0 }, + { ASN_OCTETSTRING, offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.IssuerNameHash), + CRYPT_AsnDecodeOctets, sizeof(CRYPT_HASH_BLOB), FALSE, TRUE, + offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.IssuerNameHash.pbData), 0 }, + { ASN_OCTETSTRING, offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.IssuerKeyHash), + CRYPT_AsnDecodeOctets, sizeof(CRYPT_HASH_BLOB), FALSE, TRUE, + offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.IssuerKeyHash.pbData), 0 }, + { ASN_INTEGER, offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.SerialNumber), + CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_INTEGER_BLOB), FALSE, TRUE, + offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.SerialNumber.pbData), 0 }, + { ASN_CONTEXT, offsetof(OCSP_BASIC_RESPONSE_ENTRY, dwCertStatus), + CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, + 0, 0 }, + /* FIXME: pRevokedInfo */ + { ASN_GENERALTIME, offsetof(OCSP_BASIC_RESPONSE_ENTRY, ThisUpdate), + CRYPT_AsnDecodeGeneralizedTime, sizeof(FILETIME), FALSE, FALSE, + 0, 0 }, + { ASN_CONTEXT | ASN_CONSTRUCTOR, offsetof(OCSP_BASIC_RESPONSE_ENTRY, NextUpdate), + CRYPT_AsnDecodeOCSPNextUpdate, sizeof(FILETIME), TRUE, FALSE, + 0, 0 }, + { ASN_CONTEXT | ASN_CONSTRUCTOR /* FIXME */, offsetof(OCSP_BASIC_RESPONSE_ENTRY, cExtension), + CRYPT_AsnDecodeCertExtensions, FINALMEMBERSIZE(OCSP_BASIC_RESPONSE_ENTRY, cExtension), + TRUE, TRUE, offsetof(OCSP_BASIC_RESPONSE_ENTRY, rgExtension), 0 }, + }; + + return CRYPT_AsnDecodeSequence(items, ARRAY_SIZE(items), + pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, + pcbStructInfo, pcbDecoded, NULL); +} + +static BOOL CRYPT_AsnDecodeOCSPBasicResponseEntriesArray(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + struct AsnArrayDescriptor arrayDesc = { ASN_SEQUENCEOF, + offsetof(OCSP_BASIC_RESPONSE_INFO, cResponseEntry), offsetof(OCSP_BASIC_RESPONSE_INFO, rgResponseEntry), + MEMBERSIZE(OCSP_BASIC_RESPONSE_INFO, cResponseEntry, cExtension), + CRYPT_AsnDecodeOCSPBasicResponseEntry, sizeof(OCSP_BASIC_RESPONSE_ENTRY), + FALSE, 0 }; + + return CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded, + dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded); +} + +static BOOL CRYPT_AsnDecodeResponderID(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + OCSP_BASIC_RESPONSE_INFO *info = pvStructInfo; + BYTE tag = pbEncoded[0] & ~3, choice = pbEncoded[0] & 3; + DWORD decodedLen, dataLen, lenBytes, bytesNeeded = sizeof(*info), len; + CERT_NAME_BLOB *blob; + + if (tag != (ASN_CONTEXT | ASN_CONSTRUCTOR)) + { + WARN("Unexpected tag %02x\n", tag); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + if (choice > 2) + { + WARN("Unexpected choice %02x\n", choice); + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + + if (pvStructInfo && *pcbStructInfo >= bytesNeeded) + info->dwResponderIdChoice = choice; + + if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)) + return FALSE; + lenBytes = GET_LEN_BYTES(pbEncoded[1]); + cbEncoded -= 1 + lenBytes; + decodedLen = 1 + lenBytes; + + if (dataLen > cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + pbEncoded += 1 + lenBytes; + if (pbEncoded[0] != ASN_OCTETSTRING) + { + WARN("Unexpected tag %02x\n", pbEncoded[0]); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + + if (!CRYPT_GetLen(pbEncoded, cbEncoded, &len)) + return FALSE; + lenBytes = GET_LEN_BYTES(pbEncoded[1]); + decodedLen += 1 + lenBytes + len; + cbEncoded -= 1 + lenBytes; + + if (len > cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + pbEncoded += 1 + lenBytes; + + if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) bytesNeeded += len; + if (pvStructInfo && *pcbStructInfo >= bytesNeeded) + { + blob = &info->u.ByNameResponderId; + blob->cbData = len; + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + blob->pbData = (BYTE *)pbEncoded; + else if (blob->cbData) + { + blob->pbData = (BYTE *)(info + 1); + memcpy(blob->pbData, pbEncoded, blob->cbData); + } + } + + if (pcbDecoded) + *pcbDecoded = decodedLen; + + if (!pvStructInfo) + { + *pcbStructInfo = bytesNeeded; + return TRUE; + } + + if (*pcbStructInfo < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbStructInfo = bytesNeeded; + return FALSE; + } + + *pcbStructInfo = bytesNeeded; + return TRUE; +} + +static BOOL WINAPI CRYPT_AsnDecodeOCSPBasicResponse(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, + CRYPT_DECODE_PARA *pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) +{ + struct AsnDecodeSequenceItem items[] = { + { 0, 0, + CRYPT_AsnDecodeResponderID, offsetof(OCSP_BASIC_RESPONSE_INFO, ProducedAt), FALSE, TRUE, + offsetof(OCSP_BASIC_RESPONSE_INFO, u.ByNameResponderId.pbData), 0 }, + { ASN_GENERALTIME, offsetof(OCSP_BASIC_RESPONSE_INFO, ProducedAt), + CRYPT_AsnDecodeGeneralizedTime, sizeof(FILETIME), FALSE, FALSE, + 0, 0 }, + { ASN_SEQUENCEOF, offsetof(OCSP_BASIC_RESPONSE_INFO, cResponseEntry), + CRYPT_AsnDecodeOCSPBasicResponseEntriesArray, MEMBERSIZE(OCSP_BASIC_RESPONSE_INFO, cResponseEntry, cExtension), + TRUE, TRUE, offsetof(OCSP_BASIC_RESPONSE_INFO, rgResponseEntry) }, + { ASN_CONTEXT | ASN_CONSTRUCTOR, offsetof(OCSP_BASIC_RESPONSE_INFO, cExtension), + CRYPT_AsnDecodeCertExtensions, FINALMEMBERSIZE(OCSP_BASIC_RESPONSE_INFO, cExtension), + TRUE, TRUE, offsetof(OCSP_BASIC_RESPONSE_INFO, rgExtension), 0 }, + }; + BOOL ret; + + __TRY + { + ret = CRYPT_AsnDecodeSequence(items, ARRAY_SIZE(items), + pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, + pcbStructInfo, NULL, NULL); + } + __EXCEPT_PAGE_FAULT + { + SetLastError(STATUS_ACCESS_VIOLATION); + ret = FALSE; + } + __ENDTRY + return ret; +} + static CryptDecodeObjectExFunc CRYPT_GetBuiltinDecoder(DWORD dwCertEncodingType, LPCSTR lpszStructType) { @@ -6441,6 +6681,9 @@ static CryptDecodeObjectExFunc CRYPT_GetBuiltinDecoder(DWORD dwCertEncodingType, case LOWORD(OCSP_BASIC_SIGNED_RESPONSE): decodeFunc = CRYPT_AsnDecodeOCSPBasicSignedResponse; break; + case LOWORD(OCSP_BASIC_RESPONSE): + decodeFunc = CRYPT_AsnDecodeOCSPBasicResponse; + break; default: FIXME("Unimplemented decoder for lpszStructType OID %d\n", LOWORD(lpszStructType)); } diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c index c4417a27e2c..b6325efd518 100644 --- a/dlls/crypt32/tests/encode.c +++ b/dlls/crypt32/tests/encode.c @@ -8948,6 +8948,63 @@ static void test_decodeOCSPBasicSignedResponseInfo(DWORD dwEncoding) LocalFree(info); }
+static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) +{ + static const BYTE resp_id[] = { + 0xb7, 0x6b, 0xa2, 0xea, 0xa8, 0xaa, 0x84, 0x8c, 0x79, 0xea, 0xb4, 0xda, 0x0f, 0x98, 0xb2, 0xc5, + 0x95, 0x76, 0xb9, 0xf4}; + static const BYTE name_hash[] = { + 0xe4, 0xe3, 0x95, 0xa2, 0x29, 0xd3, 0xd4, 0xc1, 0xc3, 0x1f, 0xf0, 0x98, 0x0c, 0x0b, 0x4e, 0xc0, + 0x09, 0x8a, 0xab, 0xd8}; + static const BYTE key_hash[] = { + 0xb7, 0x6b, 0xa2, 0xea, 0xa8, 0xaa, 0x84, 0x8c, 0x79, 0xea, 0xb4, 0xda, 0x0f, 0x98, 0xb2, 0xc5, + 0x95, 0x76, 0xb9, 0xf4}; + static const BYTE serial[] = { + 0xb1, 0xc1, 0x87, 0x54, 0x54, 0xac, 0x1e, 0x55, 0x40, 0xfb, 0xef, 0xd9, 0x6d, 0x8f, 0x49, 0x08}; + OCSP_BASIC_RESPONSE_INFO *info; + OCSP_BASIC_RESPONSE_ENTRY *entry; + DWORD size; + BOOL ret; + + size = 0; + ret = pCryptDecodeObjectEx(dwEncoding, OCSP_BASIC_RESPONSE, ocsp_to_be_signed, + sizeof(ocsp_to_be_signed), CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size); + ok(ret, "got %08lx\n", GetLastError()); + + ok(!info->dwVersion, "got %lu\n", info->dwVersion); + ok(info->dwResponderIdChoice == 2, "got %lu\n", info->dwResponderIdChoice); + ok(info->ByKeyResponderId.cbData == sizeof(resp_id), "got %lu\n", info->ByKeyResponderId.cbData); + ok(!memcmp(info->ByKeyResponderId.pbData, resp_id, sizeof(resp_id)), "wrong data\n"); + ok(info->ProducedAt.dwLowDateTime == 3438583808, "got %lu\n", info->ProducedAt.dwLowDateTime); + ok(info->ProducedAt.dwHighDateTime == 30946477, "got %lu\n", info->ProducedAt.dwHighDateTime); + ok(info->cResponseEntry == 1, "got %lu\n", info->cResponseEntry); + ok(info->rgResponseEntry != NULL, "got %p\n", info->rgResponseEntry); + + entry = info->rgResponseEntry; + ok(!strcmp(entry->CertId.HashAlgorithm.pszObjId, szOID_OIWSEC_sha1), "got '%s'\n", entry->CertId.HashAlgorithm.pszObjId); + ok(entry->CertId.HashAlgorithm.Parameters.cbData == 2, "got %lu\n", entry->CertId.HashAlgorithm.Parameters.cbData); + ok(entry->CertId.HashAlgorithm.Parameters.pbData[0] == 5, "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[0]); + ok(!entry->CertId.HashAlgorithm.Parameters.pbData[1], "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[1]); + ok(entry->CertId.IssuerNameHash.cbData == 20, "got %lu\n", entry->CertId.IssuerNameHash.cbData); + ok(!memcmp(entry->CertId.IssuerNameHash.pbData, name_hash, sizeof(name_hash)), "wrong data\n"); + ok(entry->CertId.IssuerKeyHash.cbData == 20, "got %lu\n", entry->CertId.IssuerKeyHash.cbData); + ok(!memcmp(entry->CertId.IssuerKeyHash.pbData, key_hash, sizeof(key_hash)), "wrong data\n"); + ok(entry->CertId.SerialNumber.cbData == 16, "got %lu\n", entry->CertId.SerialNumber.cbData); + ok(!memcmp(entry->CertId.SerialNumber.pbData, serial, sizeof(serial)), "wrong data\n"); + ok(entry->dwCertStatus == 0, "got %lu\n", entry->dwCertStatus); + ok(entry->pRevokedInfo == NULL, "got %p\n", entry->pRevokedInfo); + ok(entry->ThisUpdate.dwLowDateTime == 2558518400, "got %lu\n", entry->ThisUpdate.dwLowDateTime); + ok(entry->ThisUpdate.dwHighDateTime == 30946475, "got %lu\n", entry->ThisUpdate.dwHighDateTime); + ok(entry->NextUpdate.dwLowDateTime == 2014369408, "got %lu\n", entry->NextUpdate.dwLowDateTime); + ok(entry->NextUpdate.dwHighDateTime == 30947877, "got %lu\n", entry->NextUpdate.dwHighDateTime); + ok(!entry->cExtension, "got %lu\n", entry->cExtension); + ok(entry->rgExtension == NULL, "got %p\n", entry->rgExtension); + + ok(!info->cExtension, "got %lu\n", info->cExtension); + ok(info->rgExtension == NULL, "got %p\n", info->rgExtension); + LocalFree(info); +} + START_TEST(encode) { static const DWORD encodings[] = { X509_ASN_ENCODING, PKCS_7_ASN_ENCODING, @@ -9044,6 +9101,7 @@ START_TEST(encode) test_encodeOCSPRequestInfo(encodings[i]); test_decodeOCSPResponseInfo(encodings[i]); test_decodeOCSPBasicSignedResponseInfo(encodings[i]); + test_decodeOCSPBasicResponseInfo(encodings[i]); } testPortPublicKeyInfo(); }