[2/3] crypt32: Implement decoding enveloped messages.
Alexander Morozov
amorozov at etersoft.ru
Wed Dec 15 09:07:02 CST 2010
-------------- next part --------------
From 28906d4fb055b3495339e519283b7ca75d6a1a5b Mon Sep 17 00:00:00 2001
From: Alexander Morozov <amorozov at etersoft.ru>
Date: Wed, 15 Dec 2010 17:35:29 +0300
Subject: [PATCH 2/3] crypt32: Implement decoding enveloped messages.
---
dlls/crypt32/crypt32_private.h | 4 +
dlls/crypt32/decode.c | 118 +++++++++++++++++++++++++++
dlls/crypt32/msg.c | 174 +++++++++++++++++++++++++++++++++++++++-
dlls/crypt32/tests/msg.c | 7 --
4 files changed, 292 insertions(+), 11 deletions(-)
diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h
index a4b9aff..65a9cd2 100644
--- a/dlls/crypt32/crypt32_private.h
+++ b/dlls/crypt32/crypt32_private.h
@@ -100,6 +100,10 @@ typedef struct _CRYPT_ENVELOPED_DATA
BOOL CRYPT_AsnEncodePKCSEnvelopedData(const CRYPT_ENVELOPED_DATA *envelopedData,
void *pvData, DWORD *pcbData);
+BOOL CRYPT_AsnDecodePKCSEnvelopedData(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
+ CRYPT_ENVELOPED_DATA *envelopedData, DWORD *pcbEnvelopedData);
+
typedef struct _CRYPT_SIGNED_INFO
{
DWORD version;
diff --git a/dlls/crypt32/decode.c b/dlls/crypt32/decode.c
index a5602ac..62231cd 100644
--- a/dlls/crypt32/decode.c
+++ b/dlls/crypt32/decode.c
@@ -5569,6 +5569,124 @@ BOOL CRYPT_AsnDecodeCMSSignedInfo(const BYTE *pbEncoded, DWORD cbEncoded,
return ret;
}
+static BOOL CRYPT_AsnDecodeRecipientInfo(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded)
+{
+ BOOL ret;
+ CMSG_KEY_TRANS_RECIPIENT_INFO *info = pvStructInfo;
+ struct AsnDecodeSequenceItem items[] = {
+ { ASN_INTEGER, offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO, dwVersion),
+ CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+ { ASN_SEQUENCEOF, offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO,
+ RecipientId.u.IssuerSerialNumber), CRYPT_AsnDecodeIssuerSerialNumber,
+ sizeof(CERT_ISSUER_SERIAL_NUMBER), FALSE, TRUE,
+ offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO,
+ RecipientId.u.IssuerSerialNumber.Issuer.pbData), 0 },
+ { ASN_SEQUENCEOF, offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO,
+ KeyEncryptionAlgorithm), CRYPT_AsnDecodeAlgorithmId,
+ sizeof(CRYPT_ALGORITHM_IDENTIFIER), FALSE, TRUE,
+ offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO,
+ KeyEncryptionAlgorithm.pszObjId), 0 },
+ { ASN_OCTETSTRING, offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO, EncryptedKey),
+ CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DATA_BLOB), FALSE, TRUE,
+ offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO, EncryptedKey.pbData), 0 },
+ };
+
+ TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+ pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+ ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+ pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+ pcbDecoded, info ? info->RecipientId.u.IssuerSerialNumber.Issuer.pbData :
+ NULL);
+ if (info)
+ info->RecipientId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+static BOOL CRYPT_DecodeRecipientInfoArray(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+ BOOL ret;
+ struct AsnArrayDescriptor arrayDesc = { ASN_CONSTRUCTOR | ASN_SETOF,
+ offsetof(CRYPT_ENVELOPED_DATA, cRecipientInfo),
+ offsetof(CRYPT_ENVELOPED_DATA, rgRecipientInfo),
+ MEMBERSIZE(CRYPT_ENVELOPED_DATA, cRecipientInfo, encryptedContentInfo),
+ CRYPT_AsnDecodeRecipientInfo, sizeof(CMSG_KEY_TRANS_RECIPIENT_INFO), TRUE,
+ offsetof(CMSG_KEY_TRANS_RECIPIENT_INFO,
+ RecipientId.u.IssuerSerialNumber.Issuer.pbData) };
+
+ TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+ pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+ ret = CRYPT_AsnDecodeArray(&arrayDesc, pbEncoded, cbEncoded,
+ dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded);
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+static BOOL CRYPT_AsnDecodeEncryptedContentInfo(const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ DWORD *pcbDecoded)
+{
+ BOOL ret;
+ CRYPT_ENCRYPTED_CONTENT_INFO *info = pvStructInfo;
+ struct AsnDecodeSequenceItem items[] = {
+ { ASN_OBJECTIDENTIFIER, offsetof(CRYPT_ENCRYPTED_CONTENT_INFO,
+ contentType), CRYPT_AsnDecodeOidInternal, sizeof(LPSTR),
+ FALSE, TRUE, offsetof(CRYPT_ENCRYPTED_CONTENT_INFO,
+ contentType), 0 },
+ { ASN_SEQUENCEOF, offsetof(CRYPT_ENCRYPTED_CONTENT_INFO,
+ contentEncryptionAlgorithm), CRYPT_AsnDecodeAlgorithmId,
+ sizeof(CRYPT_ALGORITHM_IDENTIFIER), FALSE, TRUE,
+ offsetof(CRYPT_ENCRYPTED_CONTENT_INFO,
+ contentEncryptionAlgorithm.pszObjId), 0 },
+ { ASN_CONTEXT | 0, offsetof(CRYPT_ENCRYPTED_CONTENT_INFO,
+ encryptedContent), CRYPT_AsnDecodeOctetsInternal,
+ sizeof(CRYPT_DATA_BLOB), TRUE, TRUE,
+ offsetof(CRYPT_ENCRYPTED_CONTENT_INFO, encryptedContent.pbData) },
+ };
+
+ TRACE("%p, %d, %08x, %p, %d, %p\n", pbEncoded, cbEncoded, dwFlags,
+ pvStructInfo, *pcbStructInfo, pcbDecoded);
+
+ ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+ pbEncoded, cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo,
+ pcbDecoded, info ? info->contentType : NULL);
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+BOOL CRYPT_AsnDecodePKCSEnvelopedData(const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
+ CRYPT_ENVELOPED_DATA *envelopedData, DWORD *pcbEnvelopedData)
+{
+ BOOL ret;
+ struct AsnDecodeSequenceItem items[] = {
+ { ASN_INTEGER, offsetof(CRYPT_ENVELOPED_DATA, version),
+ CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 },
+ { ASN_CONSTRUCTOR | ASN_SETOF, offsetof(CRYPT_ENVELOPED_DATA,
+ cRecipientInfo), CRYPT_DecodeRecipientInfoArray,
+ MEMBERSIZE(CRYPT_ENVELOPED_DATA, cRecipientInfo, encryptedContentInfo),
+ FALSE, TRUE, offsetof(CRYPT_ENVELOPED_DATA, rgRecipientInfo), 0 },
+ { ASN_SEQUENCEOF, offsetof(CRYPT_ENVELOPED_DATA, encryptedContentInfo),
+ CRYPT_AsnDecodeEncryptedContentInfo,
+ sizeof(CRYPT_ENCRYPTED_CONTENT_INFO), FALSE, TRUE,
+ offsetof(CRYPT_ENVELOPED_DATA, encryptedContentInfo.contentType), 0 },
+ };
+
+ TRACE("%p, %d, %08x, %p, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+ pDecodePara, envelopedData, *pcbEnvelopedData);
+
+ ret = CRYPT_AsnDecodeSequence(items, sizeof(items) / sizeof(items[0]),
+ pbEncoded, cbEncoded, dwFlags, pDecodePara, envelopedData,
+ pcbEnvelopedData, NULL, NULL);
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
static CryptDecodeObjectExFunc CRYPT_GetBuiltinDecoder(DWORD dwCertEncodingType,
LPCSTR lpszStructType)
{
diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c
index 1c5cca4..d85d2a0 100644
--- a/dlls/crypt32/msg.c
+++ b/dlls/crypt32/msg.c
@@ -2045,14 +2045,23 @@ HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags,
return msg;
}
+typedef struct _CEnvelopedDecodeMsg
+{
+ CRYPT_ENVELOPED_DATA *data;
+ HCRYPTPROV crypt_prov;
+ CRYPT_DATA_BLOB content;
+ BOOL decrypted;
+} CEnvelopedDecodeMsg;
+
typedef struct _CDecodeMsg
{
CryptMsgBase base;
DWORD type;
HCRYPTPROV crypt_prov;
union {
- HCRYPTHASH hash;
- CSignedMsgData signed_data;
+ HCRYPTHASH hash;
+ CSignedMsgData signed_data;
+ CEnvelopedDecodeMsg enveloped_data;
} u;
CRYPT_DATA_BLOB msg_data;
CRYPT_DATA_BLOB detached_data;
@@ -2071,6 +2080,12 @@ static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg)
if (msg->u.hash)
CryptDestroyHash(msg->u.hash);
break;
+ case CMSG_ENVELOPED:
+ if (msg->u.enveloped_data.crypt_prov)
+ CryptReleaseContext(msg->u.enveloped_data.crypt_prov, 0);
+ LocalFree(msg->u.enveloped_data.data);
+ CryptMemFree(msg->u.enveloped_data.content.pbData);
+ break;
case CMSG_SIGNED:
if (msg->u.signed_data.info)
{
@@ -2203,6 +2218,21 @@ static BOOL CDecodeMsg_DecodeHashedContent(CDecodeMsg *msg,
return ret;
}
+static BOOL CDecodeMsg_DecodeEnvelopedContent(CDecodeMsg *msg,
+ const CRYPT_DER_BLOB *blob)
+{
+ BOOL ret;
+ CRYPT_ENVELOPED_DATA *envelopedData;
+ DWORD size;
+
+ ret = CRYPT_AsnDecodePKCSEnvelopedData(blob->pbData, blob->cbData,
+ CRYPT_DECODE_ALLOC_FLAG, NULL, (CRYPT_ENVELOPED_DATA *)&envelopedData,
+ &size);
+ if (ret)
+ msg->u.enveloped_data.data = envelopedData;
+ return ret;
+}
+
static BOOL CDecodeMsg_DecodeSignedContent(CDecodeMsg *msg,
const CRYPT_DER_BLOB *blob)
{
@@ -2239,8 +2269,8 @@ static BOOL CDecodeMsg_DecodeContent(CDecodeMsg *msg, const CRYPT_DER_BLOB *blob
msg->type = CMSG_HASHED;
break;
case CMSG_ENVELOPED:
- FIXME("unimplemented for type CMSG_ENVELOPED\n");
- ret = TRUE;
+ if ((ret = CDecodeMsg_DecodeEnvelopedContent(msg, blob)))
+ msg->type = CMSG_ENVELOPED;
break;
case CMSG_SIGNED:
if ((ret = CDecodeMsg_DecodeSignedContent(msg, blob)))
@@ -2316,6 +2346,20 @@ static BOOL CDecodeMsg_FinalizeHashedContent(CDecodeMsg *msg,
return ret;
}
+static BOOL CDecodeMsg_FinalizeEnvelopedContent(CDecodeMsg *msg,
+ CRYPT_DER_BLOB *blob)
+{
+ CRYPT_DATA_BLOB *content;
+
+ if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+ content = &msg->detached_data;
+ else
+ content =
+ &msg->u.enveloped_data.data->encryptedContentInfo.encryptedContent;
+
+ return CRYPT_ConstructBlob(&msg->u.enveloped_data.content, content);
+}
+
static BOOL CDecodeMsg_FinalizeSignedContent(CDecodeMsg *msg,
CRYPT_DER_BLOB *blob)
{
@@ -2377,6 +2421,9 @@ static BOOL CDecodeMsg_FinalizeContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob)
case CMSG_HASHED:
ret = CDecodeMsg_FinalizeHashedContent(msg, blob);
break;
+ case CMSG_ENVELOPED:
+ ret = CDecodeMsg_FinalizeEnvelopedContent(msg, blob);
+ break;
case CMSG_SIGNED:
ret = CDecodeMsg_FinalizeSignedContent(msg, blob);
break;
@@ -3210,6 +3257,118 @@ static BOOL CDecodeSignedMsg_VerifySignatureEx(CDecodeMsg *msg,
return ret;
}
+static BOOL WINAPI CRYPT_ImportKeyTrans(
+ PCRYPT_ALGORITHM_IDENTIFIER pContentEncryptionAlgorithm,
+ PCMSG_CTRL_KEY_TRANS_DECRYPT_PARA pKeyTransDecryptPara, DWORD dwFlags,
+ void *pvReserved, HCRYPTKEY *phContentEncryptKey)
+{
+ BOOL ret;
+ HCRYPTKEY key;
+
+ ret = CryptGetUserKey(pKeyTransDecryptPara->hCryptProv,
+ pKeyTransDecryptPara->dwKeySpec ? pKeyTransDecryptPara->dwKeySpec :
+ AT_KEYEXCHANGE, &key);
+ if (ret)
+ {
+ CMSG_KEY_TRANS_RECIPIENT_INFO *info =
+ &pKeyTransDecryptPara->pKeyTrans[pKeyTransDecryptPara->dwRecipientIndex];
+ CRYPT_DATA_BLOB *encryptedKey = &info->EncryptedKey;
+ DWORD size = encryptedKey->cbData + sizeof(BLOBHEADER) + sizeof(ALG_ID);
+ BYTE *keyBlob = CryptMemAlloc(size);
+
+ if (keyBlob)
+ {
+ DWORD i, k = size - 1;
+ BLOBHEADER *blobHeader = (BLOBHEADER *)keyBlob;
+ ALG_ID *algID = (ALG_ID *)(keyBlob + sizeof(BLOBHEADER));
+
+ blobHeader->bType = SIMPLEBLOB;
+ blobHeader->bVersion = CUR_BLOB_VERSION;
+ blobHeader->reserved = 0;
+ blobHeader->aiKeyAlg = CertOIDToAlgId(
+ pContentEncryptionAlgorithm->pszObjId);
+ *algID = CertOIDToAlgId(info->KeyEncryptionAlgorithm.pszObjId);
+ for (i = 0; i < encryptedKey->cbData; ++i, --k)
+ keyBlob[k] = encryptedKey->pbData[i];
+
+ ret = CryptImportKey(pKeyTransDecryptPara->hCryptProv, keyBlob,
+ size, key, 0, phContentEncryptKey);
+ CryptMemFree(keyBlob);
+ }
+ else
+ ret = FALSE;
+ CryptDestroyKey(key);
+ }
+ return ret;
+}
+
+static BOOL CRYPT_ImportEncryptedKey(PCRYPT_ALGORITHM_IDENTIFIER contEncrAlg,
+ PCMSG_CTRL_DECRYPT_PARA para, PCMSG_KEY_TRANS_RECIPIENT_INFO info,
+ HCRYPTKEY *key)
+{
+ static HCRYPTOIDFUNCSET set = NULL;
+ PFN_CMSG_IMPORT_KEY_TRANS importKeyFunc = NULL;
+ HCRYPTOIDFUNCADDR hFunc = NULL;
+ CMSG_CTRL_KEY_TRANS_DECRYPT_PARA decryptPara;
+ BOOL ret;
+
+ memset(&decryptPara, 0, sizeof(decryptPara));
+ decryptPara.cbSize = sizeof(decryptPara);
+ decryptPara.hCryptProv = para->hCryptProv;
+ decryptPara.dwKeySpec = para->dwKeySpec;
+ decryptPara.pKeyTrans = info;
+ decryptPara.dwRecipientIndex = para->dwRecipientIndex;
+
+ if (!set)
+ set = CryptInitOIDFunctionSet(CMSG_OID_IMPORT_KEY_TRANS_FUNC, 0);
+ CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, contEncrAlg->pszObjId, 0,
+ (void **)&importKeyFunc, &hFunc);
+ if (!importKeyFunc)
+ importKeyFunc = CRYPT_ImportKeyTrans;
+ ret = importKeyFunc(contEncrAlg, &decryptPara, 0, NULL, key);
+ if (hFunc)
+ CryptFreeOIDFunctionAddress(hFunc, 0);
+ return ret;
+}
+
+static BOOL CDecodeEnvelopedMsg_CrtlDecrypt(CDecodeMsg *msg,
+ PCMSG_CTRL_DECRYPT_PARA para)
+{
+ BOOL ret = FALSE;
+ CEnvelopedDecodeMsg *enveloped_data = &msg->u.enveloped_data;
+ CRYPT_ENVELOPED_DATA *data = enveloped_data->data;
+
+ if (para->cbSize != sizeof(CMSG_CTRL_DECRYPT_PARA))
+ SetLastError(E_INVALIDARG);
+ else if (!data)
+ SetLastError(CRYPT_E_INVALID_MSG_TYPE);
+ else if (para->dwRecipientIndex >= data->cRecipientInfo)
+ SetLastError(CRYPT_E_INVALID_INDEX);
+ else if (enveloped_data->decrypted)
+ SetLastError(CRYPT_E_ALREADY_DECRYPTED);
+ else if (!para->hCryptProv)
+ SetLastError(ERROR_INVALID_PARAMETER);
+ else if (enveloped_data->content.cbData)
+ {
+ HCRYPTKEY key;
+
+ ret = CRYPT_ImportEncryptedKey(
+ &data->encryptedContentInfo.contentEncryptionAlgorithm, para,
+ data->rgRecipientInfo, &key);
+ if (ret)
+ {
+ ret = CryptDecrypt(key, 0, TRUE, 0, enveloped_data->content.pbData,
+ &enveloped_data->content.cbData);
+ CryptDestroyKey(key);
+ }
+ }
+ else
+ ret = TRUE;
+ if (ret)
+ enveloped_data->decrypted = TRUE;
+ return ret;
+}
+
static BOOL CDecodeMsg_Control(HCRYPTMSG hCryptMsg, DWORD dwFlags,
DWORD dwCtrlType, const void *pvCtrlPara)
{
@@ -3231,6 +3390,13 @@ static BOOL CDecodeMsg_Control(HCRYPTMSG hCryptMsg, DWORD dwFlags,
case CMSG_CTRL_DECRYPT:
switch (msg->type)
{
+ case CMSG_ENVELOPED:
+ ret = CDecodeEnvelopedMsg_CrtlDecrypt(msg,
+ (PCMSG_CTRL_DECRYPT_PARA)pvCtrlPara);
+ if (ret && (dwFlags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG))
+ msg->u.enveloped_data.crypt_prov =
+ ((PCMSG_CTRL_DECRYPT_PARA)pvCtrlPara)->hCryptProv;
+ break;
default:
SetLastError(CRYPT_E_INVALID_MSG_TYPE);
}
diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c
index e32690b..20ad5c9 100644
--- a/dlls/crypt32/tests/msg.c
+++ b/dlls/crypt32/tests/msg.c
@@ -2606,7 +2606,6 @@ static void test_decode_msg_update(void)
SetLastError(0xdeadbeef);
ret = CryptMsgUpdate(msg, envelopedEmptyContent,
sizeof(envelopedEmptyContent), TRUE);
- todo_wine
ok(!ret &&
(GetLastError() == CRYPT_E_ASN1_BADTAG ||
GetLastError() == OSS_DATA_ERROR), /* Win9x */
@@ -3089,12 +3088,10 @@ static void test_decode_msg_get_param(void)
decryptPara.hCryptProv = hCryptProv;
SetLastError(0xdeadbeef);
ret = CryptMsgControl(msg, 0, CMSG_CTRL_DECRYPT, &decryptPara);
- todo_wine
ok(ret, "CryptMsgControl failed: %08x\n", GetLastError());
decryptPara.hCryptProv = 0;
SetLastError(0xdeadbeef);
ret = CryptMsgControl(msg, 0, CMSG_CTRL_DECRYPT, &decryptPara);
- todo_wine
ok(!ret && GetLastError() == CRYPT_E_ALREADY_DECRYPTED,
"expected CRYPT_E_ALREADY_DECRYPTED, got %08x\n", GetLastError());
todo_wine
@@ -3118,7 +3115,6 @@ static void test_decode_msg_get_param(void)
decryptPara.hCryptProv = hCryptProv;
SetLastError(0xdeadbeef);
ret = CryptMsgControl(msg, 0, CMSG_CTRL_DECRYPT, &decryptPara);
- todo_wine
ok(ret, "CryptMsgControl failed: %08x\n", GetLastError());
todo_wine
check_param("enveloped bare message", msg, CMSG_CONTENT_PARAM, msgData,
@@ -3556,7 +3552,6 @@ static void test_msg_control(void)
decryptPara.cbSize = 0;
SetLastError(0xdeadbeef);
ret = CryptMsgControl(msg, 0, CMSG_CTRL_DECRYPT, &decryptPara);
- todo_wine
ok(!ret && GetLastError() == E_INVALIDARG,
"expected E_INVALIDARG, got %08x\n", GetLastError());
decryptPara.cbSize = sizeof(decryptPara);
@@ -3573,7 +3568,6 @@ static void test_msg_control(void)
ok(ret, "CryptMsgUpdate failed: %08x\n", GetLastError());
SetLastError(0xdeadbeef);
ret = CryptMsgControl(msg, 0, CMSG_CTRL_DECRYPT, &decryptPara);
- todo_wine
ok(!ret && GetLastError() == CRYPT_E_INVALID_INDEX,
"expected CRYPT_E_INVALID_INDEX, got %08x\n", GetLastError());
CryptMsgClose(msg);
@@ -3586,7 +3580,6 @@ static void test_msg_control(void)
ok(ret, "CryptMsgUpdate failed: %08x\n", GetLastError());
SetLastError(0xdeadbeef);
ret = CryptMsgControl(msg, 0, CMSG_CTRL_DECRYPT, &decryptPara);
- todo_wine
ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER,
"expected ERROR_INVALID_PARAMETER, got %08x\n", GetLastError());
CryptMsgClose(msg);
--
1.7.3.2
More information about the wine-patches
mailing list