xsec/tools/threadTest/threadtest.cpp (348 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * XSEC * * threadTest := Run up a number of threads signing and validating * the signatures. * * Author(s): Berin Lautenbach * * $Id$ * */ // XSEC #include <xsec/framework/XSECProvider.hpp> #include <xsec/dsig/DSIGSignature.hpp> #include <xsec/dsig/DSIGReference.hpp> #if defined (XSEC_HAVE_OPENSSL) # include <xsec/enc/OpenSSL/OpenSSLCryptoKeyHMAC.hpp> #else # if defined (XSEC_HAVE_WINCAPI) # include <xsec/enc/WinCAPI/WinCAPICryptoKeyHMAC.hpp> # else # error No crypto provider available # endif #endif #include "../../utils/XSECDOMUtils.hpp" #include <xercesc/util/PlatformUtils.hpp> #include <xercesc/dom/DOM.hpp> #include <xercesc/parsers/XercesDOMParser.hpp> #include <xercesc/util/XMLException.hpp> #include <xercesc/util/Mutexes.hpp> #include <xercesc/framework/StdOutFormatTarget.hpp> #include <xercesc/framework/MemBufFormatTarget.hpp> #include <xercesc/framework/MemBufInputSource.hpp> #include <strstream> #include <iostream> #include <queue> #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winbase.h> using std::endl; using std::cerr; using std::cout; using std::queue; using std::vector; using std::ostrstream; XERCES_CPP_NAMESPACE_USE // -------------------------------------------------------------------------------- // Globals used and read by all threads // -------------------------------------------------------------------------------- #define numThreads 7 #define secretKey "secret" #define sleepTime 30 typedef queue<char *> charQueueType; XSECProvider * g_provider; HANDLE g_toVerifyQueueSemaphore; HANDLE g_toSignQueueSemaphore; XMLMutex g_toVerifyQueueMutex; charQueueType g_toVerifyQueue; // Control markers XMLMutex g_initMutex; bool g_completed; int g_initVerifyCount; int g_initSignCount; unsigned int g_signCount[numThreads]; unsigned int g_verifyCount[numThreads]; unsigned int g_errors; // -------------------------------------------------------------------------------- // Document manipulation functions // -------------------------------------------------------------------------------- void outputDoc (DOMImplementation *impl, DOMDocument * doc) { XMLFormatTarget *formatTarget = new StdOutFormatTarget(); // Output a doc to stdout DOMLSSerializer *theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer(); // Get the config so we can set up pretty printing DOMConfiguration *dc = theSerializer->getDomConfig(); dc->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, true); // Now create an output object to format to UTF-8 DOMLSOutput *theOutput = ((DOMImplementationLS*)impl)->createLSOutput(); Janitor<DOMLSOutput> j_theOutput(theOutput); theOutput->setEncoding(MAKE_UNICODE_STRING("UTF-8")); theOutput->setByteStream(formatTarget); theSerializer->write(doc, theOutput); cout << endl; delete theSerializer; delete formatTarget; } void addDocToQueue (DOMImplementation *impl, DOMDocument * doc) { // Output a document to a memory buffer and add the buffer to // the queue MemBufFormatTarget *formatTarget = new MemBufFormatTarget(); // DOM L3 version as per Xerces 3.0 API DOMLSSerializer *theSerializer = ((DOMImplementationLS*)impl)->createLSSerializer(); // Get the config so we can set up pretty printing DOMConfiguration *dc = theSerializer->getDomConfig(); dc->setParameter(XMLUni::fgDOMWRTFormatPrettyPrint, false); // Now create an output object to format to UTF-8 DOMLSOutput *theOutput = ((DOMImplementationLS*)impl)->createLSOutput(); Janitor<DOMLSOutput> j_theOutput(theOutput); theOutput->setEncoding(MAKE_UNICODE_STRING("UTF-8")); theOutput->setByteStream(formatTarget); theSerializer->write(doc, theOutput); // Copy to a new buffer XMLSize_t len = formatTarget->getLen(); char * buf = new char [len + 1]; memcpy(buf, formatTarget->getRawBuffer(), len); buf[len] = '\0'; // Add to the queue (but wait for queue to be small enough) WaitForSingleObject(g_toSignQueueSemaphore, INFINITE); g_toVerifyQueueMutex.lock(); g_toVerifyQueue.push(buf); g_toVerifyQueueMutex.unlock(); // Signal a validate thread that this is ready to read ReleaseSemaphore(g_toVerifyQueueSemaphore, 1, NULL); delete theSerializer; delete formatTarget; } DOMText *createDocSkeleton(DOMImplementation *impl, char * tid) { // Create a new document skeleton that can be used by the // calling thread //g_providerMutex.lock(); DOMDocument *doc = impl->createDocument( 0, MAKE_UNICODE_STRING("Document"), NULL); //g_providerMutex.unlock(); DOMElement *rootElem = doc->getDocumentElement(); // Add the thread ID element DOMElement * tidElem = doc->createElement(MAKE_UNICODE_STRING("ThreadID")); tidElem->appendChild(doc->createTextNode(MAKE_UNICODE_STRING(tid))); rootElem->appendChild(tidElem); rootElem->appendChild(doc->createTextNode(DSIGConstants::s_unicodeStrNL)); // Create an element for the unique element DOMElement * uniqueElem = doc->createElement(MAKE_UNICODE_STRING("UniqueData")); DOMText * uniqueTextElem = doc->createTextNode(MAKE_UNICODE_STRING("preUnique")); rootElem->appendChild(uniqueElem); rootElem->appendChild(doc->createTextNode(DSIGConstants::s_unicodeStrNL)); uniqueElem->appendChild(uniqueTextElem); return uniqueTextElem; } // -------------------------------------------------------------------------------- // Signing Thread // -------------------------------------------------------------------------------- DWORD WINAPI doSignThread (LPVOID Param) { // This is called to start up a new thread int myId; ostrstream msg; ostrstream tid; DOMImplementation *impl; DOMDocument * myDoc; DOMElement * myRootElem; DOMText * myText; impl = reinterpret_cast<DOMImplementation *>(Param); const DWORD theThreadID = GetCurrentThreadId(); tid << theThreadID << '\0'; // Obtain an thread number; g_initMutex.lock(); myId = g_initSignCount++; g_initMutex.unlock(); g_signCount[myId] = 0; // Sign while (g_completed == false) { // Create a document to sign myText = createDocSkeleton(impl, tid.str()); myDoc = myText->getOwnerDocument(); myRootElem = myDoc->getDocumentElement(); // The provider object internally manages multiple threads DSIGSignature * sig = g_provider->newSignature(); DSIGReference * ref; DOMElement * sigNode; sig->setDSIGNSPrefix(MAKE_UNICODE_STRING("ds")); sigNode = sig->createBlankSignature(myDoc, DSIGConstants::s_unicodeStrURIC14N_COM, DSIGConstants::s_unicodeStrURIHMAC_SHA1); myRootElem->appendChild(sigNode); myRootElem->appendChild(myDoc->createTextNode(DSIGConstants::s_unicodeStrNL)); ref = sig->createReference(MAKE_UNICODE_STRING(""), DSIGConstants::s_unicodeStrURISHA1); ref->appendEnvelopedSignatureTransform(); sig->appendKeyName(MAKE_UNICODE_STRING("The secret key is \"secret\"")); #if defined (XSEC_HAVE_OPENSSL) OpenSSLCryptoKeyHMAC * hmacKey = new OpenSSLCryptoKeyHMAC(); #else WinCAPICryptoKeyHMAC * hmacKey = new WinCAPICryptoKeyHMAC(0); #endif hmacKey->setKey((unsigned char *) "secret", (unsigned int) strlen("secret")); sig->setSigningKey(hmacKey); sig->sign(); g_provider->releaseSignature(sig); // Serialise and add to verify queue addDocToQueue(impl, myDoc); myDoc->release(); // Tell the control thread what we have done g_signCount[myId] += 1; // Sleep for a while Sleep (sleepTime + (rand() % sleepTime)); } msg << "Ending signing thread - " << theThreadID << endl << '\0'; cerr << msg.str(); // Allow the output stream memory to be released. tid.freeze(false); msg.freeze(false); return 0; } // -------------------------------------------------------------------------------- // Verify Thread // -------------------------------------------------------------------------------- DWORD WINAPI doVerifyThread (LPVOID Param) { // This is called to start up a new thread int myId; ostrstream msg; DOMImplementation *impl; DOMDocument * myDoc; impl = reinterpret_cast<DOMImplementation *>(Param); const DWORD theThreadID = GetCurrentThreadId(); // Find my ID g_initMutex.lock(); myId = g_initVerifyCount++; g_initMutex.unlock(); g_verifyCount[myId] = 0; // Create a parser XercesDOMParser * parser = new XercesDOMParser; parser->setDoNamespaces(true); parser->setCreateEntityReferenceNodes(true); // Wait for a semaphore event to tell us that there is a buffer to validate WaitForSingleObject( g_toVerifyQueueSemaphore , // handle to semaphore INFINITE); while (g_completed == false) { // Get the buffer g_toVerifyQueueMutex.lock(); char * buf = g_toVerifyQueue.front(); g_toVerifyQueue.pop(); g_toVerifyQueueMutex.unlock(); // Signal the signing proceses that there is room in the queue ReleaseSemaphore(g_toSignQueueSemaphore, 1, NULL); // Now parse and validate the signature MemBufInputSource* memIS = new MemBufInputSource ((const XMLByte*) buf, (unsigned int) strlen(buf), "XSECMem"); parser->parse(*memIS); delete(memIS); myDoc = parser->adoptDocument(); if ((rand() % 5) == 1) { // Reset the value of "UniqueData" to invalidate the signature DOMNode * n = myDoc->getDocumentElement()->getFirstChild(); while (n != NULL && (n->getNodeType() != DOMNode::ELEMENT_NODE || !strEquals(n->getNodeName(), "UniqueData"))) n = n->getNextSibling(); if (n != NULL) { n = n->getFirstChild(); if (n->getNodeType() == DOMNode::TEXT_NODE) { n->setNodeValue(MAKE_UNICODE_STRING("bad unique data")); } } } DSIGSignature * sig = g_provider->newSignatureFromDOM(myDoc); #if defined (XSEC_HAVE_OPENSSL) OpenSSLCryptoKeyHMAC *hmacKey = new OpenSSLCryptoKeyHMAC(); #else WinCAPICryptoKeyHMAC *hmacKey = new WinCAPICryptoKeyHMAC(0); #endif hmacKey->setKey((unsigned char *) secretKey, (unsigned int) strlen(secretKey)); sig->setSigningKey(hmacKey); sig->load(); if (sig->verify() != true) { // Re-use the init Mutex to protect g_errors g_initMutex.lock(); g_errors++; g_initMutex.unlock(); } g_provider->releaseSignature(sig); // Delete the validated buffer delete[] buf; // Clean the doc myDoc->release(); g_verifyCount[myId] += 1; Sleep (sleepTime + (rand() % sleepTime)); // Wait for another object WaitForSingleObject( g_toVerifyQueueSemaphore , // handle to semaphore INFINITE); } msg << "Ending validate thread : " << theThreadID << endl << '\0'; cerr << msg.str(); msg.freeze(false); delete parser; return 0; } // -------------------------------------------------------------------------------- // Control thread - used to shut down program // -------------------------------------------------------------------------------- DWORD WINAPI doControlThread (LPVOID Param) { // Output stats and shutdown when done using std::cin; using std::cout; // Quick and dirty cin.peek(); // Signal all other threads g_completed = true; ReleaseSemaphore(g_toVerifyQueueSemaphore, 5, NULL); ReleaseSemaphore(g_toSignQueueSemaphore, 5, NULL); return 0; } // -------------------------------------------------------------------------------- // Output Thread // -------------------------------------------------------------------------------- DWORD WINAPI doOutputThread (LPVOID Param) { // Output stats int i, total, lastSignTotal, lastVerifyTotal; lastSignTotal = lastVerifyTotal = 0; while (g_completed != true) { // Output some info cerr << "Signing Threads" << endl; cerr << "---------------" << endl << endl; total = 0; for (i = 0; i < numThreads; ++ i) { cerr << "Thread: " << g_signCount[i] << endl; total += g_signCount[i]; } cerr << endl << "Total: " << total << endl; cerr << "Ops/Sec: " << total - lastSignTotal << endl << endl; lastSignTotal = total; cerr << "Verify Threads" << endl; cerr << "--------------" << endl << endl; total = 0; for (i = 0; i < numThreads; ++ i) { cerr << "Thread: " << g_verifyCount[i] << endl; total += g_verifyCount[i]; } cerr << endl << "Total: " << total << endl; cerr << "Ops/Sec: " << total - lastVerifyTotal << endl << endl; lastVerifyTotal = total; cerr << "Total Errors : " << g_errors << endl; cerr << "Buffers in Queue : " << (unsigned int) g_toVerifyQueue.size() << endl; // Go to sleep for a second Sleep(1000); } return 0; } // -------------------------------------------------------------------------------- // Start up threads // -------------------------------------------------------------------------------- void runThreads(DOMImplementation * impl, int nThreads) { // Set up the worker queue g_toVerifyQueueSemaphore = CreateSemaphore(NULL, 0, 20, "verifyQueue"); g_toSignQueueSemaphore = CreateSemaphore(NULL, 20, 20, "signQueue"); // Ensure nobody stops too soon g_completed = false; // How many signature errors do we have? g_errors = 0; int i; for (i = 0; i < numThreads; ++i) { g_signCount[i] = 0; g_verifyCount[i] = 0; } vector<HANDLE> hThreads; hThreads.reserve(nThreads); i = 0; for (; i < nThreads; ++i) { DWORD threadID; const HANDLE hThread = CreateThread( 0, 4096, // Stack size for thread. doSignThread, // pointer to thread function reinterpret_cast<LPVOID>(impl), // argument for new thread 0, // creation flags &threadID); assert(hThread != 0); hThreads.push_back(hThread); } for (; i < nThreads * 2; ++i) { DWORD threadID; const HANDLE hThread = CreateThread( 0, 4096, // Stack size for thread. doVerifyThread, // pointer to thread function reinterpret_cast<LPVOID>(impl), // argument for new thread 0, // creation flags &threadID); assert(hThread != 0); hThreads.push_back(hThread); } // Start the control thread DWORD threadID; const HANDLE hThread = CreateThread( 0, 4096, // Stack size for thread. doControlThread, // pointer to thread function reinterpret_cast<LPVOID>(impl), // argument for new thread 0, // creation flags &threadID); assert(hThread != 0); hThreads.push_back(hThread); // Start the output thread const HANDLE h2Thread = CreateThread( 0, 4096, // Stack size for thread. doOutputThread, // pointer to thread function reinterpret_cast<LPVOID>(impl), // argument for new thread 0, // creation flags &threadID); assert(h2Thread != 0); hThreads.push_back(h2Thread); WaitForMultipleObjects((DWORD) hThreads.size(), &hThreads[0], TRUE, INFINITE); for (i = 0; i < nThreads; ++i) { CloseHandle(hThreads[i]); } // Clear out the unverified buffers while (g_toVerifyQueue.size() != 0) { char * buf = g_toVerifyQueue.front(); g_toVerifyQueue.pop(); delete[] buf; } } // -------------------------------------------------------------------------------- // Main // -------------------------------------------------------------------------------- int main (int argc, char ** argv) { // Initialise the XML system try { XMLPlatformUtils::Initialize(); XSECPlatformUtils::Initialise(); } catch (const XMLException &e) { cerr << "Error during initialisation of Xerces" << endl; cerr << "Error Message = : " << e.getMessage() << endl; } // Create a single implementation DOMImplementation *impl = DOMImplementationRegistry::getDOMImplementation(MAKE_UNICODE_STRING("core")); // Initialise that which needs to be initialised prior to thread startup g_provider = new XSECProvider; runThreads(impl, numThreads); // Clean up delete g_provider; XSECPlatformUtils::Terminate(); XMLPlatformUtils::Terminate(); return 0; }