crypt32(10/20): Hash and sign data when updating signed messages
Juan Lang
juan.lang at gmail.com
Mon Jul 23 20:29:44 CDT 2007
--Juan
-------------- next part --------------
From 5436f819826473c211b9611482be5c23d452777f Mon Sep 17 00:00:00 2001
From: Juan Lang <juanlang at juan.corp.google.com>
Date: Mon, 23 Jul 2007 17:56:12 -0700
Subject: [PATCH] Hash and sign data when updating signed messages
---
dlls/crypt32/msg.c | 239 +++++++++++++++++++++++++++++++++++++++++++++-
dlls/crypt32/tests/msg.c | 6 +
2 files changed, 239 insertions(+), 6 deletions(-)
diff --git a/dlls/crypt32/msg.c b/dlls/crypt32/msg.c
index ce2ac3d..28fb247 100644
--- a/dlls/crypt32/msg.c
+++ b/dlls/crypt32/msg.c
@@ -601,17 +601,165 @@ static BOOL CRYPT_IsValidSigner(CMSG_SIG
return TRUE;
}
+typedef struct _CSignerInfo
+{
+ HCRYPTPROV prov;
+ HCRYPTHASH hash;
+ HCRYPTKEY key;
+ CMSG_SIGNER_INFO info;
+} CSignerInfo;
+
+static BOOL CRYPT_CopyBlob(CRYPT_DATA_BLOB *out, const CRYPT_DATA_BLOB *in)
+{
+ BOOL ret = TRUE;
+
+ out->cbData = in->cbData;
+ if (out->cbData)
+ {
+ out->pbData = CryptMemAlloc(out->cbData);
+ if (out->pbData)
+ memcpy(out->pbData, in->pbData, out->cbData);
+ else
+ ret = FALSE;
+ }
+ else
+ out->pbData = NULL;
+ return ret;
+}
+
+static BOOL CRYPT_CopyAttribute(CRYPT_ATTRIBUTE *out, const CRYPT_ATTRIBUTE *in)
+{
+ BOOL ret = TRUE;
+
+ /* Assumption: algorithm IDs will point to static strings, not stack-based
+ * ones, so copying the pointer values is safe.
+ */
+ out->pszObjId = in->pszObjId;
+ out->cValue = in->cValue;
+ if (out->cValue)
+ {
+ out->rgValue = CryptMemAlloc(out->cValue * sizeof(CRYPT_DATA_BLOB));
+ if (out->rgValue)
+ {
+ DWORD i;
+
+ memset(out->rgValue, 0, out->cValue * sizeof(CRYPT_DATA_BLOB));
+ for (i = 0; ret && i < out->cValue; i++)
+ ret = CRYPT_CopyBlob(&out->rgValue[i], &in->rgValue[i]);
+ }
+ else
+ ret = FALSE;
+ }
+ return ret;
+}
+
+static BOOL CRYPT_CopyAttributes(CRYPT_ATTRIBUTES *out,
+ const CRYPT_ATTRIBUTES *in)
+{
+ BOOL ret = TRUE;
+
+ out->cAttr = in->cAttr;
+ if (out->cAttr)
+ {
+ out->rgAttr = CryptMemAlloc(out->cAttr * sizeof(CRYPT_ATTRIBUTE));
+ if (out->rgAttr)
+ {
+ DWORD i;
+
+ memset(out->rgAttr, 0, out->cAttr * sizeof(CRYPT_ATTRIBUTE));
+ for (i = 0; ret && i < out->cAttr; i++)
+ ret = CRYPT_CopyAttribute(&out->rgAttr[i], &in->rgAttr[i]);
+ }
+ else
+ ret = FALSE;
+ }
+ else
+ out->rgAttr = NULL;
+ return ret;
+}
+
+static BOOL CSignerInfo_Construct(CSignerInfo *out,
+ CMSG_SIGNER_ENCODE_INFO_WITH_CMS *in, DWORD open_flags)
+{
+ ALG_ID algID;
+ BOOL ret;
+
+ out->prov = in->hCryptProv;
+ if (!(open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG))
+ CryptContextAddRef(out->prov, NULL, 0);
+ algID = CertOIDToAlgId(in->HashAlgorithm.pszObjId);
+ ret = CryptCreateHash(out->prov, algID, 0, 0, &out->hash);
+ if (ret)
+ {
+ /* Note: needs to change if CMS fields are supported */
+ out->info.dwVersion = CMSG_SIGNER_INFO_V1;
+ ret = CRYPT_CopyBlob(&out->info.Issuer, &in->pCertInfo->Issuer);
+ if (ret)
+ ret = CRYPT_CopyBlob(&out->info.SerialNumber,
+ &in->pCertInfo->SerialNumber);
+ /* Assumption: algorithm IDs will point to static strings, not
+ * stack-based ones, so copying the pointer values is safe.
+ */
+ out->info.HashAlgorithm.pszObjId = in->HashAlgorithm.pszObjId;
+ if (ret)
+ ret = CRYPT_CopyBlob(&out->info.HashAlgorithm.Parameters,
+ &in->HashAlgorithm.Parameters);
+ memset(&out->info.HashEncryptionAlgorithm, 0,
+ sizeof(out->info.HashEncryptionAlgorithm));
+ if (ret)
+ ret = CRYPT_CopyAttributes(&out->info.AuthAttrs,
+ (CRYPT_ATTRIBUTES *)&in->cAuthAttr);
+ if (ret)
+ ret = CRYPT_CopyAttributes(&out->info.UnauthAttrs,
+ (CRYPT_ATTRIBUTES *)&in->cUnauthAttr);
+ }
+ return ret;
+}
+
+static void CSignerInfo_Free(CSignerInfo *signer)
+{
+ DWORD i, j;
+
+ CryptDestroyKey(signer->key);
+ CryptDestroyHash(signer->hash);
+ CryptReleaseContext(signer->prov, 0);
+ CryptMemFree(signer->info.Issuer.pbData);
+ CryptMemFree(signer->info.SerialNumber.pbData);
+ CryptMemFree(signer->info.HashAlgorithm.Parameters.pbData);
+ CryptMemFree(signer->info.EncryptedHash.pbData);
+ for (i = 0; i < signer->info.AuthAttrs.cAttr; i++)
+ {
+ for (j = 0; j < signer->info.AuthAttrs.rgAttr[i].cValue; j++)
+ CryptMemFree(signer->info.AuthAttrs.rgAttr[i].rgValue[j].pbData);
+ CryptMemFree(signer->info.AuthAttrs.rgAttr[i].rgValue);
+ }
+ CryptMemFree(signer->info.AuthAttrs.rgAttr);
+ for (i = 0; i < signer->info.UnauthAttrs.cAttr; i++)
+ {
+ for (j = 0; j < signer->info.UnauthAttrs.rgAttr[i].cValue; j++)
+ CryptMemFree(signer->info.UnauthAttrs.rgAttr[i].rgValue[j].pbData);
+ CryptMemFree(signer->info.UnauthAttrs.rgAttr[i].rgValue);
+ }
+ CryptMemFree(signer->info.UnauthAttrs.rgAttr);
+}
+
typedef struct _CSignedEncodeMsg
{
CryptMsgBase base;
CRYPT_DATA_BLOB data;
+ DWORD cSigners;
+ CSignerInfo *signers;
} CSignedEncodeMsg;
static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg)
{
CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
+ DWORD i;
CryptMemFree(msg->data.pbData);
+ for (i = 0; i < msg->cSigners; i++)
+ CSignerInfo_Free(&msg->signers[i]);
+ CryptMemFree(msg->signers);
}
static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
@@ -622,6 +770,62 @@ static BOOL CSignedEncodeMsg_GetParam(HC
return FALSE;
}
+static BOOL CSignedEncodeMsg_UpdateHash(CSignedEncodeMsg *msg,
+ const BYTE *pbData, DWORD cbData)
+{
+ DWORD i;
+ BOOL ret = TRUE;
+
+ TRACE("(%p, %p, %d)\n", msg, pbData, cbData);
+
+ for (i = 0; ret && i < msg->cSigners; i++)
+ ret = CryptHashData(msg->signers[i].hash, pbData, cbData, 0);
+ return ret;
+}
+
+static void CRYPT_ReverseBytes(CRYPT_HASH_BLOB *hash)
+{
+ DWORD i;
+ BYTE tmp;
+
+ for (i = 0; i < hash->cbData / 2; i++)
+ {
+ tmp = hash->pbData[hash->cbData - i - 1];
+ hash->pbData[hash->cbData - i - 1] = hash->pbData[i];
+ hash->pbData[i] = tmp;
+ }
+}
+
+static BOOL CSignedEncodeMsg_Sign(CSignedEncodeMsg *msg)
+{
+ DWORD i;
+ BOOL ret = TRUE;
+
+ TRACE("(%p)\n", msg);
+
+ for (i = 0; ret && i < msg->cSigners; i++)
+ {
+ ret = CryptSignHashW(msg->signers[i].hash, AT_SIGNATURE, NULL, 0, NULL,
+ &msg->signers[i].info.EncryptedHash.cbData);
+ if (ret)
+ {
+ msg->signers[i].info.EncryptedHash.pbData =
+ CryptMemAlloc(msg->signers[i].info.EncryptedHash.cbData);
+ if (msg->signers[i].info.EncryptedHash.pbData)
+ {
+ ret = CryptSignHashW(msg->signers[i].hash, AT_SIGNATURE, NULL,
+ 0, msg->signers[i].info.EncryptedHash.pbData,
+ &msg->signers[i].info.EncryptedHash.cbData);
+ if (ret)
+ CRYPT_ReverseBytes(&msg->signers[i].info.EncryptedHash);
+ }
+ else
+ ret = FALSE;
+ }
+ }
+ return ret;
+}
+
static BOOL CSignedEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
DWORD cbData, BOOL fFinal)
{
@@ -630,8 +834,12 @@ static BOOL CSignedEncodeMsg_Update(HCRY
if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
{
- FIXME("streamed / detached update unimplemented\n");
- ret = TRUE;
+ ret = CSignedEncodeMsg_UpdateHash(msg, pbData, cbData);
+ /* FIXME: hash authenticated attributes on final update */
+ if (ret && fFinal)
+ ret = CSignedEncodeMsg_Sign(msg);
+ if (msg->base.streamed)
+ FIXME("streamed partial stub\n");
}
else
{
@@ -652,7 +860,10 @@ static BOOL CSignedEncodeMsg_Update(HCRY
else
ret = TRUE;
if (ret)
- FIXME("non-streamed final update: partial stub\n");
+ ret = CSignedEncodeMsg_UpdateHash(msg, pbData, cbData);
+ /* FIXME: hash authenticated attributes */
+ if (ret)
+ ret = CSignedEncodeMsg_Sign(msg);
}
}
return ret;
@@ -684,11 +895,33 @@ static HCRYPTMSG CSignedEncodeMsg_Open(D
msg = CryptMemAlloc(sizeof(CSignedEncodeMsg));
if (msg)
{
+ BOOL ret = TRUE;
+
CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
CSignedEncodeMsg_Close, CSignedEncodeMsg_GetParam,
CSignedEncodeMsg_Update);
msg->data.cbData = 0;
msg->data.pbData = NULL;
+ msg->cSigners = 0;
+ if (info->cSigners)
+ {
+ msg->signers = CryptMemAlloc(info->cSigners * sizeof(CSignerInfo));
+ if (msg->signers)
+ {
+ msg->cSigners = info->cSigners;
+ memset(msg->signers, 0, msg->cSigners * sizeof(CSignerInfo));
+ for (i = 0; ret && i < msg->cSigners; i++)
+ ret = CSignerInfo_Construct(&msg->signers[i],
+ &info->rgSigners[i], dwFlags);
+ }
+ else
+ ret = FALSE;
+ }
+ if (!ret)
+ {
+ CSignedEncodeMsg_Close(msg);
+ msg = NULL;
+ }
}
return msg;
}
diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c
index 44c3098..f9f2ab6 100644
--- a/dlls/crypt32/tests/msg.c
+++ b/dlls/crypt32/tests/msg.c
@@ -1121,9 +1121,9 @@ static void test_signed_msg_update(void)
*/
SetLastError(0xdeadbeef);
ret = CryptMsgUpdate(msg, NULL, 0, TRUE);
- todo_wine
- ok(!ret && GetLastError() == NTE_BAD_KEYSET,
- "Expected NTE_BAD_KEYSET, got %x\n", GetLastError());
+ ok(!ret && (GetLastError() == NTE_BAD_KEYSET ||
+ GetLastError() == NTE_NO_KEY),
+ "Expected NTE_BAD_KEYSET or NTE_NO_KEY, got %x\n", GetLastError());
ret = CryptImportKey(signer.hCryptProv, (LPBYTE)privKey, sizeof(privKey),
0, 0, &key);
ok(ret, "CryptImportKey failed: %08x\n", GetLastError());
--
1.4.1
More information about the wine-patches
mailing list