Juan Lang : crypt32: Implement streamed encoding of definite-length data messages.

Alexandre Julliard julliard at wine.codeweavers.com
Fri Jul 13 08:30:21 CDT 2007


Module: wine
Branch: master
Commit: afaba37ed71ba3d4fc15cb7fb0d47f8ba869da00
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=afaba37ed71ba3d4fc15cb7fb0d47f8ba869da00

Author: Juan Lang <juan.lang at 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: */




More information about the wine-cvs mailing list