| /** |
| * 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; |
| |
| } |