NT4/private/windows/base/advapi/digsig/digsig/pkcs7.cpp
2020-09-30 17:12:29 +02:00

1379 lines
33 KiB
C++

//
// pkcs7.cpp
//
// Implementation of pkcs7 reader / writer
//
#include "stdpch.h"
#include "common.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// Remove this when it goes into imagehlp.h
#ifndef CERT_PE_IMAGE_DIGEST_IAT
#define CERT_PE_IMAGE_DIGEST_IAT 0x04
#endif
#define CERT_PE_IMAGE_DIGEST_ALL (CERT_PE_IMAGE_DIGEST_DEBUG_INFO|CERT_PE_IMAGE_DIGEST_RESOURCES|CERT_PE_IMAGE_DIGEST_IAT)
/////////////////////////////////////////////////////////////////////////////
BOOL DIGSIGAPI CreatePkcs7SignedData(IUnknown* punkOuter, REFIID iid, LPVOID*ppv)
{
HRESULT hr = CPkcs7::CreateInstance(punkOuter, iid, ppv);
if (hr != S_OK)
{
SetLastError(Win32FromHResult(hr));
return FALSE;
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CPkcs7::Init(void)
{
HRESULT hr = S_OK;
m_pworld = new OSSWORLD; if (m_pworld == NULL) { return E_OUTOFMEMORY; }
hr = CPersistGlue::Init(m_punkOuter, this); if (hr != S_OK) return hr;
m_pSignedData = (SignedData*)m_pworld->Alloc(sizeof(SignedData));
if (m_pSignedData== NULL) { return E_OUTOFMEMORY; }
m_pworld->Init(*m_pSignedData);
return S_OK;
}
void CPkcs7::Free()
{
FreeToData();
CPersistGlue::Free();
if (m_pworld)
{
delete m_pworld;
m_pworld = NULL;
}
}
void CPkcs7::FreeToData()
// Free our state that we use to manipulate our loaded data,
// and free our data itself.
{
if (m_pSignedData)
{
m_pworld->Free(m_pSignedData);
m_pSignedData = NULL;
}
m_isDirty = FALSE;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CPkcs7::GetClassID(CLSID* pclsid)
{
*pclsid = CLSID_Pkcs7SignedData;
return S_OK;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CPkcs7::put_ContentInfo(PKCS7_CONTENTINFO* pinfo)
// Set the content info from its DER encoding. Note that the data
// itself may in fact be omitted.
{
HRESULT hr = S_OK;
ASSERT(m_pSignedData);
ContentInfo& info = m_pSignedData->contentInfo;
m_pworld->Free(info);
m_pworld->Assign(info.contentType, pinfo->pidContentType);
if (pinfo->data.pBlobData)
{
info.bit_mask |= content_present;
if (m_pworld->Copy(info.content, pinfo->data.cbSize, pinfo->data.pBlobData))
;
else
hr = E_OUTOFMEMORY;
}
#ifdef _DEBUG
if (hr == S_OK)
{
PKCS7_CONTENTINFO newInfo;
GOOD(get_ContentInfo(&newInfo));
ObjectID id1, id2;
m_pworld->Assign(id1, pinfo->pidContentType);
m_pworld->Assign(id2, newInfo.pidContentType);
ASSERT(id1 == id2);
ASSERT(IsEqual(pinfo->data, newInfo.data));
FreeTaskMem(newInfo);
}
#endif
return hr;
}
HRESULT CPkcs7::get_ContentInfo(PKCS7_CONTENTINFO* pinfo)
// Return the DER encoding of the content data. Remember that the
// data may not in fact be there.
{
HRESULT hr = S_OK;
ASSERT(m_pSignedData);
m_pworld->Init(pinfo->data);
ContentInfo& info = m_pSignedData->contentInfo;
pinfo->pidContentType = (OSIOBJECTID*)CoTaskMemAlloc(sizeof(ObjectID));
if (pinfo->pidContentType)
{
*(ObjectID*)pinfo->pidContentType = info.contentType;
if ((info.bit_mask & content_present) && info.content.encoded)
{
if (!CopyToTaskMem(&pinfo->data, info.content.length, info.content.encoded))
{
CoTaskMemFree(pinfo->pidContentType);
pinfo->pidContentType = NULL;
hr = E_OUTOFMEMORY;
}
}
}
else
hr = E_OUTOFMEMORY;
return hr;
}
void CPkcs7::GetContentType(ObjectID& id)
{
ASSERT(m_pSignedData);
id = m_pSignedData->contentInfo.contentType;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CPkcs7::put_IndirectDataContent(OSIOBJECTID* pidToUse, BLOB* pBlob, DIGESTINFO* pDigest)
{
ObjectID id;
m_pworld->Assign(id, pidToUse);
return put_IndirectDataContent(id, *pBlob, *pDigest);
}
HRESULT CPkcs7::put_IndirectDataContent(ObjectID& idToUse, BLOB& blob, DIGESTINFO& digest)
// Inverse to succeeding function
{
HRESULT hr = S_OK;
IndirectDataContent* pindirect = (IndirectDataContent*)m_pworld->Alloc(sizeof(IndirectDataContent));
if (pindirect)
{
m_pworld->Init(*pindirect);
hr = m_pworld->Assign(pindirect->messageDigest, digest);
if (hr == S_OK)
{
pindirect->data.type = idToUse;
pindirect->data.value.length = blob.cbSize;
pindirect->data.value.encoded = blob.pBlobData;
pindirect->data.bit_mask = blob.pBlobData ? value_present : 0;
BLOB b;
if (0 == m_pworld->Encode(IndirectDataContent_PDU, pindirect, &b))
{
PKCS7_CONTENTINFO content;
content.pidContentType = (OSIOBJECTID*)&id_indirectdata;
content.data = b;
hr = put_ContentInfo(&content);
m_pworld->Free(b);
}
else
hr = DIGSIG_E_ENCODE;
m_pworld->Init(pindirect->data.value); // so we don't free it below
}
m_pworld->Free(pindirect);
}
#ifdef _DEBUG
if (hr == S_OK)
{
BLOB b;
DIGESTINFO d;
GOOD(get_IndirectDataContent(idToUse, !!blob.pBlobData, b, d));
ASSERT(IsEqual(b, blob));
ASSERT(memcmp(&d, &digest, sizeof(d)) == 0);
FreeTaskMem(b);
}
#endif
return hr;
}
HRESULT CPkcs7::get_IndirectDataContent(OSIOBJECTID* pid, BOOL fValueNeeded, BLOB* pBlob, DIGESTINFO* pDigest)
{
ObjectID id;
m_pworld->Assign(id, pid);
return get_IndirectDataContent(id, fValueNeeded, *pBlob, *pDigest);
}
HRESULT CPkcs7::get_IndirectDataContent(ObjectID& idNeeded, BOOL fValueNeeded, BLOB& blob, DIGESTINFO& digest)
// Load the content info of this SignedData. If it is an IndirectData,
// then return the digest in digest and the inner content (the value of the ATAV)
// in blob. However, fail if the type of the inner content isn't idNeeded.
//
// E_FAIL is returned if all went well but for the fact that this SignedData doesn't
// have the IndirectDataContent inside that we want.
//
// Allocator used here is the task allocator.
{
PKCS7_CONTENTINFO content;
m_pworld->Init(blob);
HRESULT hr = get_ContentInfo(&content);
if (hr == S_OK)
{
ObjectID id;
m_pworld->Assign(id, content.pidContentType);
if (id == id_indirectdata && content.data.pBlobData)
{
IndirectDataContent* pindirect;
if (0 == m_pworld->Decode(IndirectDataContent_PDU, &content.data, (LPVOID*)&pindirect))
{
hr = m_pworld->Assign(digest, pindirect->messageDigest);
if (hr == S_OK)
{
if (idNeeded == pindirect->data.type)
{
// the value is optional
if (pindirect->data.bit_mask & value_present)
{
OpenType& ot = pindirect->data.value;
if (CopyToTaskMem(&blob, ot.length, ot.encoded))
{
hr = S_OK;
}
else
hr = E_OUTOFMEMORY;
}
else
{
hr = fValueNeeded ? E_FAIL : S_OK;
}
}
else
hr = E_FAIL;
}
m_pworld->Free(pindirect);
}
else
hr = DIGSIG_E_DECODE;
}
else
hr = E_FAIL;
FreeTaskMem(content);
}
if (hr != S_OK)
{
FreeTaskMem(blob);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////////
HRESULT CPkcs7::get_ContentRawFile(PKCS7_FILEDATA* pdata)
// The content had better be a raw file. Decode it and return it.
{
return get_ContentFile(id_indirectdata_rawFile, pdata);
}
HRESULT LinkToCERT_LINKTaskMem(Link* plinkIn, CERT_LINK* plinkOut)
// Convert between link representations. Notice that that allocator used is the
// task allocator.
{
HRESULT hr = S_OK;
CERT_LINK& link = *plinkOut;
Zero(link);
switch (plinkIn->choice)
{
case file_chosen: {
link.tag = CERT_LINK_TYPE_FILE;
if (!(link.wszFile = CopyToTaskMem(plinkIn->u.file)))
hr = E_OUTOFMEMORY;
break;
}
case url_chosen: {
link.tag = CERT_LINK_TYPE_URL;
if (!(link.wszUrl = CopyToTaskMem(plinkIn->u.url)))
hr = E_OUTOFMEMORY;
break;
}
case moniker_chosen: {
SerializedMoniker& moniker = plinkIn->u.moniker;
link.tag = CERT_LINK_TYPE_MONIKER;
link.clsidMoniker = *(GUID*)&moniker.classid.value;
if (!CopyToTaskMem(&link.blobMoniker, moniker.serializedData.length, moniker.serializedData.value))
hr = E_OUTOFMEMORY;
if (moniker.codeLocation)
{
//
// There's a place to get the code from if you need it. Dig that out
//
link.plinkCodeLocation = (CERT_LINK*)CoTaskMemAlloc(sizeof(CERT_LINK));
if (link.plinkCodeLocation)
{
hr = LinkToCERT_LINKTaskMem(moniker.codeLocation, link.plinkCodeLocation);
}
else
hr = E_OUTOFMEMORY;
}
break;
}
default:
NOTREACHED();
hr = E_UNEXPECTED;
}
if (hr!=S_OK)
{
FreeTaskMem(*plinkOut);
}
return hr;
}
HRESULT CPkcs7::LinkToFileData(Link* plink, PKCS7_FILEDATA* pdata)
// Notice that the allocator is the task allocator
{
return LinkToCERT_LINKTaskMem(plink, &pdata->link);
}
HRESULT CPkcs7::get_ContentFile(ObjectID& id, PKCS7_FILEDATA* pdata)
{
m_pworld->Init(*pdata);
BLOB b;
HRESULT hr = get_IndirectDataContent(id, FALSE, b, pdata->digest);
if (hr == S_OK)
{
if (b.pBlobData)
{
Link* plink = NULL;
if (0 == m_pworld->Decode(Link_PDU, &b, (LPVOID*)&plink))
{
hr = LinkToFileData(plink, pdata);
m_pworld->Free(plink);
}
else
hr = DIGSIG_E_DECODE;
FreeTaskMem(b);
}
else
{
pdata->link.tag = CERT_LINK_TYPE_NONE;
}
}
return hr;
}
///////////////////////////////////////////////////////////////
HRESULT CPkcs7::put_ContentRawFile(PKCS7_FILEDATA* pdata)
// Set the content info from the indicated raw file data
{
return put_ContentFile(id_indirectdata_rawFile, pdata);
}
HRESULT CERT_LINKToLink(OSSWORLD* pworld, CERT_LINK& certlink, Link& link)
{
HRESULT hr = S_OK;
pworld->Init(link);
switch(certlink.tag)
{
case CERT_LINK_TYPE_URL: {
link.choice = url_chosen;
if (!(link.u.url = pworld->ToIA5String(certlink.wszUrl)))
hr = E_OUTOFMEMORY;
break;
}
case CERT_LINK_TYPE_FILE: {
link.choice = file_chosen;
if (!pworld->Assign(link.u.file, certlink.wszFile))
hr = E_OUTOFMEMORY;
break;
}
case CERT_LINK_TYPE_MONIKER: {
link.choice = moniker_chosen;
SerializedMoniker& moniker = link.u.moniker;
moniker.classid.length = sizeof(GUID);
memcpy(&moniker.classid.value, &certlink.clsidMoniker, sizeof(GUID));
moniker.serializedData.value = (BYTE*)pworld->Copy(&certlink.blobMoniker);
if (moniker.serializedData.value)
{
moniker.serializedData.length = certlink.blobMoniker.cbSize;
}
else
hr = E_OUTOFMEMORY;
break;
}
default:
NOTREACHED();
hr = E_UNEXPECTED;
}
return hr;
}
HRESULT CPkcs7::FileDataToLink(PKCS7_FILEDATA* pdata, Link* plink)
{
return CERT_LINKToLink(m_pworld, pdata->link, *plink);
}
HRESULT CPkcs7::put_ContentFile(ObjectID& id, PKCS7_FILEDATA* pdata)
{
HRESULT hr = S_OK;
BLOB b;
m_pworld->Init(b);
if (pdata->link.tag != CERT_LINK_TYPE_NONE)
{
Link* plink = (Link*)m_pworld->Alloc(sizeof(Link));
if (plink)
hr = FileDataToLink(pdata, plink);
else
hr = E_OUTOFMEMORY;
if (hr == S_OK)
{
if (0 == m_pworld->Encode(Link_PDU, plink, &b))
hr = S_OK;
else
hr = DIGSIG_E_ENCODE;
m_pworld->Free(plink);
}
}
else // CERT_LINK_TYPE_NONE
{
//
// Leave the value in the IndirectDataContent empty
//
hr = S_OK;
}
if (hr == S_OK)
hr = put_IndirectDataContent(id, b, pdata->digest);
m_pworld->Free(b);
return hr;
}
///////////////////////////////////////////////////////////////////////////////////
//
// Helper functionality that supports us having externally pluggable modules that
// simply know a) how to hash themselves, and b) how to save and load a signature blob.
// We do the rest here.
//
HRESULT CPkcs7::HashAndSetSignableDocument(ISignableDocument* pdoc, HCRYPTPROV hprov, ALG_ID algidHash)
{
HRESULT hr = S_OK;
PKCS7_FILEDATA rf;
//
// Get the info as to where the data lives
//
m_pworld->Init(rf);
hr = pdoc->get_DataLocation(&rf.link);
//
// Hash the data into rf.digest
//
if (hr==S_OK)
{
rf.digest.algid = algidHash;
BOOL fUs = FALSE;
//
// Get a provider if we weren't given one
//
if (hprov == NULL)
{
hr = DefaultHasher(&hprov, algidHash);
if (hr==S_OK)
fUs = TRUE;
}
if (hr==S_OK)
{
HCRYPTHASH hash;
if (CryptCreateHash(hprov, algidHash, 0, 0, &hash))
{
//
// Actually hash the data
//
hr = pdoc->Hash(hash);
//
// Get the hash data from CAPI
//
if (hr == S_OK)
hr = GetHashData(hash, rf.digest);
VERIFY(CryptDestroyHash(hash));
}
}
if (fUs)
VERIFY(CryptReleaseContext(hprov,0));
}
//
// Store that as our indirect data content
//
if (hr==S_OK)
{
OSIOBJECTID* pid;
hr = pdoc->get_DataIdentifier(&pid);
if (hr==S_OK)
{
ObjectID id;
m_pworld->Assign(id, pid);
hr = put_ContentFile(id, &rf);
FreeTaskMem(pid);
}
}
FreeTaskMem(rf.link);
#ifdef _DEBUG
if (hr==S_OK)
{
GOOD(VerifySignableDocument(pdoc, hprov, algidHash));
GOOD(VerifySignableDocument(pdoc, hprov, 0));
GOOD(VerifySignableDocument(pdoc, NULL, 0));
}
#endif
return hr;
}
HRESULT CPkcs7::VerifySignableDocument(ISignableDocument* pdoc, HCRYPTPROV hprov, ALG_ID algidHash)
//
// Return values:
//
// S_OK It all matched
//
// E_FAIL Content isn't an IndirectDataContent of the right type
//
// NTE_BAD_SIGNATURE The hash did not match
//
// other...
//
{
//
// Find out what type of indirect data is expected
//
OSIOBJECTID* pid;
HRESULT hr = pdoc->get_DataIdentifier(&pid);
if (hr==S_OK)
{
ObjectID id;
m_pworld->Assign(id, pid);
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
//
// Get the indirect data if it's of that type
//
HRESULT hr = get_ContentFile(id, &rf);
if (hr == S_OK)
{
//
// Hash the data that's there presently
//
DIGESTINFO digest;
if (algidHash == 0)
{
// Take algid from data if caller doesn't provide it
algidHash = rf.digest.algid;
}
digest.algid = algidHash;
BOOL fUs = FALSE;
//
// Get a provider if we weren't given one
//
if (hprov == NULL)
{
hr = DefaultHasher(&hprov, algidHash);
if (hr==S_OK)
fUs = TRUE;
}
if (hr==S_OK)
{
HCRYPTHASH hash;
if (CryptCreateHash(hprov, algidHash, 0, 0, &hash))
{
//
// Actually hash the data
//
hr = pdoc->Hash(hash);
//
// Get the hash data from CAPI
//
if (hr == S_OK)
hr = GetHashData(hash, digest);
VERIFY(CryptDestroyHash(hash));
}
}
if (fUs)
VERIFY(CryptReleaseContext(hprov,0));
//
// Compare the stored hash to the present hash
//
if (hr == S_OK)
{
if (IsEqual(digest, rf.digest))
hr = S_OK;
else
hr = NTE_BAD_SIGNATURE;
}
FreeTaskMem(rf);
}
else
{
//
// Wrong type or missing indirect data
//
hr = E_FAIL;
}
FreeTaskMem(pid);
}
return hr;
}
/////////////////////////////////////////////////////////////////////////////
HRESULT CPkcs7::HashAndSetRawFile(HANDLE hFile, LPCWSTR wszFile, HCRYPTPROV hprov, ALG_ID algidHash)
// Set the content to be, indirectly, the indicated raw file, using the indicated
// hash algorithm.
//
{
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
if (wszFile)
{
rf.link.tag = CERT_LINK_TYPE_FILE;
rf.link.wszFile = (LPWSTR)wszFile;
}
else
rf.link.tag = CERT_LINK_TYPE_NONE;
HRESULT hr = S_OK;
if (hFile != INVALID_HANDLE_VALUE)
hr = HashFile(hFile, hprov, algidHash, rf.digest);
else
hr = HashFile(wszFile, hprov, algidHash, rf.digest);
if (hr == S_OK)
{
hr = put_ContentRawFile(&rf);
}
#ifdef _DEBUG
if (hr == S_OK)
{
GOOD(VerifyRawFile(hFile, wszFile, hprov, algidHash));
}
#endif
return hr;
}
HRESULT CPkcs7::VerifyRawFile(HANDLE hFile, LPCWSTR wszFile, HCRYPTPROV hprov, ALG_ID algidHash)
// The twin to HashAndSetRawFile.
//
// Verify that a) this SignedData contains
// an IndirectDataContent of type 'raw file', and b) the hash found inside
// said IndirectDataContent matches the hash of the file indicated here as
// EITHER wszFileName OR hFile.
//
// Note that we do NOT compare the link info in the IndirectDataContent
// to wszFileName in any way. Note also that this does not do signature
// checking; that instead is done on SignerInfos found in this SignedData.
//
// So, the actual verification process is two step: a) verify that the hash of the
// current file is what is expected (call this function); and b) verify that
// the signature holds (call IAmSigned::Verify in a SignerInfo).
//
//
// Return values:
//
// S_OK It all matched
//
// E_FAIL Content isn't an IndirectDataContent of type raw file
//
// NTE_BAD_SIGNATURE
// The hash did not match
{
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
HRESULT hr = get_ContentRawFile(&rf);
if (hr == S_OK)
{
if (algidHash == 0)
{
algidHash = rf.digest.algid;
}
DIGESTINFO digest;
if (hFile != INVALID_HANDLE_VALUE)
hr = HashFile(hFile, hprov, algidHash, digest);
else
hr = HashFile(wszFile, hprov, algidHash, digest);
if (hr == S_OK)
{
if (IsEqual(digest, rf.digest))
hr = S_OK;
else
hr = NTE_BAD_SIGNATURE;
}
FreeTaskMem(rf);
}
else
hr = E_FAIL;
return hr;
}
///////////////////////////////////////////////
//
// Java class file content functions
HRESULT CPkcs7::put_ContentJavaClassFile(PKCS7_FILEDATA* pdata)
// Set the content info from the indicated java class file data
{
return put_ContentFile(id_indirectdata_javaClassFile, pdata);
}
HRESULT CPkcs7::get_ContentJavaClassFile(PKCS7_FILEDATA* pdata)
// The content had better be a java class file. Decode it and return it.
{
return get_ContentFile(id_indirectdata_javaClassFile, pdata);
}
HRESULT CPkcs7::VerifyJavaClassFile(HANDLE hFile, LPCWSTR wszFile, HCRYPTPROV hprov, ALG_ID algidHash)
{
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
HRESULT hr = get_ContentJavaClassFile(&rf);
if (hr == S_OK)
{
if (algidHash == 0)
{
algidHash = rf.digest.algid;
}
DIGESTINFO digest;
hr = HashJavaClassFile(hFile, wszFile, hprov, algidHash, digest);
if (hr == S_OK)
{
if (IsEqual(digest, rf.digest))
hr = S_OK;
else
hr = NTE_BAD_SIGNATURE;
}
FreeTaskMem(rf);
}
else
hr = E_FAIL;
return hr;
}
HRESULT CPkcs7::HashAndSetJavaClassFile(HANDLE hFile, LPCWSTR wszFile, HCRYPTPROV hprov, ALG_ID algidHash)
{
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
if (wszFile)
{
rf.link.tag = CERT_LINK_TYPE_FILE;
rf.link.wszFile = (LPWSTR)wszFile;
}
else
rf.link.tag = CERT_LINK_TYPE_NONE;
HRESULT hr = HashJavaClassFile(hFile, wszFile, hprov, algidHash, rf.digest);
if (hr == S_OK)
{
hr = put_ContentJavaClassFile(&rf);
}
#ifdef _DEBUG
if (hr == S_OK)
{
GOOD(VerifyJavaClassFile(hFile, wszFile, hprov, algidHash));
}
#endif
return hr;
}
HRESULT CPkcs7::SaveIntoJavaClassFile(FILEHANDLE hFile,LPCOLESTR wszFileName,BOOL fClearDirty)
// Save this signed data (presumably already signed) into the right place in the class file
{
HRESULT hr = S_OK;
CFileStream stm;
if (stm.OpenFileForWriting(hFile, wszFileName, FALSE))
{
IPersistStream* pPerStream;
hr = CJavaClassFile::CreateInstance(NULL, IID_IPersistStream, (LPVOID*)&pPerStream);
if (hr == S_OK)
{
hr = pPerStream->Load(&stm); // load the class file
if (hr == S_OK)
{
BLOB b;
hr = CPersistMemoryHelper2::Save(&b, FALSE); // get our bits
if (hr == S_OK)
{
IInsertSig* pSig;
hr = pPerStream->QueryInterface(IID_IInsertSig, (LPVOID*)&pSig);
if (hr == S_OK)
{
hr = pSig->SaveSig(&b); // stuff our bits in
pSig->Release();
}
FreeTaskMem(b);
}
if (hr == S_OK)
{
stm.Reset();
stm.Truncate();
hr = pPerStream->Save(&stm, FALSE); // save the class file
}
}
pPerStream->Release();
}
}
else
hr = HError();
if (hr == S_OK && fClearDirty)
m_isDirty = FALSE;
return hr;
}
HRESULT CPkcs7::LoadFromJavaClassFile(FILEHANDLE hFile,LPCOLESTR wszFileName)
// Load this signed data from this Java class file
{
HRESULT hr = S_OK;
CFileStream stm;
if (stm.OpenFileForReading(hFile, wszFileName))
{
IPersistStream* pPerStream;
hr = CJavaClassFile::CreateInstance(NULL, IID_IPersistStream, (LPVOID*)&pPerStream);
if (hr == S_OK)
{
hr = pPerStream->Load(&stm); // load the class file
if (hr == S_OK)
{
IInsertSig* pSig;
hr = pPerStream->QueryInterface(IID_IInsertSig, (LPVOID*)&pSig);
if (hr == S_OK)
{
BLOB b;
hr = pSig->LoadSig(&b); // get our bits back out
if (hr == S_OK)
{
hr = CPersistMemoryHelper2::Load(&b); // load our bits
FreeTaskMem(b);
}
pSig->Release();
}
}
pPerStream->Release();
}
}
else
hr = HError();
return hr;
}
///////////////////////////////////////////////
//
// Storage content functions
HRESULT CPkcs7::put_ContentStructuredStorage(PKCS7_FILEDATA* pdata)
// Set the content info from the indicated raw file data
{
return put_ContentFile(id_indirectdata_structuredStorage, pdata);
}
HRESULT CPkcs7::get_ContentStructuredStorage(PKCS7_FILEDATA* pdata)
// Set the content info from the indicated raw file data
{
return get_ContentFile(id_indirectdata_structuredStorage, pdata);
}
HRESULT CPkcs7::HashAndSetStorage(IStorage* pstg, HCRYPTPROV hprov, ALG_ID algidHash)
// Set the content to be the indicated raw file, using the indicated hash algorithm
{
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
rf.link.tag = CERT_LINK_TYPE_NONE;
HRESULT hr = HashStorage(m_pworld, pstg, hprov, algidHash, rf.digest);
if (hr == S_OK)
{
hr = put_ContentStructuredStorage(&rf);
}
#ifdef _DEBUG
if (hr == S_OK)
{
GOOD(VerifyStorage(pstg, hprov, algidHash));
}
#endif
return hr;
}
HRESULT CPkcs7::VerifyStorage(IStorage* pstg, HCRYPTPROV hprov, ALG_ID algidHash)
{
PKCS7_FILEDATA rf;
m_pworld->Init(rf);
HRESULT hr = get_ContentStructuredStorage(&rf);
if (hr == S_OK)
{
if (algidHash == 0)
{
algidHash = rf.digest.algid;
}
DIGESTINFO digest;
hr = HashStorage(m_pworld, pstg, hprov, algidHash, digest);
if (hr == S_OK)
{
if (IsEqual(digest, rf.digest))
hr = S_OK;
else
hr = NTE_BAD_SIGNATURE;
}
FreeTaskMem(rf);
}
else
hr = E_FAIL;
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// PE image content functions
HRESULT CPkcs7::put_ContentImageFile(PKCS7_IMAGEFILEDATA* pdata)
// Set the content info to be an indirect data content pointing
// to the indicated executable.
{
HRESULT hr = S_OK;
PeImageData *pimage = (PeImageData*)m_pworld->Alloc(sizeof(PeImageData));
if (pimage)
{
m_pworld->Init(*pimage);
if (pdata->file.link.tag != CERT_LINK_TYPE_NONE)
{
hr = FileDataToLink(&pdata->file, &pimage->file);
pimage->bit_mask |= file_present;
}
else
pimage->bit_mask &= ~file_present;
pimage->bit_mask |= flags_present;
if (pdata->dwDigestLevel & CERT_PE_IMAGE_DIGEST_RESOURCES)
pimage->flags |= includeResources;
if (pdata->dwDigestLevel & CERT_PE_IMAGE_DIGEST_DEBUG_INFO)
pimage->flags |= includeDebugInfo;
if (pdata->dwDigestLevel & CERT_PE_IMAGE_DIGEST_IAT)
pimage->flags |= includeImportAddressTable;
if (pdata->dwDigestLevel & ~(CERT_PE_IMAGE_DIGEST_ALL))
hr = E_INVALIDARG;
}
else
hr = E_OUTOFMEMORY;
BLOB b;
m_pworld->Init(b);
if (hr == S_OK)
{
if (0 == m_pworld->Encode(PeImageData_PDU, pimage, &b))
hr = S_OK;
else
hr = DIGSIG_E_ENCODE;
}
m_pworld->Free(pimage);
if (hr == S_OK)
hr = put_IndirectDataContent(id_indirectdata_peImage, b, pdata->file.digest);
m_pworld->Free(b);
return hr;
}
HRESULT CPkcs7::get_ContentImageFile(PKCS7_IMAGEFILEDATA* pdata, BOOL fWantFileInfo)
// If the content info is an indirect data content pointing to a file,
// retrieve the relavent info
{
m_pworld->Init(*pdata);
BLOB b;
HRESULT hr = get_IndirectDataContent(id_indirectdata_peImage, FALSE, b, pdata->file.digest);
if (hr == S_OK)
{
if (b.pBlobData)
{
PeImageData* pimage;
if (0 == m_pworld->Decode(PeImageData_PDU, &b, (LPVOID*)&pimage))
{
if ((pimage->bit_mask & file_present) && fWantFileInfo)
hr = LinkToFileData(&pimage->file, &pdata->file);
else
pdata->file.link.tag = CERT_LINK_TYPE_NONE;
if (pimage->bit_mask & flags_present)
{
if (pimage->flags & includeResources)
pdata->dwDigestLevel |= CERT_PE_IMAGE_DIGEST_RESOURCES;
if (pimage->flags & includeDebugInfo)
pdata->dwDigestLevel |= CERT_PE_IMAGE_DIGEST_DEBUG_INFO;
if (pimage->flags & includeImportAddressTable)
pdata->dwDigestLevel |= CERT_PE_IMAGE_DIGEST_IAT;
}
else
pdata->dwDigestLevel |= CERT_PE_IMAGE_DIGEST_RESOURCES; // this is the default in the ASN
m_pworld->Free(pimage);
}
else
hr = DIGSIG_E_DECODE;
FreeTaskMem(b);
}
else
hr = DIGSIG_E_DECODE;
}
return hr;
}
HRESULT CPkcs7::HashAndSetImageFile(DWORD dwDigestLevel, HANDLE hFile,
LPCOLESTR wszFile, HCRYPTPROV hprov, ALG_ID algidHash)
// Set the content to be, indirectly, the indicated raw file, using the indicated
// hash algorithm.
//
{
PKCS7_IMAGEFILEDATA fd;
m_pworld->Init(fd);
fd.dwDigestLevel = dwDigestLevel;
fd.file.link.tag = CERT_LINK_TYPE_FILE;
fd.file.link.wszFile = (LPWSTR)wszFile;
HRESULT hr = S_OK;
if (hFile != INVALID_HANDLE_VALUE)
hr = HashImageFile(dwDigestLevel, hFile, hprov, algidHash, fd.file.digest);
else
hr = HashImageFile(dwDigestLevel, wszFile, hprov, algidHash, fd.file.digest);
if (hr == S_OK)
hr = put_ContentImageFile(&fd);
#ifdef _DEBUG
if (hr == S_OK)
{
GOOD(VerifyImageFile(dwDigestLevel, hFile, wszFile, hprov, algidHash));
}
#endif
return hr;
}
HRESULT CPkcs7::VerifyImageFile(DWORD dwDigestLevel, HANDLE hFile,
LPCOLESTR wszFile, HCRYPTPROV hprov, ALG_ID algidHash)
// The twin to HashAndSetImageFile. Verify that a) this SignedData contains
// an IndirectDataContent of type 'image file', and b) the hash found inside
// said IndirectDataContent matches the hash of the file indicated here as
// wszFile.
//
// Return values:
//
// S_OK It all matched
//
// E_FAIL Content isn't an IndirectDataContent of type image file
//
// NTE_BAD_SIGNATURE
// The hash did not match
{
PKCS7_IMAGEFILEDATA fd;
m_pworld->Init(fd);
HRESULT hr = get_ContentImageFile(&fd, TRUE);
if (hr == S_OK)
{
if (algidHash == 0)
{
algidHash = fd.file.digest.algid;
}
//
// If the user says 'verify according to what's there already' then
// use what's there already
//
if (dwDigestLevel == 0)
{
dwDigestLevel = fd.dwDigestLevel;
}
DIGESTINFO digest;
if (hFile != INVALID_HANDLE_VALUE)
hr = HashImageFile(dwDigestLevel, hFile, hprov, algidHash, digest);
else
hr = HashImageFile(dwDigestLevel, wszFile, hprov, algidHash, digest);
if (hr == S_OK)
{
if (IsEqual(digest, fd.file.digest))
hr = S_OK;
else
hr = NTE_BAD_SIGNATURE;
}
FreeTaskMem(fd.file);
}
else
hr = E_FAIL;
return hr;
}
/////////////////////////////////////////////////////////////////////////////
//
// SignerInfo and Certificate management
//
HRESULT CPkcs7::get_SignerInfoCount(LONG* pcinfo)
// Answer the number of signer infos that we presently have in this SignedData
{
*pcinfo = 0;
SignerInfos plink = m_pSignedData->signerInfos;
while (plink)
{
*pcinfo += 1;
plink = plink->next;
}
return S_OK;
}
HRESULT CPkcs7::get_CertificateCount(LONG* pccert)
{
*pccert = 0;
ExtendedCertificatesAndCertificates plink = m_pSignedData->certificates;
while (plink)
{
// Ignore the certificates which are PKCS #6 certificates
// I.e.: only count the X.509 certificates
if (plink->value.choice == certificate_chosen)
{
*pccert += 1;
}
plink = plink->next;
}
return S_OK;
}
////////////////////////////////////////
HRESULT CPkcs7::get_SignerInfo(LONG iInfo, REFIID iid, void** ppv)
// Return access to the nth signer info in this SignedData
{
*ppv = NULL;
int count = 0;
SignerInfos plink = m_pSignedData->signerInfos;
while (plink)
{
if (count == iInfo || (iInfo == -1 && plink->next == NULL))
{
return CSignerInfo::CreateInstance(NULL, &plink->value, this, iid, ppv);
}
count++;
plink = plink->next;
}
return E_INVALIDARG;
}
HRESULT CPkcs7::get_Certificate(LONG iCert, REFIID iid, void** ppv)
{
*ppv = NULL;
int count = 0;
ExtendedCertificatesAndCertificates plink = m_pSignedData->certificates;
while (plink)
{
if (plink->value.choice == certificate_chosen)
{
if (count == iCert || (iCert == -1 && plink->next == NULL))
{
return CX509::CreateInstance(
NULL,
&plink->value.u.certificate,
AsUnk(this),
&m_cCertActive,
&m_isDirty,
iid,
ppv);
}
count++;
}
plink = plink->next;
}
return E_INVALIDARG;
}
////////////////////////////////////////
HRESULT CPkcs7::create_SignerInfo(LONG iInfoBefore, REFIID iid, void* * ppv)
// Create a new, empty signer info before the indicated index
// in our list, or at the end if iInfoBefore == -1
{
*ppv = NULL;
if (m_cSignerInfoActive)
{
return E_FAIL;
}
ASSERT(m_pSignedData); if (!m_pSignedData) return E_POINTER;
SignerInfos infosNew = (SignerInfos) m_pworld->Alloc(sizeof(SignerInfos_));
if (infosNew != NULL)
{
LONG cInfo = 0;
m_pworld->Init(*infosNew);
SignerInfos* pinfos = &m_pSignedData->signerInfos;
while ((iInfoBefore==-1 || cInfo<iInfoBefore) && *pinfos)
{
cInfo++;
pinfos = &((*pinfos)->next);
}
cInfo++;
infosNew->next = *pinfos;
*pinfos = infosNew;
HRESULT hr = get_SignerInfo(cInfo-1, iid, ppv);
if (hr != S_OK)
{
remove_SignerInfo(cInfo-1);
}
return hr;
}
return E_OUTOFMEMORY;
}
HRESULT CPkcs7::create_Certificate(LONG iCertBefore, REFIID iid, void* * ppv)
{
*ppv = NULL;
if (m_cCertActive)
{
return E_FAIL;
}
ASSERT(m_pSignedData); if (!m_pSignedData) return E_POINTER;
ExtendedCertificatesAndCertificates certsNew = (ExtendedCertificatesAndCertificates)
m_pworld->Alloc(sizeof(ExtendedCertificatesAndCertificates_));
if (certsNew != NULL)
{
LONG cCert = 0;
m_pworld->Init(*certsNew);
ExtendedCertificatesAndCertificates* pcerts = &m_pSignedData->certificates;
while ((iCertBefore==-1 || cCert<iCertBefore) && *pcerts)
{
if ((*pcerts)->value.choice == certificate_chosen)
{
cCert++;
}
pcerts = &((*pcerts)->next);
}
cCert++;
certsNew->next = *pcerts;
*pcerts = certsNew;
HRESULT hr = get_Certificate(cCert-1, iid, ppv);
if (hr != S_OK)
{
remove_Certificate(cCert-1);
}
return hr;
}
return E_OUTOFMEMORY;
}
////////////////////////////////////////
HRESULT CPkcs7::remove_SignerInfo(LONG iInfo)
// Remove the indicated signer info from the list
{
if (m_cSignerInfoActive)
{
return E_FAIL;
}
ASSERT(m_pSignedData); if (!m_pSignedData) return E_POINTER;
#ifdef _DEBUG
LONG cStart;
GOOD(get_SignerInfoCount(&cStart));
#endif
LONG count;
SignerInfos infos;
count = 0;
for (infos = (SignerInfos)(&m_pSignedData->signerInfos);
infos->next != NULL;
infos = infos->next)
{
SignerInfos infosNext = infos->next;
if (count == iInfo || (iInfo == -1 && infosNext->next == NULL))
{
infos->next = infosNext->next;
infosNext->next = NULL;
m_pworld->Free(infosNext);
#ifdef _DEBUG
{
LONG cEnd;
GOOD(get_SignerInfoCount(&cEnd));
VERIFY(cStart == cEnd + 1);
}
#endif
return S_OK;
}
count++;
}
return E_INVALIDARG;
}
HRESULT CPkcs7::remove_Certificate(LONG iCert)
// Remove the indicated certificate info from the list
{
if (m_cCertActive)
{
return E_FAIL;
}
ASSERT(m_pSignedData); if (!m_pSignedData) return E_POINTER;
#ifdef _DEBUG
LONG cStart;
GOOD(get_CertificateCount(&cStart));
#endif
LONG cCert;
ExtendedCertificatesAndCertificates plink;
cCert = 0;
for (plink = (ExtendedCertificatesAndCertificates)(&m_pSignedData->certificates);
plink->next != NULL;
plink = plink->next)
{
ExtendedCertificatesAndCertificates plinkNext = plink->next;
if (plinkNext->value.choice == certificate_chosen)
{
if (cCert == iCert || (iCert == -1 && plinkNext->next == NULL))
{
plink->next = plinkNext->next;
plinkNext->next = NULL;
m_pworld->Free(plinkNext);
#ifdef _DEBUG
{
LONG cEnd;
GOOD(get_CertificateCount(&cEnd));
VERIFY(cStart == cEnd + 1);
}
#endif
return S_OK;
}
cCert++;
}
}
return E_INVALIDARG;
}