blob: 696702bdc6ecf20bd1c952bab323b42089f5f488 [file] [log] [blame]
/**
* 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;
}