Module: wine Branch: master Commit: afaba37ed71ba3d4fc15cb7fb0d47f8ba869da00 URL: http://source.winehq.org/git/wine.git/?a=commit;h=afaba37ed71ba3d4fc15cb7fb0...
Author: Juan Lang juan.lang@gmail.com Date: Thu Jul 12 14:20:20 2007 -0700
crypt32: Implement streamed encoding of definite-length data messages.
---
dlls/crypt32/msg.c | 156 ++++++++++++++++++++++++++++++++++++++++----- dlls/crypt32/tests/msg.c | 25 +++++++- 2 files changed, 161 insertions(+), 20 deletions(-)
diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c index 63f6671..299b97e 100644 --- a/dlls/crypt32/msg.c +++ b/dlls/crypt32/msg.c @@ -19,8 +19,11 @@ #include "windef.h" #include "winbase.h" #include "wincrypt.h" +#include "snmp.h"
#include "wine/debug.h" +#include "wine/exception.h" +#include "crypt32_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
@@ -74,6 +77,7 @@ typedef struct _CDataEncodeMsg CryptMsgBase base; DWORD bare_content_len; LPBYTE bare_content; + BOOL begun; } CDataEncodeMsg;
static const BYTE empty_data_content[] = { 0x04,0x00 }; @@ -86,6 +90,71 @@ static void CDataEncodeMsg_Close(HCRYPTMSG hCryptMsg) LocalFree(msg->bare_content); }
+static WINAPI BOOL CRYPT_EncodeContentLength(DWORD dwCertEncodingType, + LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, + PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) +{ + const CDataEncodeMsg *msg = (const CDataEncodeMsg *)pvStructInfo; + DWORD lenBytes; + BOOL ret = TRUE; + + /* Trick: report bytes needed based on total message length, even though + * the message isn't available yet. The caller will use the length + * reported here to encode its length. + */ + CRYPT_EncodeLen(msg->base.stream_info.cbContent, NULL, &lenBytes); + if (!pbEncoded) + *pcbEncoded = 1 + lenBytes + msg->base.stream_info.cbContent; + else + { + if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, + pcbEncoded, 1 + lenBytes))) + { + if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) + pbEncoded = *(BYTE **)pbEncoded; + *pbEncoded++ = ASN_OCTETSTRING; + CRYPT_EncodeLen(msg->base.stream_info.cbContent, pbEncoded, + &lenBytes); + } + } + return ret; +} + +static BOOL CRYPT_EncodeDataContentInfoHeader(CDataEncodeMsg *msg, + CRYPT_DATA_BLOB *header) +{ + BOOL ret; + + if (msg->base.streamed && msg->base.stream_info.cbContent == 0xffffffff) + { + FIXME("unimplemented for indefinite-length encoding\n"); + header->cbData = 0; + header->pbData = NULL; + ret = TRUE; + } + else + { + struct AsnConstructedItem constructed = { 0, msg, + CRYPT_EncodeContentLength }; + struct AsnEncodeSequenceItem items[2] = { + { szOID_RSA_data, CRYPT_AsnEncodeOid, 0 }, + { &constructed, CRYPT_AsnEncodeConstructed, 0 }, + }; + + ret = CRYPT_AsnEncodeSequence(X509_ASN_ENCODING, items, + sizeof(items) / sizeof(items[0]), CRYPT_ENCODE_ALLOC_FLAG, NULL, + (LPBYTE)&header->pbData, &header->cbData); + if (ret) + { + /* Trick: subtract the content length from the reported length, + * as the actual content hasn't come yet. + */ + header->cbData -= msg->base.stream_info.cbContent; + } + } + return ret; +} + static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData, DWORD cbData, BOOL fFinal) { @@ -94,30 +163,80 @@ static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
if (msg->base.finalized) SetLastError(CRYPT_E_MSG_ERROR); - else if (!fFinal) + else if (msg->base.streamed) { - if (msg->base.open_flags & CMSG_DETACHED_FLAG) - SetLastError(E_INVALIDARG); - else - SetLastError(CRYPT_E_MSG_ERROR); + if (fFinal) + msg->base.finalized = TRUE; + __TRY + { + if (!msg->begun) + { + CRYPT_DATA_BLOB header; + + msg->begun = TRUE; + ret = CRYPT_EncodeDataContentInfoHeader(msg, &header); + if (ret) + { + ret = msg->base.stream_info.pfnStreamOutput( + msg->base.stream_info.pvArg, header.pbData, header.cbData, + FALSE); + LocalFree(header.pbData); + } + } + if (!fFinal) + ret = msg->base.stream_info.pfnStreamOutput( + msg->base.stream_info.pvArg, (BYTE *)pbData, cbData, + FALSE); + else + { + if (msg->base.stream_info.cbContent == 0xffffffff) + { + BYTE indefinite_trailer[6] = { 0 }; + + ret = msg->base.stream_info.pfnStreamOutput( + msg->base.stream_info.pvArg, (BYTE *)pbData, cbData, + FALSE); + if (ret) + ret = msg->base.stream_info.pfnStreamOutput( + msg->base.stream_info.pvArg, indefinite_trailer, + sizeof(indefinite_trailer), TRUE); + } + else + ret = msg->base.stream_info.pfnStreamOutput( + msg->base.stream_info.pvArg, (BYTE *)pbData, cbData, TRUE); + } + } + __EXCEPT_PAGE_FAULT + { + SetLastError(STATUS_ACCESS_VIOLATION); + } + __ENDTRY; } else { - msg->base.finalized = TRUE; - if (!cbData) - SetLastError(E_INVALIDARG); + if (!fFinal) + { + if (msg->base.open_flags & CMSG_DETACHED_FLAG) + SetLastError(E_INVALIDARG); + else + SetLastError(CRYPT_E_MSG_ERROR); + } else { - CRYPT_DATA_BLOB blob = { cbData, (LPBYTE)pbData }; - - /* data messages don't allow non-final updates, don't bother - * checking whether data already exist, they can't. - */ - ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING, - &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL, &msg->bare_content, - &msg->bare_content_len); - if (ret && msg->base.streamed) - FIXME("stream info unimplemented\n"); + msg->base.finalized = TRUE; + if (!cbData) + SetLastError(E_INVALIDARG); + else + { + CRYPT_DATA_BLOB blob = { cbData, (LPBYTE)pbData }; + + /* non-streamed data messages don't allow non-final updates, + * don't bother checking whether data already exist, they can't. + */ + ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING, + &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL, &msg->bare_content, + &msg->bare_content_len); + } } } return ret; @@ -189,6 +308,7 @@ static HCRYPTMSG CDataEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo, CDataEncodeMsg_Close, CDataEncodeMsg_GetParam, CDataEncodeMsg_Update); msg->bare_content_len = sizeof(empty_data_content); msg->bare_content = (LPBYTE)empty_data_content; + msg->begun = FALSE; } return (HCRYPTMSG)msg; } diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index 1e987d2..4898c6e 100644 --- a/dlls/crypt32/tests/msg.c +++ b/dlls/crypt32/tests/msg.c @@ -336,6 +336,7 @@ static void test_data_msg_update(void) { HCRYPTMSG msg; BOOL ret; + CMSG_STREAM_INFO streamInfo = { 0 };
msg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING, 0, CMSG_DATA, NULL, NULL, NULL); @@ -383,6 +384,28 @@ static void test_data_msg_update(void) ret = CryptMsgUpdate(msg, msgData, sizeof(msgData), TRUE); ok(ret, "CryptMsgUpdate failed: %x\n", GetLastError()); CryptMsgClose(msg); + + /* Calling update after opening with an empty stream info (with a bogus + * output function) yields an error: + */ + msg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING, 0, CMSG_DATA, NULL, NULL, + &streamInfo); + SetLastError(0xdeadbeef); + ret = CryptMsgUpdate(msg, msgData, sizeof(msgData), FALSE); + ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION, + "Expected STATUS_ACCESS_VIOLATION, got %x\n", GetLastError()); + CryptMsgClose(msg); + /* Calling update with a valid output function succeeds, even if the data + * exceeds the size specified in the stream info. + */ + streamInfo.pfnStreamOutput = nop_stream_output; + msg = CryptMsgOpenToEncode(PKCS_7_ASN_ENCODING, 0, CMSG_DATA, NULL, NULL, + &streamInfo); + ret = CryptMsgUpdate(msg, msgData, sizeof(msgData), FALSE); + ok(ret, "CryptMsgUpdate failed: %x\n", GetLastError()); + ret = CryptMsgUpdate(msg, msgData, sizeof(msgData), TRUE); + ok(ret, "CryptMsgUpdate failed: %x\n", GetLastError()); + CryptMsgClose(msg); }
static void test_data_msg_get_param(void) @@ -600,7 +623,6 @@ static void test_data_msg_encoding(void) CryptMsgUpdate(msg, msgData, sizeof(msgData), FALSE); CryptMsgUpdate(msg, msgData, sizeof(msgData), TRUE); CryptMsgClose(msg); - todo_wine check_updates("bogus data message with definite length", &a1, &accum); free_updates(&accum); /* A valid definite-length encoding: */ @@ -609,7 +631,6 @@ static void test_data_msg_encoding(void) NULL, &streamInfo); CryptMsgUpdate(msg, msgData, sizeof(msgData), TRUE); CryptMsgClose(msg); - todo_wine check_updates("data message with definite length", &a2, &accum); free_updates(&accum); /* An indefinite-length encoding: */