aboutsummaryrefslogtreecommitdiffstats
path: root/SoftHSMv2/src/lib/object_store
diff options
context:
space:
mode:
Diffstat (limited to 'SoftHSMv2/src/lib/object_store')
-rw-r--r--SoftHSMv2/src/lib/object_store/DB.cpp926
-rw-r--r--SoftHSMv2/src/lib/object_store/DB.h188
-rw-r--r--SoftHSMv2/src/lib/object_store/DBObject.cpp1493
-rw-r--r--SoftHSMv2/src/lib/object_store/DBObject.h142
-rw-r--r--SoftHSMv2/src/lib/object_store/DBToken.cpp874
-rw-r--r--SoftHSMv2/src/lib/object_store/DBToken.h135
-rw-r--r--SoftHSMv2/src/lib/object_store/Directory.cpp278
-rw-r--r--SoftHSMv2/src/lib/object_store/Directory.h89
-rw-r--r--SoftHSMv2/src/lib/object_store/File.cpp765
-rw-r--r--SoftHSMv2/src/lib/object_store/File.h136
-rw-r--r--SoftHSMv2/src/lib/object_store/FindOperation.cpp80
-rw-r--r--SoftHSMv2/src/lib/object_store/FindOperation.h67
-rw-r--r--SoftHSMv2/src/lib/object_store/Generation.cpp261
-rw-r--r--SoftHSMv2/src/lib/object_store/Generation.h92
-rw-r--r--SoftHSMv2/src/lib/object_store/Makefile.am34
-rw-r--r--SoftHSMv2/src/lib/object_store/OSAttribute.cpp187
-rw-r--r--SoftHSMv2/src/lib/object_store/OSAttribute.h103
-rw-r--r--SoftHSMv2/src/lib/object_store/OSAttributes.h50
-rw-r--r--SoftHSMv2/src/lib/object_store/OSObject.h95
-rw-r--r--SoftHSMv2/src/lib/object_store/OSPathSep.h45
-rw-r--r--SoftHSMv2/src/lib/object_store/OSToken.cpp722
-rw-r--r--SoftHSMv2/src/lib/object_store/OSToken.h160
-rw-r--r--SoftHSMv2/src/lib/object_store/ObjectFile.cpp870
-rw-r--r--SoftHSMv2/src/lib/object_store/ObjectFile.h154
-rw-r--r--SoftHSMv2/src/lib/object_store/ObjectStore.cpp187
-rw-r--r--SoftHSMv2/src/lib/object_store/ObjectStore.h88
-rw-r--r--SoftHSMv2/src/lib/object_store/ObjectStoreToken.cpp84
-rw-r--r--SoftHSMv2/src/lib/object_store/ObjectStoreToken.h106
-rw-r--r--SoftHSMv2/src/lib/object_store/SessionObject.cpp334
-rw-r--r--SoftHSMv2/src/lib/object_store/SessionObject.h132
-rw-r--r--SoftHSMv2/src/lib/object_store/SessionObjectStore.cpp212
-rw-r--r--SoftHSMv2/src/lib/object_store/SessionObjectStore.h100
-rw-r--r--SoftHSMv2/src/lib/object_store/UUID.cpp68
-rw-r--r--SoftHSMv2/src/lib/object_store/UUID.h48
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.cpp159
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.h79
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBObjectTests.cpp816
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBObjectTests.h93
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBTests.cpp648
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBTests.h134
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBTokenTests.cpp491
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DBTokenTests.h66
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DirectoryTests.cpp227
-rw-r--r--SoftHSMv2/src/lib/object_store/test/DirectoryTests.h54
-rw-r--r--SoftHSMv2/src/lib/object_store/test/FileTests.cpp340
-rw-r--r--SoftHSMv2/src/lib/object_store/test/FileTests.h63
-rw-r--r--SoftHSMv2/src/lib/object_store/test/Makefile.am39
-rw-r--r--SoftHSMv2/src/lib/object_store/test/OSTokenTests.cpp504
-rw-r--r--SoftHSMv2/src/lib/object_store/test/OSTokenTests.h60
-rw-r--r--SoftHSMv2/src/lib/object_store/test/ObjectFileTests.cpp911
-rw-r--r--SoftHSMv2/src/lib/object_store/test/ObjectFileTests.h72
-rw-r--r--SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.cpp278
-rw-r--r--SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.h58
-rw-r--r--SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.cpp319
-rw-r--r--SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.h56
-rw-r--r--SoftHSMv2/src/lib/object_store/test/SessionObjectTests.cpp436
-rw-r--r--SoftHSMv2/src/lib/object_store/test/SessionObjectTests.h68
-rw-r--r--SoftHSMv2/src/lib/object_store/test/UUIDTests.cpp61
-rw-r--r--SoftHSMv2/src/lib/object_store/test/UUIDTests.h54
-rw-r--r--SoftHSMv2/src/lib/object_store/test/objstoretest.cpp91
60 files changed, 15482 insertions, 0 deletions
diff --git a/SoftHSMv2/src/lib/object_store/DB.cpp b/SoftHSMv2/src/lib/object_store/DB.cpp
new file mode 100644
index 0000000..d82d7cb
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DB.cpp
@@ -0,0 +1,926 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DB.cpp
+
+ Specifies classes to access the Token Database
+ *****************************************************************************/
+#define HAVE_SQL_TRACE 0
+
+#include "config.h"
+#include "OSPathSep.h"
+#include "log.h"
+#include <cstdlib>
+#include <cstdio>
+#include <iostream>
+#include <vector>
+#include <sqlite3.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "DB.h"
+
+#if HAVE_SQL_TRACE
+static void xTrace(void*connectionLabel,const char*zSql)
+{
+ const char *label = static_cast<const char *>(connectionLabel);
+ if (label)
+ std::cout << std::endl << label << ": " << zSql ;
+ else
+ std::cout << std::endl << zSql ;
+}
+#endif
+
+static int static_log_err(const char *format, va_list ap)
+{
+ std::vector<char> logMessage;
+ logMessage.resize(4096);
+ vsnprintf(&logMessage[0], 4096, format, ap);
+ ERROR_MSG(&logMessage[0]);
+ return 0;
+}
+
+static DB::LogErrorHandler static_LogErrorhandler = static_log_err;
+
+void DB::logError(const std::string &format, ...)
+{
+ if (!static_LogErrorhandler)
+ return;
+ va_list args;
+ va_start(args, format);
+ static_LogErrorhandler(format.c_str(),args);
+ va_end(args);
+}
+
+DB::LogErrorHandler DB::setLogErrorHandler(DB::LogErrorHandler handler)
+{
+ LogErrorHandler temp = static_LogErrorhandler;
+ static_LogErrorhandler = handler;
+ return temp;
+}
+
+void DB::resetLogErrorHandler()
+{
+ static_LogErrorhandler = static_log_err;
+}
+
+static void reportErrorDB(sqlite3 *db)
+{
+ if (!db) {
+ DB::logError("sqlite3 pointer is NULL");
+ return;
+ }
+
+ int rv = sqlite3_errcode(db);
+ if (rv == SQLITE_OK || rv == SQLITE_ROW || rv == SQLITE_DONE)
+ return;
+
+#ifdef HAVE_SILENT_BUSY_AND_LOCKED_ERRORS
+ // Either the database file is locked (SQLITE_BUSY)
+ // or a table in the database is locked (SQLITE_LOCKED)
+ if (rv == SQLITE_BUSY || rv == SQLITE_LOCKED)
+ return;
+#endif
+
+ DB::logError("SQLITE3: %s (%d)", sqlite3_errmsg(db), rv);
+}
+
+static void reportError(sqlite3_stmt *stmt)
+{
+ if (!stmt) {
+ DB::logError("sqlite3_stmt pointer is NULL");
+ return;
+ }
+ reportErrorDB(sqlite3_db_handle(stmt));
+}
+
+static time_t sqlite3_gmtime(struct tm *tm)
+{
+ // We don't want to depend on timegm() so we use a workaround via the
+ // gmtime_r() function to determine this.
+ // As input we use a moment in time just 10 days after the POSIX epoch.
+ // The POSIX epoch is defined as the moment in time at midnight Coordinated
+ // Universal Time (UTC) of Thursday, January 1, 1970. A time_t value is
+ // the number of seconds elapsed since epoch.
+ struct tm ref_tm = {0,0,0,0,0,0,0,0,0,0,0};
+ ref_tm.tm_year = 70; // Years since 1900;
+ ref_tm.tm_mday = 10; // 10th
+
+ // We need the time difference between local time and UTC time.
+ // mktime will interpret the UTC time stored in tm as local time
+ // so let's assume we are in a time zone 1 hour ahead of UTC (UTC+1)
+ // then a time of 13:00 interpreted as local time needs 1 hour subtracted
+ // to arrive at UTC time. This UTC time is then converted to a POSIX
+ // time_t value.
+ time_t posix_time = mktime(&ref_tm);
+
+ // Use gmtime_r to convert the POSIX time back to a tm struct.
+ // No time adjustment is done this time because POSIX time is
+ // defined in terms of UTC.
+ gmtime_r(&posix_time, &ref_tm);
+ if (ref_tm.tm_isdst != 0) {
+ DB::logError("expected gmtime_r to return zero in tm_isdst member of tm struct");
+ return ((time_t)-1);
+ }
+
+ // Using mktime again to convert tm. This will again subtract 1 hour from
+ // the time (under the assumption that we are 1 hour ahead of UTC).
+ // We can now use this to determine how much local time differred
+ // from UTC time on january the 10th 1970
+ long diff_time = posix_time - mktime(&ref_tm);
+
+ // We explicitly set tm_isdst to zero to prevent errors
+ // when the time we are trying to convert is occuring at
+ // the moment when a dst change is in progress.
+ // We require mktime to respect our setting of tm_isdst
+ // indicating that no dst is in effect.
+ tm->tm_isdst = 0; // Tell (and force) mktime not to take dst into account.
+
+ // We now can calculate and return a correct POSIX time.
+ // So, although mktime() interprets gm_tm as local time adjusts for
+ // the time difference between local time and UTC time. We then undo
+ // that adjustment by adding diff_time.
+ return mktime(tm) + diff_time;
+}
+
+/**************************
+ * Handle
+ **************************/
+
+class DB::Handle {
+public:
+ int _refcount;
+ sqlite3_stmt *_stmt;
+ Handle(sqlite3_stmt *stmt)
+ : _refcount(1), _stmt(stmt)
+ {
+ }
+ ~Handle()
+ {
+ if (_stmt)
+ {
+ sqlite3_finalize(_stmt);
+ _stmt = NULL;
+ }
+ }
+
+ Handle *retain()
+ {
+ if (_refcount)
+ {
+ _refcount++;
+ return this;
+ }
+ return NULL;
+ }
+ void release()
+ {
+ if (_refcount)
+ {
+ _refcount--;
+ if (_refcount)
+ return;
+ delete this;
+ }
+ }
+ bool reset()
+ {
+ if (sqlite3_reset(_stmt) != SQLITE_OK)
+ {
+ reportError(_stmt);
+ return false;
+ }
+ return true;
+ }
+ Statement::ReturnCode step()
+ {
+ int rv = sqlite3_step(_stmt);
+ if (rv != SQLITE_ROW && rv != SQLITE_DONE)
+ {
+ reportError(_stmt);
+ return Statement::ReturnCodeError;
+ }
+
+ if (rv==SQLITE_ROW)
+ {
+ return Statement::ReturnCodeRow;
+ }
+
+ return Statement::ReturnCodeDone;
+ }
+private:
+ // disable evil constructors
+ Handle(const Handle &);
+ Handle & operator=(const Handle &);
+};
+
+DB::Statement::Statement()
+ : _handle(NULL)
+{
+}
+
+DB::Statement::Statement(sqlite3_stmt *statement)
+ : _handle(new Handle(statement))
+{
+}
+
+DB::Statement::Statement(const DB::Statement &statement)
+ : _handle(statement._handle)
+{
+ if (_handle)
+ _handle = _handle->retain();
+}
+
+DB::Statement &DB::Statement::operator=(const DB::Statement &statement)
+{
+ if (this != &statement)
+ {
+ Handle *tmp = NULL;
+ if (statement._handle) {
+ tmp = statement._handle->retain();
+ }
+ if (_handle) {
+ _handle->release();
+ }
+ _handle = tmp;
+ }
+ return *this;
+}
+
+DB::Statement::~Statement()
+{
+ if (_handle) {
+ _handle->release();
+ _handle = NULL;
+ }
+}
+
+bool DB::Statement::isValid()
+{
+ return _handle != NULL && _handle->_stmt != NULL;
+}
+
+int DB::Statement::refcount()
+{
+ return _handle ? _handle->_refcount : 0;
+}
+
+
+bool DB::Statement::reset()
+{
+ if (!isValid()) {
+ DB::logError("Statement::reset: statement is not valid");
+ return false;
+ }
+ return _handle->reset();
+}
+
+DB::Statement::ReturnCode DB::Statement::step()
+{
+ if (!isValid()) {
+ DB::logError("Statement::step: statement is not valid");
+ return ReturnCodeError;
+ }
+ return _handle->step();
+}
+
+DB::Handle *DB::Statement::handle() const
+{
+ return _handle;
+}
+
+/**************************
+ * Bindings
+ **************************/
+
+DB::Bindings::Bindings()
+ : Statement()
+{
+}
+
+DB::Bindings::Bindings(const Statement &statement)
+ : Statement(statement)
+{
+}
+
+bool DB::Bindings::clear()
+{
+ if (!isValid()) {
+ DB::logError("Bindings::clear: statement is not valid");
+ return false;
+ }
+ if (sqlite3_clear_bindings(_handle->_stmt) != SQLITE_OK) {
+ reportError(_handle->_stmt);
+ return false;
+ }
+ return true;
+}
+
+bool DB::Bindings::bindBlob(int index, const void *value, int n, void(*destruct)(void*))
+{
+ if (!isValid()) {
+ DB::logError("Bindings::bindBlob: statement is not valid");
+ return false;
+ }
+ if (sqlite3_bind_blob(_handle->_stmt, index, value, n, destruct) != SQLITE_OK) {
+ reportError(_handle->_stmt);
+ return false;
+ }
+ return true;
+}
+
+bool DB::Bindings::bindDouble(int index, double value)
+{
+ if (!isValid()) {
+ DB::logError("Bindings::bindDouble: statement is not valid");
+ return false;
+ }
+ if (sqlite3_bind_double(_handle->_stmt, index, value) != SQLITE_OK) {
+ reportError(_handle->_stmt);
+ return false;
+ }
+ return true;
+}
+
+bool DB::Bindings::bindInt(int index, int value)
+{
+ if (!isValid()) {
+ DB::logError("Bindings::bindInt: statement is not valid");
+ return false;
+ }
+ if (sqlite3_bind_int(_handle->_stmt, index, value) != SQLITE_OK) {
+ reportError(_handle->_stmt);
+ return false;
+ }
+ return true;
+}
+
+bool DB::Bindings::bindInt64(int index, long long value)
+{
+ if (!isValid()) {
+ DB::logError("Bindings::bindInt64: statement is not valid");
+ return false;
+ }
+ if (sqlite3_bind_int64(_handle->_stmt, index, value) != SQLITE_OK) {
+ reportError(_handle->_stmt);
+ return false;
+ }
+ return true;
+}
+
+//bool DB::Bindings::bindNull(int index)
+//{
+//#if 0
+// int sqlite3_bind_null(sqlite3_stmt*, int);
+//#endif
+// return false;
+//}
+
+bool DB::Bindings::bindText(int index, const char *value, int n, void (*destruct)(void *))
+{
+ if (!isValid()) {
+ DB::logError("Bindings::bindText: statement is not valid");
+ return false;
+ }
+ if (sqlite3_bind_text(_handle->_stmt, index, value, n, destruct) != SQLITE_OK) {
+ reportError(_handle->_stmt);
+ return false;
+ }
+ return true;
+}
+
+//bool DB::Bindings::bindZeroBlob(int index, int n)
+//{
+//#if 0
+// int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+//#endif
+// return false;
+//}
+
+/**************************
+ * Result
+ **************************/
+
+DB::Result::Result()
+ : Statement()
+{
+}
+
+DB::Result::Result(const Statement &statement)
+ : Statement(statement)
+{
+}
+
+#if 0
+unsigned int DB::Result::getField(const std::string &fieldname)
+{
+ unsigned int fieldidx = fields[fieldname];
+ if (fieldidx == 0)
+ DB::logError("Result: invalid field name \"%s\"",fieldname.c_str());
+ return fieldidx;
+}
+#endif
+
+bool DB::Result::fieldIsNull(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::fieldIsNull: statement is not valid");
+ return true;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return true;
+ }
+ int column_type = sqlite3_column_type(_handle->_stmt, fieldidx-1);
+ return column_type == SQLITE_NULL;
+}
+
+time_t DB::Result::getDatetime(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getDatetime: statement is not valid");
+ return ((time_t)-1);
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return ((time_t)-1);
+ }
+
+ const unsigned char *value = sqlite3_column_text(_handle->_stmt, fieldidx-1);
+ int valuelen = sqlite3_column_bytes(_handle->_stmt, fieldidx-1);
+
+ unsigned long years,mons,days,hours,mins,secs;
+ struct tm gm_tm = {0,0,0,0,0,0,0,0,0,0,0};
+ gm_tm.tm_isdst = 0; // Tell mktime not to take dst into account.
+ gm_tm.tm_year = 70; // 1970
+ gm_tm.tm_mday = 1; // 1th day of the month
+ const char *p = (const char *)value;
+ char *pnext;
+ bool bdateonly = true;
+ switch (valuelen) {
+ case 19: // 2011-12-31 23:59:59
+ bdateonly = false;
+ // falls through to next case
+ case 10: // 2011-12-31
+ years = strtoul(p,&pnext,10);
+ gm_tm.tm_year = ((int)years)-1900; /* years since 1900 */
+ p = pnext+1;
+ mons = strtoul(p,&pnext,10);
+ gm_tm.tm_mon = ((int)mons)-1; /* months since January [0-11] */
+ p = pnext+1;
+ days = strtoul(p,&pnext,10);
+ gm_tm.tm_mday = ((int)days); /* day of the month [1-31] */
+ p = pnext+1;
+ if (bdateonly)
+ break;
+ // falls through to next case
+ case 8: // 23:59:59
+ hours = strtoul(p,&pnext,10);
+ gm_tm.tm_hour = (int)hours; /* hours since midnight [0-23] */
+ if ((pnext-p) != 2) {
+ DB::logError("Result: invalid hours in time: '%s'",value);
+ return 0;
+ }
+ p = pnext+1;
+ mins = strtoul(p,&pnext,10);
+ gm_tm.tm_min = (int)mins; /* minutes after the hour [0-59] */
+ if ((pnext-p) != 2) {
+ DB::logError("Result: invalid minutes in time: '%s'",value);
+ return 0;
+ }
+ p = pnext+1;
+ secs = strtoul(p,&pnext,10);
+ gm_tm.tm_sec = (int)secs; /* seconds after the minute [0-60] */
+ if ((pnext-p) != 2) {
+ DB::logError("Result: invalid seconds in time: '%s'",value);
+ return 0;
+ }
+ break;
+ default:
+ DB::logError("Result: invalid date/time value: '%s'",value);
+ return 0;
+ }
+
+ return sqlite3_gmtime(&gm_tm);
+}
+
+unsigned char DB::Result::getUChar(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getUChar: statement is not valid");
+ return 0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0;
+ }
+ int value = sqlite3_column_int(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return (unsigned char)value;
+}
+
+float DB::Result::getFloat(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getFloat: statement is not valid");
+ return 0.0f;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0.0f;
+ }
+ double value = sqlite3_column_double(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return (float)value;
+}
+
+double DB::Result::getDouble(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getDouble: statement is not valid");
+ return 0.0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0.0;
+ }
+ double value = sqlite3_column_double(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return value;
+}
+
+int DB::Result::getInt(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getInt: statement is not valid");
+ return 0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0;
+ }
+ int value = sqlite3_column_int(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return value;
+}
+
+unsigned int DB::Result::getUInt(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getUInt: statement is not valid");
+ return 0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0;
+ }
+ int value = sqlite3_column_int(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return (unsigned int)value;
+}
+
+long long DB::Result::getLongLong(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getLongLong: statement is not valid");
+ return 0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0;
+ }
+ sqlite3_int64 value = sqlite3_column_int64(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return value;
+}
+
+unsigned long long DB::Result::getULongLong(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getULongLong: statement is not valid");
+ return 0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0;
+ }
+ sqlite3_int64 value = sqlite3_column_int64(_handle->_stmt, fieldidx-1);
+ reportError(_handle->_stmt);
+ return (unsigned long long)value;
+}
+
+const char *DB::Result::getString(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getString: statement is not valid");
+ return NULL;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return NULL;
+ }
+ const unsigned char *value = sqlite3_column_text(_handle->_stmt,fieldidx-1);
+ reportError(_handle->_stmt);
+ return (const char *)value;
+}
+
+const unsigned char *DB::Result::getBinary(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getBinary: statement is not valid");
+ return NULL;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return NULL;
+ }
+ const unsigned char *value =
+ (const unsigned char *)sqlite3_column_blob(_handle->_stmt,fieldidx-1);
+ reportError(_handle->_stmt);
+ return value;
+}
+
+size_t DB::Result::getFieldLength(unsigned int fieldidx)
+{
+ if (!isValid()) {
+ DB::logError("Result::getFieldLength: statement is not valid");
+ return 0;
+ }
+ if (fieldidx == 0) {
+ DB::logError("Result: zero is an invalid field index");
+ return 0;
+ }
+ int value = sqlite3_column_bytes(_handle->_stmt,fieldidx-1);
+ reportError(_handle->_stmt);
+ return (size_t)value;
+}
+
+bool DB::Result::firstRow()
+{
+ if (!isValid()) {
+ DB::logError("Result::firstRow: statement is not valid");
+ return false;
+ }
+ return _handle->reset() && _handle->step()==Statement::ReturnCodeRow;
+}
+
+bool DB::Result::nextRow()
+{
+ if (!isValid()) {
+ DB::logError("Result::nextRow: statement is not valid");
+ return false;
+ }
+ return _handle->step()==Statement::ReturnCodeRow;
+}
+
+/**************************
+ * Connection
+ **************************/
+
+DB::Connection *DB::Connection::Create(const std::string &dbdir, const std::string &dbname)
+{
+ if (dbdir.length() == 0) {
+ DB::logError("Connection::Create: database directory parameter dbdir is empty");
+ return NULL;
+ }
+
+ if (dbname.length() == 0) {
+ DB::logError("Connection::Create: database name parameter dbname is empty");
+ return NULL;
+ }
+
+ return new Connection(dbdir,dbname);
+}
+
+DB::Connection::Connection(const std::string &dbdir, const std::string &dbname)
+ : _dbdir(dbdir)
+ , _dbpath(dbdir + OS_PATHSEP + dbname)
+ , _db(NULL)
+{
+}
+
+DB::Connection::~Connection()
+{
+ close();
+}
+
+const std::string &DB::Connection::dbdir()
+{
+ return _dbdir;
+}
+
+const std::string &DB::Connection::dbpath()
+{
+ return _dbpath;
+}
+
+DB::Statement DB::Connection::prepare(const std::string &format, ...){
+ // pstatement will hold a dynamically allocated string that needs to be deleted.
+ char *pstatement = NULL;
+
+ // short form
+ char statement[128];
+ va_list args;
+ va_start(args, format);
+ int cneeded = vsnprintf(statement,sizeof(statement),format.c_str(),args);
+ va_end(args);
+ if (cneeded<0) {
+ DB::logError("Connection::prepare: vsnprintf encoding error");
+ return Statement();
+ }
+ if (((size_t)cneeded)>=sizeof(statement)) {
+ // long form
+ pstatement = new char[cneeded+1];
+ if (!pstatement) {
+ DB::logError("Connection::prepare: out of memory");
+ return Statement();
+ }
+ va_start(args, format);
+ bool ok = vsnprintf(pstatement,cneeded+1,format.c_str(),args)==cneeded;
+ va_end(args);
+ if (!ok) {
+ DB::logError("Connection::prepare: vsnprintf error");
+ delete[] pstatement;
+ return Statement();
+ }
+ }
+
+ sqlite3_stmt *stmt = NULL;
+ int rv = sqlite3_prepare_v2(_db,
+ pstatement ? pstatement : statement,
+ cneeded+1,
+ &stmt,
+ NULL);
+
+ if (pstatement)
+ delete[] pstatement;
+
+ if (rv != SQLITE_OK) {
+ reportErrorDB(_db);
+ if (stmt)
+ sqlite3_finalize(stmt);
+ return Statement();
+ }
+
+ if (!stmt) {
+ DB::logError("Connection::prepare: expected sqlite3_prepare_v2 to return a compiled "
+ "statement, got NULL, out of memory ?");
+ return Statement();
+ }
+
+ return Statement(stmt);
+}
+
+DB::Result DB::Connection::perform(DB::Statement &statement)
+{
+ return (statement.step()==Statement::ReturnCodeRow) ? Result(statement) : Result();
+}
+
+bool DB::Connection::execute(DB::Statement &statement)
+{
+ return statement.step()==Statement::ReturnCodeDone;
+}
+
+bool DB::Connection::connect(const char *
+#if HAVE_SQL_TRACE
+ connectionLabel
+#endif
+ )
+{
+ // Create and set file permissions if the DB does not exist.
+ int fd = open(_dbpath.c_str(), O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1)
+ {
+ DB::logError("Could not open database: %s (errno %i)",
+ _dbpath.c_str(), errno);
+ return false;
+ }
+ ::close(fd);
+
+ int rv = sqlite3_open_v2(_dbpath.c_str(),
+ &_db,
+ SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE
+ | SQLITE_OPEN_FULLMUTEX,
+ NULL);
+
+ if (rv != SQLITE_OK) {
+ reportErrorDB(_db);
+ return false;
+ }
+
+ int foreignKeyEnabled = 0;
+ rv = sqlite3_db_config(_db,SQLITE_DBCONFIG_ENABLE_FKEY,1,&foreignKeyEnabled);
+ if (rv != SQLITE_OK) {
+ reportErrorDB(_db);
+ return false;
+ }
+
+ if (foreignKeyEnabled != 1) {
+ DB::logError("Connection::connect: foreign key support not enabled");
+ return false;
+ }
+
+ rv = sqlite3_busy_timeout(_db, 15000); // 15 seconds
+ if (rv != SQLITE_OK) {
+ reportErrorDB(_db);
+ return false;
+ }
+#if HAVE_SQL_TRACE
+ sqlite3_trace(_db, xTrace, const_cast<char *>(connectionLabel));
+#endif
+ return true;
+}
+
+void DB::Connection::close()
+{
+ if (_db) {
+ sqlite3_close(_db);
+ _db = NULL;
+ }
+}
+
+bool DB::Connection::setBusyTimeout(int ms)
+{
+ int rv = sqlite3_busy_timeout(_db, ms);
+ if (rv != SQLITE_OK) {
+ reportErrorDB(_db);
+ return false;
+ }
+
+ return true;
+}
+
+bool DB::Connection::tableExists(const std::string &tablename)
+{
+ Statement statement = prepare("select name from sqlite_master where type='table' and name='%s';",tablename.c_str());
+ return statement.step()==Statement::ReturnCodeRow && statement.step()==Statement::ReturnCodeDone;
+}
+
+long long DB::Connection::lastInsertRowId()
+{
+ return sqlite3_last_insert_rowid(_db);
+}
+
+bool DB::Connection::inTransaction()
+{
+ return sqlite3_get_autocommit(_db)==0;
+}
+
+bool DB::Connection::beginTransactionRO()
+{
+ Statement statement = prepare("begin");
+ return statement.step()==Statement::ReturnCodeDone;
+}
+
+bool DB::Connection::endTransactionRO()
+{
+ Statement statement = prepare("end");
+ return statement.step()==Statement::ReturnCodeDone;
+}
+
+bool DB::Connection::beginTransactionRW()
+{
+ Statement statement = prepare("begin immediate");
+ return statement.step()==Statement::ReturnCodeDone;
+}
+
+bool DB::Connection::commitTransaction()
+{
+ Statement statement = prepare("commit");
+ return statement.step()==Statement::ReturnCodeDone;
+}
+
+bool DB::Connection::rollbackTransaction()
+{
+ Statement statement = prepare("rollback");
+ return statement.step()==Statement::ReturnCodeDone;
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/DB.h b/SoftHSMv2/src/lib/object_store/DB.h
new file mode 100644
index 0000000..e4e1a11
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DB.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DB.h
+
+ Specifies classes to access the Token Database
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DB_H
+#define _SOFTHSM_V2_DB_H
+
+#include "config.h"
+
+#include <string>
+#include <sqlite3.h>
+
+namespace DB {
+
+// Log an error to the error handler that has been setup using a call to setLogErrorHandler declared below.
+void logError(const std::string &format, ...);
+
+// The ap parameter has already been started with va_start.
+// So the handler only has to pass this on to a vprintf function
+// to actually print it.
+typedef int (*LogErrorHandler)(const char *format, va_list ap);
+
+// Set an alternative for vprintf to log the actual errors.
+// Set to NULL to disable logging al together.
+LogErrorHandler setLogErrorHandler(LogErrorHandler handler);
+
+// Set the log error handler back to the default value that logs to stdout.
+void resetLogErrorHandler();
+
+// Forward declaration of the handle class used by Statement, Binding and Result.
+class Handle;
+
+// Responsible for holding on to a prepared statement.
+// After a prepared statement has been used it can be reused when the same query is performed again.
+class Statement {
+public:
+ Statement();
+ Statement(sqlite3_stmt *statement);
+ Statement(const Statement &statement);
+ Statement &operator=(const Statement &statement);
+
+ virtual ~Statement();
+ bool isValid();
+
+ // Something we'd like to check during testing.
+ int refcount();
+
+ // Reset a prepared statement
+ bool reset();
+
+ // Perform a single step of the prepared statement.
+ enum ReturnCode {
+ ReturnCodeRow,
+ ReturnCodeDone,
+ ReturnCodeError
+ };
+
+ ReturnCode step();
+
+ Handle *handle() const;
+protected:
+ Handle *_handle;
+};
+
+// Responsible for allowing parameters to be bound to statements.
+// On a statement that has been performed or executed you first
+// need to call reset() before new parameters can be bound.
+class Bindings : public Statement {
+public:
+ Bindings();
+ Bindings(const Statement &statement);
+
+ // To clear all existing bindings call this method.
+ bool clear();
+
+ // Bind a value to a parameter in a prepared statement
+ bool bindBlob(int index, const void *value, int n, void(*destruct)(void*));
+ bool bindDouble(int index, double value);
+ bool bindInt(int index, int value);
+ bool bindInt64(int index, long long value );
+ //bool bindNull(int index);
+ bool bindText(int index, const char *value, int n, void(*destruct)(void*));
+ //bool bindZeroBlob(int index, int n);
+};
+
+// Responsible for providing access to the result set of a query.
+// Used for queries that actually provide a result set.
+// A result that is returned will be positioned at the first row.
+class Result : public Statement {
+public:
+ Result();
+ Result(const Statement &statement);
+
+ bool fieldIsNull(unsigned int fieldidx);
+ time_t getDatetime(unsigned int fieldidx);
+ unsigned char getUChar(unsigned int fieldidx);
+ float getFloat(unsigned int fieldidx);
+ double getDouble(unsigned int fieldidx);
+ int getInt(unsigned int fieldidx);
+ unsigned int getUInt(unsigned int fieldidx);
+ long long getLongLong(unsigned int fieldidx);
+ unsigned long long getULongLong(unsigned int fieldidx);
+
+ const char *getString(unsigned int fieldidx);
+ const unsigned char *getBinary(unsigned int fieldidx);
+ size_t getFieldLength(unsigned int fieldidx);
+
+ // Position the result on the first row again.
+ bool firstRow();
+
+ // Position the result on the next row.
+ bool nextRow();
+};
+
+// Responsible for connection to the database and for managing prepared statements.
+class Connection {
+public:
+ static Connection *Create(const std::string &dbdir, const std::string &dbname);
+ virtual ~Connection();
+
+ // value that was passed into dbdir when this connection was created.
+ const std::string &dbdir();
+
+ // concatenation of dbdir and dbname
+ const std::string &dbpath();
+
+ Statement prepare(const std::string &format, ...);
+ Result perform(Statement &statement);
+ bool execute(Statement &statement);
+
+ bool connect(const char *connectionLabel = NULL);
+ void close();
+
+ bool tableExists(const std::string &tablename);
+ long long lastInsertRowId();
+
+ bool inTransaction();
+ bool beginTransactionRO();
+ bool endTransactionRO();
+ bool beginTransactionRW();
+ bool commitTransaction();
+ bool rollbackTransaction();
+
+ // Set the busy timeout that the database layer will wait for a database lock to become available.
+ bool setBusyTimeout(int ms);
+private:
+ std::string _dbdir;
+ std::string _dbpath;
+ sqlite3 *_db;
+
+ Connection(const std::string &dbdir, const std::string &dbname);
+
+ // disable evil constructors
+ Connection(const Connection &);
+ void operator=(const Connection&);
+};
+
+}
+
+#endif // !_SOFTHSM_V2_DB_H
diff --git a/SoftHSMv2/src/lib/object_store/DBObject.cpp b/SoftHSMv2/src/lib/object_store/DBObject.cpp
new file mode 100644
index 0000000..d2515bd
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DBObject.cpp
@@ -0,0 +1,1493 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBObject.h
+
+ This class represents object records in a database
+ *****************************************************************************/
+
+#include "config.h"
+#include "DBObject.h"
+#include "OSPathSep.h"
+#include "DB.h"
+#include "OSAttributes.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <map>
+
+// Create an object that can access a record, but don't do anything yet.
+DBObject::DBObject(DB::Connection *connection, ObjectStoreToken *token)
+ : _mutex(MutexFactory::i()->getMutex()), _connection(connection), _token(token), _objectId(0), _transaction(NULL)
+{
+
+}
+
+DBObject::DBObject(DB::Connection *connection, ObjectStoreToken *token, long long objectId)
+ : _mutex(MutexFactory::i()->getMutex()), _connection(connection), _token(token), _objectId(objectId), _transaction(NULL)
+{
+}
+
+// Destructor
+DBObject::~DBObject()
+{
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _attributes.begin(); it!=_attributes.end(); ++it) {
+ delete it->second;
+ it->second = NULL;
+ }
+ if (_transaction)
+ {
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _transaction->begin(); it!=_transaction->end(); ++it) {
+ delete it->second;
+ it->second = NULL;
+ }
+ delete _transaction;
+ }
+ MutexFactory::i()->recycleMutex(_mutex);
+}
+
+void DBObject::dropConnection()
+{
+ MutexLocker lock(_mutex);
+
+ _connection = NULL;
+}
+
+// create tables to support storage of attributes for the DBObject
+bool DBObject::createTables()
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ // Create the tables inside the database
+ DB::Statement cr_object = _connection->prepare("create table object (id integer primary key autoincrement);");
+ if (!_connection->execute(cr_object))
+ {
+ ERROR_MSG("Failed to create \"object\" table");
+ return false;
+ }
+
+ // attribute_text
+ DB::Statement cr_attr_text = _connection->prepare(
+ "create table attribute_text ("
+ "value text,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_text))
+ {
+ ERROR_MSG("Failed to create \"attribute_text\" table");
+ return false;
+ }
+
+ // attribute_integer
+ DB::Statement cr_attr_integer = _connection->prepare(
+ "create table attribute_integer ("
+ "value integer,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_integer))
+ {
+ ERROR_MSG("Failed to create \"attribute_integer\" table");
+ return false;
+ }
+
+ // attribute_binary
+ DB::Statement cr_attr_binary = _connection->prepare(
+ "create table attribute_binary ("
+ "value blob,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_binary))
+ {
+ ERROR_MSG("Failed to create \"attribute_binary\" table");
+ return false;
+ }
+
+ // attribute_array
+ DB::Statement cr_attr_array = _connection->prepare(
+ "create table attribute_array ("
+ "value blob,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_array))
+ {
+ ERROR_MSG("Failed to create \"attribute_array\" table");
+ return false;
+ }
+
+ // attribute_boolean
+ DB::Statement cr_attr_boolean = _connection->prepare(
+ "create table attribute_boolean ("
+ "value boolean,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_boolean))
+ {
+ ERROR_MSG("Failed to create \"attribute_boolean\" table");
+ return false;
+ }
+
+ // attribute_datetime
+ DB::Statement cr_attr_datetime = _connection->prepare(
+ "create table attribute_datetime ("
+ "value datetime,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_datetime))
+ {
+ ERROR_MSG("Failed to create \"attribute_datetime\" table");
+ return false;
+ }
+
+ // attribute_real
+ DB::Statement cr_attr_real = _connection->prepare(
+ "create table attribute_real ("
+ "value real,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ if (!_connection->execute(cr_attr_real))
+ {
+ ERROR_MSG("Failed to create \"attribute_real\" table");
+ return false;
+ }
+
+ return true;
+}
+
+bool DBObject::dropTables()
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ // Create the tables inside the database
+ DB::Statement dr_object = _connection->prepare("drop table object");
+ if (!_connection->execute(dr_object))
+ {
+ ERROR_MSG("Failed to drop \"object\" table");
+ return false;
+ }
+
+ // attribute_text
+ DB::Statement dr_attr_text = _connection->prepare("drop table attribute_text");
+ if (!_connection->execute(dr_attr_text))
+ {
+ ERROR_MSG("Failed to drop \"attribute_text\" table");
+ return false;
+ }
+
+ // attribute_integer
+ DB::Statement dr_attr_integer = _connection->prepare("drop table attribute_integer");
+ if (!_connection->execute(dr_attr_integer))
+ {
+ ERROR_MSG("Failed to drop \"attribute_integer\" table");
+ return false;
+ }
+
+ // attribute_binary
+ DB::Statement dr_attr_binary = _connection->prepare("drop table attribute_binary");
+ if (!_connection->execute(dr_attr_binary))
+ {
+ ERROR_MSG("Failed to drop \"attribute_binary\" table");
+ return false;
+ }
+
+ // attribute_array
+ DB::Statement dr_attr_array = _connection->prepare("drop table attribute_array");
+ if (!_connection->execute(dr_attr_array))
+ {
+ ERROR_MSG("Failed to drop \"attribute_array\" table");
+ return false;
+ }
+
+ // attribute_boolean
+ DB::Statement dr_attr_boolean = _connection->prepare("drop table attribute_boolean");
+ if (!_connection->execute(dr_attr_boolean))
+ {
+ ERROR_MSG("Failed to drop \"attribute_boolean\" table");
+ return false;
+ }
+
+ // attribute_datetime
+ DB::Statement dr_attr_datetime = _connection->prepare("drop table attribute_datetime");
+ if (!_connection->execute(dr_attr_datetime))
+ {
+ ERROR_MSG("Failed to drop \"attribute_datetime\" table");
+ return false;
+ }
+
+ // attribute_real
+ DB::Statement dr_attr_real = _connection->prepare("drop table attribute_real");
+ if (!_connection->execute(dr_attr_real))
+ {
+ ERROR_MSG("Failed to drop \"attribute_real\" table");
+ return false;
+ }
+
+ return true;
+}
+
+bool DBObject::find(long long objectId)
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ if (objectId == 0) {
+ ERROR_MSG("Invalid object_id 0 passed to find");
+ return false;
+ }
+
+ // find the object in the database for the given object_id
+ DB::Statement statement = _connection->prepare(
+ "select id from object where id=%lld",
+ objectId);
+ if (!statement.isValid()) {
+ ERROR_MSG("Preparing object selection statement failed");
+ return false;
+ }
+
+ DB::Result result = _connection->perform(statement);
+ if (result.getLongLong(1) != objectId) {
+ ERROR_MSG("Failed to find object with id %lld",objectId);
+ return false;
+ }
+
+ _objectId = objectId;
+ return true;
+}
+
+bool DBObject::insert()
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ DB::Statement statement = _connection->prepare("insert into object default values");
+
+ if (!_connection->execute(statement)) {
+ ERROR_MSG("Failed to insert a new object");
+ return false;
+ }
+
+ _objectId = _connection->lastInsertRowId();
+ return _objectId != 0;
+}
+
+bool DBObject::remove()
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ DB::Statement statement = _connection->prepare("delete from object where id=%lld",_objectId);
+
+ if (!_connection->execute(statement)) {
+ ERROR_MSG("Failed to remove an existing object");
+ return false;
+ }
+
+ _objectId = 0;
+ return true;
+}
+
+long long DBObject::objectId()
+{
+ MutexLocker lock(_mutex);
+
+ return _objectId;
+}
+
+static bool isModifiable(CK_ATTRIBUTE_TYPE type)
+{
+ switch (type) {
+ case CKA_LABEL:
+ case CKA_TRUSTED:
+ case CKA_ID:
+ case CKA_ISSUER:
+ case CKA_SERIAL_NUMBER:
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ case CKA_DERIVE:
+ case CKA_SUBJECT:
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ case CKA_SENSITIVE:
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ case CKA_EXTRACTABLE:
+ case CKA_OS_TOKENFLAGS:
+ case CKA_OS_SOPIN:
+ case CKA_OS_USERPIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+enum AttributeKind {
+ akUnknown,
+ akBoolean,
+ akInteger,
+ akBinary,
+ akAttrMap,
+ akMechSet
+};
+
+static AttributeKind attributeKind(CK_ATTRIBUTE_TYPE type)
+{
+ switch (type) {
+ case CKA_CLASS: return akInteger;
+ case CKA_TOKEN: return akBoolean;
+ case CKA_PRIVATE: return akBoolean;
+ case CKA_LABEL: return akBinary;
+ case CKA_APPLICATION: return akBinary;
+ case CKA_VALUE: return akBinary;
+ case CKA_OBJECT_ID: return akBinary;
+ case CKA_CERTIFICATE_TYPE: return akInteger;
+ case CKA_ISSUER: return akBinary;
+ case CKA_SERIAL_NUMBER: return akBinary;
+ case CKA_AC_ISSUER: return akBinary;
+ case CKA_OWNER: return akBinary;
+ case CKA_ATTR_TYPES: return akBinary;
+ case CKA_TRUSTED: return akBoolean;
+ case CKA_CERTIFICATE_CATEGORY: return akInteger;
+ case CKA_JAVA_MIDP_SECURITY_DOMAIN: return akInteger;
+ case CKA_URL: return akBinary;
+ case CKA_HASH_OF_SUBJECT_PUBLIC_KEY: return akBinary;
+ case CKA_HASH_OF_ISSUER_PUBLIC_KEY: return akBinary;
+ case CKA_NAME_HASH_ALGORITHM: return akInteger;
+ case CKA_CHECK_VALUE: return akBinary;
+ case CKA_KEY_TYPE: return akInteger;
+ case CKA_SUBJECT: return akBinary;
+ case CKA_ID: return akBinary;
+ case CKA_SENSITIVE: return akBoolean;
+ case CKA_ENCRYPT: return akBoolean;
+ case CKA_DECRYPT: return akBoolean;
+ case CKA_WRAP: return akBoolean;
+ case CKA_UNWRAP: return akBoolean;
+ case CKA_SIGN: return akBoolean;
+ case CKA_SIGN_RECOVER: return akBoolean;
+ case CKA_VERIFY: return akBoolean;
+ case CKA_VERIFY_RECOVER: return akBoolean;
+ case CKA_DERIVE: return akBoolean;
+ case CKA_START_DATE: return akBinary;
+ case CKA_END_DATE: return akBinary;
+ case CKA_MODULUS: return akBinary;
+ case CKA_MODULUS_BITS: return akInteger;
+ case CKA_PUBLIC_EXPONENT: return akBinary;
+ case CKA_PRIVATE_EXPONENT: return akBinary;
+ case CKA_PRIME_1: return akBinary;
+ case CKA_PRIME_2: return akBinary;
+ case CKA_EXPONENT_1: return akBinary;
+ case CKA_EXPONENT_2: return akBinary;
+ case CKA_COEFFICIENT: return akBinary;
+ case CKA_PRIME: return akBinary;
+ case CKA_SUBPRIME: return akBinary;
+ case CKA_BASE: return akBinary;
+ case CKA_PRIME_BITS: return akInteger;
+ case CKA_SUBPRIME_BITS: return akInteger;
+ case CKA_VALUE_BITS: return akInteger;
+ case CKA_VALUE_LEN: return akInteger;
+ case CKA_EXTRACTABLE: return akBoolean;
+ case CKA_LOCAL: return akBoolean;
+ case CKA_NEVER_EXTRACTABLE: return akBoolean;
+ case CKA_ALWAYS_SENSITIVE: return akBoolean;
+ case CKA_KEY_GEN_MECHANISM: return akInteger;
+ case CKA_MODIFIABLE: return akBoolean;
+ case CKA_COPYABLE: return akBoolean;
+ case CKA_ECDSA_PARAMS: return akBinary;
+ case CKA_EC_POINT: return akBinary;
+ case CKA_SECONDARY_AUTH: return akBoolean;
+ case CKA_AUTH_PIN_FLAGS: return akInteger;
+ case CKA_ALWAYS_AUTHENTICATE: return akBoolean;
+ case CKA_WRAP_WITH_TRUSTED: return akBoolean;
+/*
+ case CKA_OTP_FORMAT:
+ case CKA_OTP_LENGTH:
+ case CKA_OTP_TIME_INTERVAL:
+ case CKA_OTP_USER_FRIENDLY_MODE:
+ case CKA_OTP_CHALLENGE_REQUIREMENT:
+ case CKA_OTP_TIME_REQUIREMENT:
+ case CKA_OTP_COUNTER_REQUIREMENT:
+ case CKA_OTP_PIN_REQUIREMENT:
+ case CKA_OTP_COUNTER:
+ case CKA_OTP_TIME:
+ case CKA_OTP_USER_IDENTIFIER:
+ case CKA_OTP_SERVICE_IDENTIFIER:
+ case CKA_OTP_SERVICE_LOGO:
+ case CKA_OTP_SERVICE_LOGO_TYPE:
+*/
+ case CKA_GOSTR3410_PARAMS: return akBinary;
+ case CKA_GOSTR3411_PARAMS: return akBinary;
+ case CKA_GOST28147_PARAMS: return akBinary;
+/*
+ case CKA_HW_FEATURE_TYPE:
+ case CKA_RESET_ON_INIT:
+ case CKA_HAS_RESET:
+ case CKA_PIXEL_X:
+ case CKA_PIXEL_Y:
+ case CKA_RESOLUTION:
+ case CKA_CHAR_ROWS:
+ case CKA_CHAR_COLUMNS:
+ case CKA_COLOR:
+ case CKA_BITS_PER_PIXEL:
+ case CKA_CHAR_SETS:
+ case CKA_ENCODING_METHODS:
+ case CKA_MIME_TYPES:
+ case CKA_MECHANISM_TYPE:
+ case CKA_REQUIRED_CMS_ATTRIBUTES:
+ case CKA_DEFAULT_CMS_ATTRIBUTES:
+ case CKA_SUPPORTED_CMS_ATTRIBUTES:
+*/
+ case CKA_WRAP_TEMPLATE: return akAttrMap;
+ case CKA_UNWRAP_TEMPLATE: return akAttrMap;
+ case CKA_DERIVE_TEMPLATE: return akAttrMap;
+ case CKA_ALLOWED_MECHANISMS: return akMechSet;
+
+ case CKA_OS_TOKENLABEL: return akBinary;
+ case CKA_OS_TOKENSERIAL: return akBinary;
+ case CKA_OS_TOKENFLAGS: return akInteger;
+ case CKA_OS_SOPIN: return akBinary;
+ case CKA_OS_USERPIN: return akBinary;
+
+ default: return akUnknown;
+ }
+}
+
+static bool decodeMechanismTypeSet(std::set<CK_MECHANISM_TYPE>& set, const unsigned char *binary, size_t size)
+{
+ for (size_t pos = 0; pos < size; )
+ {
+ // finished?
+ if (pos == size) break;
+
+ CK_MECHANISM_TYPE mechType;
+ if (pos + sizeof(mechType) > size)
+ {
+ ERROR_MSG("mechanism type set overrun");
+ return false;
+ }
+
+ memcpy(&mechType, binary + pos, sizeof(mechType));
+ pos += sizeof(mechType);
+
+ set.insert(mechType);
+ }
+
+ return true;
+}
+
+static void encodeMechanismTypeSet(ByteString& value, const std::set<CK_MECHANISM_TYPE>& set)
+{
+ for (std::set<CK_MECHANISM_TYPE>::const_iterator i = set.begin(); i != set.end(); ++i)
+ {
+ CK_MECHANISM_TYPE mechType = *i;
+ value += ByteString((unsigned char *) &mechType, sizeof(mechType));
+ }
+}
+
+static bool decodeAttributeMap(std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& map, const unsigned char *binary, size_t size)
+{
+ for (size_t pos = 0; pos < size; )
+ {
+ // finished?
+ if (pos == size) break;
+
+ CK_ATTRIBUTE_TYPE attrType;
+ if (pos + sizeof(attrType) > size)
+ {
+ goto overrun;
+ }
+ memcpy(&attrType, binary + pos, sizeof(attrType));
+ pos += sizeof(attrType);
+
+ AttributeKind attrKind;
+ if (pos + sizeof(AttributeKind) > size)
+ {
+ goto overrun;
+ }
+ memcpy(&attrKind, binary + pos, sizeof(attrKind));
+ pos += sizeof(attrKind);
+
+ // Verify using attributeKind()?
+
+ switch (attrKind)
+ {
+ case akBoolean:
+ {
+ bool value;
+ if (pos + sizeof(value) > size)
+ {
+ goto overrun;
+ }
+ memcpy(&value, binary + pos, sizeof(value));
+ pos += sizeof(value);
+
+ map.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, value));
+ }
+ break;
+
+ case akInteger:
+ {
+ unsigned long value;
+ if (pos + sizeof(value) > size)
+ {
+ goto overrun;
+ }
+ memcpy(&value, binary + pos, sizeof(value));
+ pos += sizeof(value);
+
+ map.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, value));
+ }
+ break;
+
+ case akBinary:
+ {
+ ByteString value;
+ unsigned long len;
+ if (pos + sizeof(len) > size)
+ {
+ goto overrun;
+ }
+ memcpy(&len, binary + pos, sizeof(len));
+ pos += sizeof(len);
+
+ if (pos + len > size)
+ {
+ goto overrun;
+ }
+ value.resize(len);
+ memcpy(&value[0], binary + pos, len);
+ pos += len;
+
+ map.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, value));
+ }
+ break;
+
+ case akMechSet:
+ {
+ unsigned long len;
+ if (pos + sizeof(len) > size)
+ {
+ goto overrun;
+ }
+ memcpy(&len, binary + pos, sizeof(len));
+ pos += sizeof(len);
+
+ if (pos + len > size)
+ {
+ goto overrun;
+ }
+
+ std::set<CK_MECHANISM_TYPE> value;
+ if (!decodeMechanismTypeSet(value, binary + pos, len)) {
+ return false;
+ }
+ pos += len;
+
+ map.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, value));
+ }
+ break;
+
+ default:
+ ERROR_MSG("unsupported attribute kind in attribute map");
+
+ return false;
+ }
+ }
+
+ return true;
+
+overrun:
+ ERROR_MSG("attribute map template overrun");
+
+ return false;
+}
+
+static bool encodeAttributeMap(ByteString& value, const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& attributes)
+{
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute>::const_iterator i = attributes.begin(); i != attributes.end(); ++i)
+ {
+ CK_ATTRIBUTE_TYPE attrType = i->first;
+ value += ByteString((unsigned char*) &attrType, sizeof(attrType));
+
+ OSAttribute attr = i->second;
+ if (attr.isBooleanAttribute())
+ {
+ AttributeKind attrKind = akBoolean;
+ value += ByteString((unsigned char*) &attrKind, sizeof(attrKind));
+
+ bool val = attr.getBooleanValue();
+ value += ByteString((unsigned char*) &val, sizeof(val));
+ }
+ else if (attr.isUnsignedLongAttribute())
+ {
+ AttributeKind attrKind = akInteger;
+ value += ByteString((unsigned char*) &attrKind, sizeof(attrKind));
+
+ unsigned long val = attr.getUnsignedLongValue();
+ value += ByteString((unsigned char*) &val, sizeof(val));
+ }
+ else if (attr.isByteStringAttribute())
+ {
+ AttributeKind attrKind = akBinary;
+ value += ByteString((unsigned char*) &attrKind, sizeof(attrKind));
+
+ ByteString val = attr.getByteStringValue();
+ unsigned long len = val.size();
+ value += ByteString((unsigned char*) &len, sizeof(len));
+ value += val;
+ }
+ else if (attr.isMechanismTypeSetAttribute())
+ {
+ AttributeKind attrKind = akMechSet;
+ value += ByteString((unsigned char*) &attrKind, sizeof(attrKind));
+
+ ByteString val;
+ encodeMechanismTypeSet(val, attr.getMechanismTypeSetValue());
+
+ unsigned long len = val.size();
+ value += ByteString((unsigned char*) &len, sizeof(len));
+ value += val;
+ }
+ else
+ {
+ ERROR_MSG("unsupported attribute kind for attribute map");
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+OSAttribute *DBObject::accessAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ switch (attributeKind(type))
+ {
+ case akUnknown:
+ return NULL;
+ case akBoolean:
+ {
+ // try to find the attribute in the boolean attribute table
+ DB::Statement statement = _connection->prepare(
+ "select value from attribute_boolean where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ if (!statement.isValid())
+ {
+ return NULL;
+ }
+ DB::Result result = _connection->perform(statement);
+ if (!result.isValid())
+ {
+ return NULL;
+ }
+ // Store the attribute in the transaction when it is active.
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> *attrs = &_attributes;
+ if (_transaction)
+ attrs = _transaction;
+
+ bool value = result.getInt(1) != 0;
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = attrs->find(type);
+ OSAttribute *attr;
+ if (it != attrs->end())
+ {
+ if (it->second != NULL)
+ {
+ delete it->second;
+ }
+
+ it->second = new OSAttribute(value);
+ attr = it->second;
+ }
+ else
+ {
+ attr = new OSAttribute(value);
+ (*attrs)[type] = attr;
+ }
+ return attr;
+ }
+ case akInteger:
+ {
+ // try to find the attribute in the integer attribute table
+ DB::Statement statement = _connection->prepare(
+ "select value from attribute_integer where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ if (!statement.isValid())
+ {
+ return NULL;
+ }
+ DB::Result result = _connection->perform(statement);
+ if (!result.isValid())
+ {
+ return NULL;
+ }
+ // Store the attribute in the transaction when it is active.
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> *attrs = &_attributes;
+ if (_transaction)
+ attrs = _transaction;
+
+ unsigned long value = result.getULongLong(1);
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = attrs->find(type);
+ OSAttribute *attr;
+ if (it != attrs->end())
+ {
+ if (it->second != NULL)
+ {
+ delete it->second;
+ }
+
+ it->second = new OSAttribute(value);
+ attr = it->second;
+ }
+ else
+ {
+ attr = new OSAttribute(value);
+ (*attrs)[type] = attr;
+ }
+ return attr;
+ }
+ case akBinary:
+ {
+ // try to find the attribute in the binary attribute table
+ DB::Statement statement = _connection->prepare(
+ "select value from attribute_binary where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ if (!statement.isValid())
+ {
+ return NULL;
+ }
+ DB::Result result = _connection->perform(statement);
+ if (!result.isValid())
+ {
+ return NULL;
+ }
+ // Store the attribute in the transaction when it is active.
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> *attrs = &_attributes;
+ if (_transaction)
+ attrs = _transaction;
+
+ const unsigned char *value = result.getBinary(1);
+ size_t size = result.getFieldLength(1);
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = attrs->find(type);
+ OSAttribute *attr;
+ if (it != attrs->end())
+ {
+ if (it->second != NULL)
+ {
+ delete it->second;
+ }
+
+ it->second = new OSAttribute(ByteString(value,size));
+ attr = it->second;
+ }
+ else
+ {
+ attr = new OSAttribute(ByteString(value,size));
+ (*attrs)[type] = attr;
+ return attr;
+ }
+ return attr;
+ }
+ case akMechSet:
+ {
+ // try to find the attribute in the binary attribute table
+ DB::Statement statement = _connection->prepare(
+ "select value from attribute_binary where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ if (!statement.isValid())
+ {
+ return NULL;
+ }
+ DB::Result result = _connection->perform(statement);
+ if (!result.isValid())
+ {
+ return NULL;
+ }
+ // Store the attribute in the transaction when it is active.
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> *attrs = &_attributes;
+ if (_transaction)
+ attrs = _transaction;
+
+ const unsigned char *value = result.getBinary(1);
+ size_t size = result.getFieldLength(1);
+
+ std::set<CK_MECHANISM_TYPE> set;
+ if (!decodeMechanismTypeSet(set, value, size))
+ {
+ return NULL;
+ }
+
+ OSAttribute *attr;
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = attrs->find(type);
+ if (it != attrs->end())
+ {
+ if (it->second != NULL)
+ {
+ delete it->second;
+ }
+
+ it->second = new OSAttribute(set);
+ attr = it->second;
+ }
+ else
+ {
+ attr = new OSAttribute(set);
+ (*attrs)[type] = attr;
+ return attr;
+ }
+ return attr;
+ }
+ case akAttrMap:
+ {
+ // try to find the attribute in the array attribute table
+ DB::Statement statement = _connection->prepare(
+ "select value from attribute_array where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ if (!statement.isValid())
+ {
+ return NULL;
+ }
+ DB::Result result = _connection->perform(statement);
+ if (!result.isValid())
+ {
+ return NULL;
+ }
+ // Store the attribute in the transaction when it is active.
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> *attrs = &_attributes;
+ if (_transaction)
+ attrs = _transaction;
+
+ const unsigned char *binary = result.getBinary(1);
+ size_t size = result.getFieldLength(1);
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = attrs->find(type);
+ OSAttribute *attr;
+ if (it != attrs->end())
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> value;
+ if (!decodeAttributeMap(value,binary,size))
+ {
+ return NULL;
+ }
+
+ if (it->second != NULL)
+ {
+ delete it->second;
+ }
+
+ it->second = new OSAttribute(value);
+ attr = it->second;
+ }
+ else
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> value;
+ if (!decodeAttributeMap(value,binary,size))
+ {
+ return NULL;
+ }
+ attr = new OSAttribute(value);
+ (*attrs)[type] = attr;
+ return attr;
+ }
+ return attr;
+ }
+ }
+
+ return NULL;
+}
+
+// Retrieve the specified attribute for internal use
+// Calling function must lock the mutex
+OSAttribute* DBObject::getAttributeDB(CK_ATTRIBUTE_TYPE type)
+{
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return NULL;
+ }
+
+ if (_objectId == 0)
+ {
+ ERROR_MSG("Cannot read from invalid object.");
+ return NULL;
+ }
+
+ // If a transaction is in progress, we can just return the attribute from the transaction.
+ if (_transaction)
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _transaction->find(type);
+ if (it != _transaction->end())
+ return it->second;
+ }
+
+ // If the attribute exists and is non-modifiable then return a previously retrieved attribute value.
+ if (!isModifiable(type))
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _attributes.find(type);
+ if (it != _attributes.end())
+ {
+ return it->second;
+ }
+ }
+
+ return accessAttribute(type);
+}
+
+// Check if the specified attribute exists
+bool DBObject::attributeExists(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(_mutex);
+
+ return getAttributeDB(type) != NULL;
+}
+
+// Retrieve the specified attribute
+OSAttribute DBObject::getAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(_mutex);
+
+ OSAttribute* attr = getAttributeDB(type);
+ if (attr == NULL) return OSAttribute((unsigned long)0);
+
+ return *attr;
+}
+
+bool DBObject::getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val)
+{
+ MutexLocker lock(_mutex);
+
+ OSAttribute* attr = getAttributeDB(type);
+ if (attr == NULL) return val;
+
+ if (attr->isBooleanAttribute())
+ {
+ return attr->getBooleanValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not a boolean: 0x%08X", type);
+ return val;
+ }
+}
+
+unsigned long DBObject::getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val)
+{
+ MutexLocker lock(_mutex);
+
+ OSAttribute* attr = getAttributeDB(type);
+ if (attr == NULL) return val;
+
+ if (attr->isUnsignedLongAttribute())
+ {
+ return attr->getUnsignedLongValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not an unsigned long: 0x%08X", type);
+ return val;
+ }
+}
+
+ByteString DBObject::getByteStringValue(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(_mutex);
+
+ ByteString val;
+
+ OSAttribute* attr = getAttributeDB(type);
+ if (attr == NULL) return val;
+
+ if (attr->isByteStringAttribute())
+ {
+ return attr->getByteStringValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not a byte string: 0x%08X", type);
+ return val;
+ }
+}
+
+CK_ATTRIBUTE_TYPE DBObject::nextAttributeType(CK_ATTRIBUTE_TYPE)
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+ if (_objectId == 0)
+ {
+ ERROR_MSG("Cannot get next attribute for invalid object.");
+ return false;
+ }
+
+ // FIXME: implement for C_CopyObject
+ return CKA_CLASS;
+}
+
+// Set the specified attribute
+bool DBObject::setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute)
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+ if (_objectId == 0)
+ {
+ ERROR_MSG("Cannot update invalid object.");
+ return false;
+ }
+
+ // Retrieve and existing attribute if it exists or NULL if it doesn't
+ OSAttribute *attr = getAttributeDB(type);
+
+ // Update an existing attribute...
+ if (attr)
+ {
+ DB::Statement statement;
+ if (attr->isBooleanAttribute())
+ {
+ // update boolean attribute
+ statement = _connection->prepare(
+ "update attribute_boolean set value=%d where type=%lu and object_id=%lld",
+ attribute.getBooleanValue() ? 1 : 0,
+ type,
+ _objectId);
+ }
+ else if (attr->isUnsignedLongAttribute())
+ {
+ // update integer attribute
+ statement = _connection->prepare(
+ "update attribute_integer set value=%lld where type=%lu and object_id=%lld",
+ static_cast<long long>(attribute.getUnsignedLongValue()),
+ type,
+ _objectId);
+ }
+ else if (attr->isByteStringAttribute())
+ {
+ // update binary attribute
+ statement = _connection->prepare(
+ "update attribute_binary set value=? where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ DB::Bindings(statement).bindBlob(1, attribute.getByteStringValue().const_byte_str(), attribute.getByteStringValue().size(), SQLITE_STATIC);
+ }
+ else if (attr->isMechanismTypeSetAttribute())
+ {
+ // update binary attribute
+ ByteString value;
+ encodeMechanismTypeSet(value, attribute.getMechanismTypeSetValue());
+
+ statement = _connection->prepare(
+ "update attribute_binary set value=? where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ DB::Bindings(statement).bindBlob(1, value.const_byte_str(), value.size(), SQLITE_TRANSIENT);
+ }
+ else if (attr->isAttributeMapAttribute())
+ {
+ // update attribute map attribute
+ ByteString value;
+ if (!encodeAttributeMap(value, attribute.getAttributeMapValue()))
+ {
+ return false;
+ }
+
+ statement = _connection->prepare(
+ "update attribute_array set value=? where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ DB::Bindings(statement).bindBlob(1, value.const_byte_str(), value.size(), SQLITE_TRANSIENT);
+ }
+
+ // Statement is valid when a prepared statement has been attached to it.
+ if (statement.isValid())
+ {
+ if (!_connection->execute(statement))
+ {
+ ERROR_MSG("Failed to update attribute %lu for object %lld",type,_objectId);
+ return false;
+ }
+
+ if (_transaction)
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _transaction->find(type);
+ if (it != _transaction->end())
+ *it->second = attribute;
+ else
+ (*_transaction)[type] = new OSAttribute(attribute);
+ } else
+ *attr = attribute;
+ return true;
+ }
+ }
+
+ DB::Statement statement;
+
+ // Insert the attribute, because it is currently unknown
+ if (attribute.isBooleanAttribute())
+ {
+ // Could not update it, so we need to insert it.
+ statement = _connection->prepare(
+ "insert into attribute_boolean (value,type,object_id) values (%d,%lu,%lld)",
+ attribute.getBooleanValue() ? 1 : 0,
+ type,
+ _objectId);
+
+ }
+ else if (attribute.isUnsignedLongAttribute())
+ {
+ // Could not update it, so we need to insert it.
+ statement = _connection->prepare(
+ "insert into attribute_integer (value,type,object_id) values (%lld,%lu,%lld)",
+ static_cast<long long>(attribute.getUnsignedLongValue()),
+ type,
+ _objectId);
+ }
+ else if (attribute.isByteStringAttribute())
+ {
+ // Could not update it, so we need to insert it.
+ statement = _connection->prepare(
+ "insert into attribute_binary (value,type,object_id) values (?,%lu,%lld)",
+ type,
+ _objectId);
+
+ DB::Bindings(statement).bindBlob(1, attribute.getByteStringValue().const_byte_str(), attribute.getByteStringValue().size(), SQLITE_STATIC);
+ }
+ else if (attribute.isMechanismTypeSetAttribute())
+ {
+ // Could not update it, so we need to insert it.
+ ByteString value;
+ encodeMechanismTypeSet(value, attribute.getMechanismTypeSetValue());
+
+ statement = _connection->prepare(
+ "insert into attribute_binary (value,type,object_id) values (?,%lu,%lld)",
+ type,
+ _objectId);
+ DB::Bindings(statement).bindBlob(1, value.const_byte_str(), value.size(), SQLITE_TRANSIENT);
+ }
+ else if (attribute.isAttributeMapAttribute())
+ {
+ // Could not update it, so we need to insert it.
+ ByteString value;
+ if (!encodeAttributeMap(value, attribute.getAttributeMapValue()))
+ {
+ return false;
+ }
+
+ statement = _connection->prepare(
+ "insert into attribute_array (value,type,object_id) values (?,%lu,%lld)",
+ type,
+ _objectId);
+ DB::Bindings(statement).bindBlob(1, value.const_byte_str(), value.size(), SQLITE_TRANSIENT);
+ }
+
+ // Statement is valid when a prepared statement has been attached to it.
+ if (statement.isValid())
+ {
+ if (!_connection->execute(statement))
+ {
+ ERROR_MSG("Failed to insert attribute %lu for object %lld",type,_objectId);
+ return false;
+ }
+
+ if (_transaction)
+ (*_transaction)[type] = new OSAttribute(attribute);
+ else
+ _attributes[type] = new OSAttribute(attribute);
+ return true;
+ }
+
+ return false;
+}
+
+// Set the specified attribute
+bool DBObject::deleteAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+ if (_objectId == 0)
+ {
+ ERROR_MSG("Cannot update invalid object.");
+ return false;
+ }
+
+ // Retrieve and existing attribute if it exists or NULL if it doesn't
+ OSAttribute *attr = getAttributeDB(type);
+ if (attr == NULL)
+ {
+ ERROR_MSG("Cannot delete an attribute that doesn't exist.");
+ return false;
+ }
+
+ DB::Statement statement;
+ if (attr->isBooleanAttribute())
+ {
+ // delete boolean attribute
+ statement = _connection->prepare(
+ "delete from attribute_boolean where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ }
+ else if (attr->isUnsignedLongAttribute())
+ {
+ // delete integer attribute
+ statement = _connection->prepare(
+ "delete from attribute_integer where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ }
+ else if (attr->isByteStringAttribute() || attr -> isMechanismTypeSetAttribute())
+ {
+ // delete binary attribute
+ statement = _connection->prepare(
+ "delete from attribute_binary where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ }
+ else if (attr->isAttributeMapAttribute())
+ {
+ // delete attribute map attribute
+ statement = _connection->prepare(
+ "delete from attribute_array where type=%lu and object_id=%lld",
+ type,
+ _objectId);
+ }
+
+ // Statement is valid when a prepared statement has been attached to it.
+ if (statement.isValid())
+ {
+ if (!_connection->execute(statement))
+ {
+ ERROR_MSG("Failed to delete attribute %lu for object %lld",type,_objectId);
+ return false;
+ }
+
+ if (_transaction)
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _transaction->find(type);
+ if (it != _transaction->end())
+ {
+ delete it->second;
+ it->second = NULL;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// The validity state of the object
+bool DBObject::isValid()
+{
+ MutexLocker lock(_mutex);
+
+ return _objectId != 0 && _connection != NULL;
+}
+
+// Start an attribute set transaction; this method is used when - for
+// example - a key is generated and all its attributes need to be
+// persisted in one go.
+//
+// N.B.: Starting a transaction locks the object!
+bool DBObject::startTransaction(Access access)
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ if (_transaction)
+ {
+ ERROR_MSG("Transaction is already active.");
+ return false;
+ }
+
+ _transaction = new std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>;
+ if (_transaction == NULL)
+ {
+ ERROR_MSG("Not enough memory to start transaction.");
+ return false;
+ }
+
+ if (_connection->inTransaction())
+ {
+ ERROR_MSG("Transaction in database is already active.");
+ return false;
+ }
+
+ // Ask the connection to start the transaction.
+ if (access == ReadWrite)
+ return _connection->beginTransactionRW();
+ else
+ return _connection->beginTransactionRO();
+}
+
+// Commit an attribute transaction
+bool DBObject::commitTransaction()
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ if (_transaction == NULL)
+ {
+ ERROR_MSG("No transaction active.");
+ return false;
+ }
+
+ if (!_connection->commitTransaction())
+ {
+ return false;
+ }
+
+ // Copy the values from the internally stored transaction to the _attributes field.
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _transaction->begin(); it!=_transaction->end(); ++it) {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator attr_it = _attributes.find(it->first);
+ if (attr_it == _attributes.end())
+ {
+ _attributes[it->first] = it->second;
+ }
+ else
+ {
+ *attr_it->second = *it->second;
+ delete it->second;
+ }
+ it->second = NULL;
+ }
+ delete _transaction;
+ _transaction = NULL;
+ return true;
+}
+
+// Abort an attribute transaction; loads back the previous version of the object from disk
+bool DBObject::abortTransaction()
+{
+ MutexLocker lock(_mutex);
+
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Object is not connected to the database.");
+ return false;
+ }
+
+ // Forget the atributes that were set during the transaction.
+ if (_transaction)
+ {
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute*>::iterator it = _transaction->begin(); it!=_transaction->end(); ++it) {
+ delete it->second;
+ it->second = NULL;
+ }
+ delete _transaction;
+ _transaction = NULL;
+ }
+
+ return _connection->rollbackTransaction();
+}
+
+// Destroy the object; WARNING: pointers to the object become invalid after this call
+bool DBObject::destroyObject()
+{
+ // NOTE: Do not lock _mutex, because _token will call us back and cause a deadlock.
+ // There is no need to lock anyway as _token is a non-mutable pointer, so no race
+ // conditions possible.
+
+ if (_token == NULL)
+ {
+ ERROR_MSG("Cannot destroy an object that is not associated with a token");
+ return false;
+ }
+
+ return _token->deleteObject(this);
+}
diff --git a/SoftHSMv2/src/lib/object_store/DBObject.h b/SoftHSMv2/src/lib/object_store/DBObject.h
new file mode 100644
index 0000000..4dc1249
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DBObject.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBObject.h
+
+ This class represents object records in a database
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DBOBJECT_H
+#define _SOFTHSM_V2_DBOBJECT_H
+
+#include "config.h"
+#include "OSAttribute.h"
+#include "cryptoki.h"
+#include "OSObject.h"
+#include "ObjectStoreToken.h"
+
+#include "MutexFactory.h"
+#include <string>
+
+namespace DB { class Connection; }
+
+class DBObject : public OSObject
+{
+public:
+ // Constructor for creating or accessing an object, don't do anything yet.
+ DBObject(DB::Connection *connection, ObjectStoreToken *token = NULL);
+
+ // Constructor for accessing an object with an objectId known to exists
+ DBObject(DB::Connection *connection, ObjectStoreToken *token, long long objectId);
+
+ // Destructor
+ virtual ~DBObject();
+
+ // Will drop any internal references to the connection
+ void dropConnection();
+
+ // create tables to support storage of attributes for the object.
+ bool createTables();
+
+ // drop tables that support storage of attributes for the object.
+ bool dropTables();
+
+ // Find an existing object.
+ bool find(long long objectId);
+
+ // Insert a new object into the database and retrieve the object id associated with it.
+ bool insert();
+
+ // Remove an existing object from the database and reset the object id to zero.
+ bool remove();
+
+ // Object id associated with this object.
+ long long objectId();
+
+ // Check if the specified attribute exists
+ virtual bool attributeExists(CK_ATTRIBUTE_TYPE type);
+
+ // Retrieve the specified attribute
+ virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type);
+ virtual bool getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val);
+ virtual unsigned long getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val);
+ virtual ByteString getByteStringValue(CK_ATTRIBUTE_TYPE type);
+
+ // Retrieve the next attribute type
+ virtual CK_ATTRIBUTE_TYPE nextAttributeType(CK_ATTRIBUTE_TYPE type);
+
+ // Set the specified attribute
+ virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute);
+
+ // Delete the specified attribute
+ virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type);
+
+ // The validity state of the object
+ virtual bool isValid();
+
+ // Start an attribute set transaction; this method is used when - for
+ // example - a key is generated and all its attributes need to be
+ // persisted in one go.
+ //
+ // N.B.: Starting a transaction locks the object!
+ //
+ // Function returns false in case a transaction is already in progress
+ virtual bool startTransaction(Access access);
+
+ // Commit an attribute transaction; returns false if no transaction is in progress
+ virtual bool commitTransaction();
+
+ // Abort an attribute transaction; loads back the previous version of the object from disk;
+ // returns false if no transaction was in progress
+ virtual bool abortTransaction();
+
+ // Destroys the object (warning, any pointers to the object are no longer
+ // valid after this call because delete is called!)
+ virtual bool destroyObject();
+
+private:
+ // Disable copy constructor and assignment
+ DBObject();
+ DBObject(const DBObject&);
+ DBObject & operator= (const DBObject &);
+
+ // Mutex object for thread-safeness
+ Mutex* _mutex;
+
+ DB::Connection *_connection;
+ ObjectStoreToken *_token;
+ long long _objectId;
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> _attributes;
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute*> *_transaction;
+
+ OSAttribute* getAttributeDB(CK_ATTRIBUTE_TYPE type);
+ OSAttribute* accessAttribute(CK_ATTRIBUTE_TYPE type);
+};
+
+#endif // !_SOFTHSM_V2_DBOBJECT_H
+
diff --git a/SoftHSMv2/src/lib/object_store/DBToken.cpp b/SoftHSMv2/src/lib/object_store/DBToken.cpp
new file mode 100644
index 0000000..e734372
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DBToken.cpp
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBToken.cpp
+
+ The token class; a token is stored in a directory containing a single
+ database file.
+ Each object is stored in multiple tables with every attribute base type
+ stored in a different table.
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "OSAttributes.h"
+#include "OSAttribute.h"
+#include "OSPathSep.h"
+
+#include "cryptoki.h"
+#include "DBToken.h"
+#include "DBObject.h"
+#include "DB.h"
+
+#include "Directory.h"
+
+#include <vector>
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+#include <cstdio>
+#include <sys/stat.h>
+#include <errno.h>
+
+const char * const DBTOKEN_FILE = "sqlite3.db";
+const long long DBTOKEN_OBJECT_TOKENINFO = 1;
+
+// Constructor for creating a new token.
+DBToken::DBToken(const std::string &baseDir, const std::string &tokenName, const ByteString &label, const ByteString &serial)
+ : _connection(NULL), _tokenMutex(NULL)
+{
+ std::string tokenDir = baseDir + OS_PATHSEP + tokenName;
+ std::string tokenPath = tokenDir + OS_PATHSEP + DBTOKEN_FILE;
+
+ // Refuse to open an already existing database.
+ FILE *f = fopen(tokenPath.c_str(),"r");
+ if (f)
+ {
+ fclose(f);
+ ERROR_MSG("Refusing to overwrite and existing database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ // First create the directory for the token, we expect basePath to already exist
+ if (mkdir(tokenDir.c_str(), S_IFDIR | S_IRWXU))
+ {
+ // Allow the directory to exists already.
+ if (errno != EEXIST)
+ {
+ ERROR_MSG("Unable to create directory \"%s\"", tokenDir.c_str());
+ return;
+ }
+ }
+
+ // Create
+ _connection = DB::Connection::Create(tokenDir, DBTOKEN_FILE);
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Failed to create a database connection for \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ if (!_connection->connect())
+ {
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to connect to the database at \"%s\"", tokenPath.c_str());
+
+ // Now remove the token directory
+ if (remove(tokenDir.c_str()))
+ {
+ ERROR_MSG("Failed to remove the token directory \"%s\"", tokenDir.c_str());
+ }
+
+ return;
+ }
+
+ // Create a DBObject for the established connection to the database.
+ DBObject tokenObject(_connection);
+
+ // First create the tables that support storage of object attributes and then insert the object containing
+ // the token info into the database.
+ if (!tokenObject.createTables() || !tokenObject.insert() || tokenObject.objectId()!=DBTOKEN_OBJECT_TOKENINFO)
+ {
+ tokenObject.dropConnection();
+
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to create tables for storing objects in database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ // Set the initial attributes
+ CK_ULONG flags =
+ CKF_RNG |
+ CKF_LOGIN_REQUIRED | // FIXME: check
+ CKF_RESTORE_KEY_NOT_NEEDED |
+ CKF_TOKEN_INITIALIZED |
+ CKF_SO_PIN_LOCKED |
+ CKF_SO_PIN_TO_BE_CHANGED;
+
+ OSAttribute tokenLabel(label);
+ OSAttribute tokenSerial(serial);
+ OSAttribute tokenFlags(flags);
+
+ if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel) ||
+ !tokenObject.setAttribute(CKA_OS_TOKENSERIAL, tokenSerial) ||
+ !tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
+ {
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ // Now remove the token file
+ if (remove(tokenPath.c_str()))
+ {
+ ERROR_MSG("Failed to remove the token file at \"%s\"", tokenPath.c_str());
+ }
+
+ // Now remove the token directory
+ if (remove(tokenDir.c_str()))
+ {
+ ERROR_MSG("Failed to remove the token directory at \"%s\"", tokenDir.c_str());
+ }
+ return;
+ }
+
+ _tokenMutex = MutexFactory::i()->getMutex();
+ // Success!
+}
+
+// Constructor for accessing an existing token.
+DBToken::DBToken(const std::string &baseDir, const std::string &tokenName)
+ : _connection(NULL), _tokenMutex(NULL)
+{
+ std::string tokenDir = baseDir + OS_PATHSEP + tokenName;
+ std::string tokenPath = tokenDir + OS_PATHSEP + DBTOKEN_FILE;
+
+ // Refuse to open an already existing database.
+ FILE *f = fopen(tokenPath.c_str(),"r");
+ if (f == NULL)
+ {
+ ERROR_MSG("Refusing to open a non-existant database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+ fclose(f);
+
+ // Create a database connection.
+ _connection = DB::Connection::Create(tokenDir, DBTOKEN_FILE);
+ if (_connection == NULL)
+ {
+ ERROR_MSG("Failed to create a database connection for \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ if (!_connection->connect())
+ {
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to connect to the database at \"%s\"", tokenPath.c_str());
+
+ return;
+ }
+
+ // Find the DBObject for the established connection to the database.
+ DBObject tokenObject(_connection);
+
+ // First find the token obect that indicates the token is properly initialized.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ tokenObject.dropConnection();
+
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ ERROR_MSG("Failed to open token object in the token database at \"%s\"", tokenPath.c_str());
+ return;
+ }
+
+ _tokenMutex = MutexFactory::i()->getMutex();
+
+ // Success!
+}
+
+DBToken *DBToken::createToken(const std::string basePath, const std::string tokenDir, const ByteString &label, const ByteString &serial)
+{
+ Directory baseDir(basePath);
+
+ if (!baseDir.isValid())
+ {
+ return NULL;
+ }
+
+ // Create the token directory
+ if (!baseDir.mkdir(tokenDir))
+ {
+ return NULL;
+ }
+
+ DBToken *token = new DBToken(basePath, tokenDir, label, serial);
+ if (!token->isValid())
+ {
+ baseDir.rmdir(tokenDir);
+
+ delete token;
+ return NULL;
+ }
+
+ DEBUG_MSG("Created new token %s", tokenDir.c_str());
+
+ return token;
+}
+
+DBToken *DBToken::accessToken(const std::string &basePath, const std::string &tokenDir)
+{
+ return new DBToken(basePath, tokenDir);
+}
+
+// Destructor
+DBToken::~DBToken()
+{
+ if (_tokenMutex)
+ {
+ MutexFactory::i()->recycleMutex(_tokenMutex);
+ _tokenMutex = NULL;
+ }
+
+ std::map<long long, OSObject*> cleanUp = _allObjects;
+ _allObjects.clear();
+ for (std::map<long long, OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); ++i)
+ {
+ delete i->second;
+ }
+
+ if (_connection)
+ {
+ delete _connection;
+ _connection = NULL;
+ }
+}
+
+// Set the SO PIN
+bool DBToken::setSOPIN(const ByteString& soPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for updating the SOPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute soPIN(soPINBlob);
+ if (!tokenObject.setAttribute(CKA_OS_SOPIN, soPIN))
+ {
+ ERROR_MSG("Error while setting SOPIN in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ // Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
+ CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
+ & ~(CKF_SO_PIN_COUNT_LOW | CKF_SO_PIN_FINAL_TRY | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED);
+
+ OSAttribute changedTokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing SOPIN and TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Get the SO PIN
+bool DBToken::getSOPIN(ByteString& soPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the SOPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_SOPIN))
+ {
+ ERROR_MSG("Error while getting SOPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ soPINBlob = tokenObject.getAttribute(CKA_OS_SOPIN).getByteStringValue();
+ return true;
+}
+
+// Set the user PIN
+bool DBToken::setUserPIN(ByteString userPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for updating the USERPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute userPIN(userPINBlob);
+ if (!tokenObject.setAttribute(CKA_OS_USERPIN, userPIN))
+ {
+ ERROR_MSG("Error while setting USERPIN in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ // Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
+ CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
+ | (CKF_USER_PIN_INITIALIZED & ~(CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED));
+
+ OSAttribute changedTokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing USERPIN and TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Get the user PIN
+bool DBToken::getUserPIN(ByteString& userPINBlob)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the USERPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_USERPIN))
+ {
+ ERROR_MSG("Error while getting USERPIN from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ userPINBlob = tokenObject.getAttribute(CKA_OS_USERPIN).getByteStringValue();
+ return true;
+}
+
+// Retrieve the token label
+bool DBToken::getTokenLabel(ByteString& label)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the TOKENLABEL from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENLABEL))
+ {
+ ERROR_MSG("Error while getting TOKENLABEL from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ label = tokenObject.getAttribute(CKA_OS_TOKENLABEL).getByteStringValue();
+ return true;
+}
+
+// Retrieve the token serial
+bool DBToken::getTokenSerial(ByteString& serial)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for getting the TOKENSERIAL from token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENSERIAL))
+ {
+ ERROR_MSG("Error while getting TOKENSERIAL from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ serial = tokenObject.getAttribute(CKA_OS_TOKENSERIAL).getByteStringValue();
+ return true;
+}
+
+// Get the token flags
+bool DBToken::getTokenFlags(CK_ULONG& flags)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadOnly))
+ {
+ ERROR_MSG("Unable to start a transaction for updating the SOPIN and TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ tokenObject.commitTransaction();
+ flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue();
+ return true;
+}
+
+// Set the token flags
+bool DBToken::setTokenFlags(const CK_ULONG flags)
+{
+ if (_connection == NULL) return false;
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for setting the TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute tokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing TOKENFLAGS changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Retrieve objects
+std::set<OSObject *> DBToken::getObjects()
+{
+ std::set<OSObject*> objects;
+ getObjects(objects);
+ return objects;
+}
+
+void DBToken::getObjects(std::set<OSObject*> &objects)
+{
+ if (_connection == NULL) return;
+
+ if (!_connection->beginTransactionRO()) return;
+
+ DB::Statement statement = _connection->prepare("select id from object limit -1 offset 1");
+
+ DB::Result result = _connection->perform(statement);
+
+ if (result.isValid())
+ {
+ do {
+ long long objectId = result.getLongLong(1);
+ {
+ MutexLocker lock(_tokenMutex);
+ std::map<long long, OSObject*>::iterator it = _allObjects.find(objectId);
+ if (it == _allObjects.end())
+ {
+ DBObject *object = new DBObject(_connection, this, objectId);
+ _allObjects[objectId] = object;
+ objects.insert(object);
+ }
+ else
+ {
+ objects.insert(it->second);
+ }
+ }
+ } while (result.nextRow());
+ }
+
+ _connection->endTransactionRO();
+}
+
+// Create a new object
+OSObject *DBToken::createObject()
+{
+ if (_connection == NULL) return NULL;
+
+ DBObject *newObject = new DBObject(_connection, this);
+ if (newObject == NULL)
+ {
+ ERROR_MSG("Failed to create an object: out of memory");
+ return NULL;
+ }
+
+ if (!newObject->startTransaction(DBObject::ReadWrite))
+ {
+ delete newObject;
+ ERROR_MSG("Unable to start a transaction in token database at \"%s\"", _connection->dbpath().c_str());
+ return NULL;
+ }
+
+ if (!newObject->insert())
+ {
+ newObject->abortTransaction();
+ delete newObject;
+ ERROR_MSG("Unable to insert an object into token database at \"%s\"", _connection->dbpath().c_str());
+ return NULL;
+ }
+
+ if (!newObject->isValid())
+ {
+ newObject->abortTransaction();
+ delete newObject;
+ ERROR_MSG("Object that was inserted in not valid");
+ return NULL;
+ }
+
+ if (!newObject->commitTransaction())
+ {
+ newObject->abortTransaction();
+ delete newObject;
+ ERROR_MSG("Unable to commit a created object to token database at \"%s\"", _connection->dbpath().c_str());
+ return NULL;
+ }
+
+ // Now add the new object to the list of existing objects.
+ {
+ MutexLocker lock(_tokenMutex);
+ _allObjects[newObject->objectId()] = newObject;
+ }
+
+ return newObject;
+}
+
+bool DBToken::deleteObject(OSObject *object)
+{
+ if (_connection == NULL) return false;
+
+ if (object == NULL)
+ {
+ ERROR_MSG("Object passed in as a parameter is NULL");
+ return false;
+ }
+
+ if (!object->startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for deleting an object in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ if (!static_cast<DBObject *>(object)->remove())
+ {
+ ERROR_MSG("Error while deleting an existing object from the token database at \"%s\"", _connection->dbpath().c_str());
+ object->abortTransaction();
+ return false;
+ }
+
+ if (!object->commitTransaction())
+ {
+ ERROR_MSG("Error while committing the deletion of an existing object in token database at \"%s\"", _connection->dbpath().c_str());
+ object->abortTransaction();
+ return false;
+ }
+
+ return true;
+}
+
+// Checks if the token is consistent
+bool DBToken::isValid()
+{
+ return _connection != NULL && _connection->tableExists("object");
+}
+
+// Invalidate the token (for instance if it is deleted)
+void DBToken::invalidate()
+{
+}
+
+// Delete the token.
+bool DBToken::clearToken()
+{
+ if (_connection == NULL) return false;
+
+ std::string tokenDir = _connection->dbdir();
+ std::string tokenPath = _connection->dbpath();
+
+ if (!DBObject(_connection).dropTables())
+ {
+ ERROR_MSG("Failed to drop all tables in the token database at \"%s\"", tokenPath.c_str());
+ return false;
+ }
+
+ _connection->close();
+ delete _connection;
+ _connection = NULL;
+
+ // Remove all files from the token directory, even ones not placed there by us.
+ Directory dir(tokenDir);
+ std::vector<std::string> tokenFiles = dir.getFiles();
+
+ for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++)
+ {
+ if (!dir.remove(*i))
+ {
+ ERROR_MSG("Failed to remove \"%s\" from token directory \"%s\"", i->c_str(), tokenDir.c_str());
+
+ return false;
+ }
+ }
+
+ // Now remove the token directory
+ if (!dir.rmdir(""))
+ {
+ ERROR_MSG("Failed to remove the token directory \"%s\"", tokenDir.c_str());
+
+ return false;
+ }
+
+ DEBUG_MSG("Token instance %s was succesfully cleared", tokenDir.c_str());
+
+ return true;
+}
+
+// Reset the token
+bool DBToken::resetToken(const ByteString& label)
+{
+ if (_connection == NULL) return false;
+
+ std::string tokenDir = _connection->dbdir();
+
+ // Clean up
+ std::set<OSObject*> cleanUp = getObjects();
+
+ for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ if (!deleteObject(*i))
+ {
+ ERROR_MSG("Unable to delete all objects in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+ }
+
+ // Create a DBObject for the established connection to the token object in the database
+ DBObject tokenObject(_connection);
+
+ if (!tokenObject.startTransaction(DBObject::ReadWrite))
+ {
+ ERROR_MSG("Unable to start a transaction for setting the TOKENLABEL in token database at \"%s\"", _connection->dbpath().c_str());
+ return false;
+ }
+
+ // First find the token object in the database.
+ if (!tokenObject.find(DBTOKEN_OBJECT_TOKENINFO))
+ {
+ ERROR_MSG("Token object not found in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (tokenObject.attributeExists(CKA_OS_USERPIN))
+ {
+ if (!tokenObject.deleteAttribute(CKA_OS_USERPIN))
+ {
+ ERROR_MSG("Error while deleting USERPIN in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+ }
+
+ if (!tokenObject.attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ ERROR_MSG("Error while getting TOKENFLAGS from token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ // Retrieve flags from the database and reset flags related to tries and expiration of the SOPIN.
+ CK_ULONG flags = tokenObject.getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue()
+ & ~(CKF_USER_PIN_INITIALIZED | CKF_USER_PIN_COUNT_LOW | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_LOCKED | CKF_USER_PIN_TO_BE_CHANGED);
+
+ OSAttribute changedTokenFlags(flags);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENFLAGS, changedTokenFlags))
+ {
+ ERROR_MSG("Error while setting TOKENFLAGS in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ OSAttribute tokenLabel(label);
+ if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel))
+ {
+ ERROR_MSG("Error while setting TOKENLABEL in token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ if (!tokenObject.commitTransaction())
+ {
+ ERROR_MSG("Error while committing TOKENLABEL changes to token database at \"%s\"", _connection->dbpath().c_str());
+ tokenObject.abortTransaction();
+ return false;
+ }
+
+ DEBUG_MSG("Token instance %s was succesfully reset", tokenDir.c_str());
+
+ return true;
+}
diff --git a/SoftHSMv2/src/lib/object_store/DBToken.h b/SoftHSMv2/src/lib/object_store/DBToken.h
new file mode 100644
index 0000000..ef4c28e
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/DBToken.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBToken.h
+
+ The token class; a token is stored in a directory containing a single
+ database file.
+ Each object is stored in multiple tables with every attribute base type
+ stored in a different table.
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DBTOKEN_H
+#define _SOFTHSM_V2_DBTOKEN_H
+
+#include "config.h"
+#include "ByteString.h"
+#include "MutexFactory.h"
+#include "OSAttribute.h"
+#include "cryptoki.h"
+#include "OSObject.h"
+#include "ObjectStoreToken.h"
+
+#include <string>
+#include <set>
+
+namespace DB { class Connection; }
+
+class DBToken : public ObjectStoreToken
+{
+public:
+ // Constructor to create a new token
+ DBToken(const std::string &baseDir, const std::string &tokenName, const ByteString& label, const ByteString& serial);
+
+ // Constructor to access an existing token
+ DBToken(const std::string &baseDir, const std::string &tokenName);
+
+ // Create a new token
+ static DBToken* createToken(const std::string basePath, const std::string tokenDir, const ByteString& label, const ByteString& serial);
+
+ // Access an existing token
+ static DBToken* accessToken(const std::string &basePath, const std::string &tokenDir);
+
+ // Destructor
+ virtual ~DBToken();
+
+ // Set the SO PIN
+ virtual bool setSOPIN(const ByteString& soPINBlob);
+
+ // Get the SO PIN
+ virtual bool getSOPIN(ByteString& soPINBlob);
+
+ // Set the user PIN
+ virtual bool setUserPIN(ByteString userPINBlob);
+
+ // Get the user PIN
+ virtual bool getUserPIN(ByteString& userPINBlob);
+
+ // Get the token flags
+ virtual bool getTokenFlags(CK_ULONG& flags);
+
+ // Set the token flags
+ virtual bool setTokenFlags(const CK_ULONG flags);
+
+ // Retrieve the token label
+ virtual bool getTokenLabel(ByteString& label);
+
+ // Retrieve the token serial
+ virtual bool getTokenSerial(ByteString& serial);
+
+ // Retrieve objects
+ virtual std::set<OSObject*> getObjects();
+
+ // Insert objects into the given set
+ virtual void getObjects(std::set<OSObject*> &objects);
+
+ // Create a new object
+ virtual OSObject* createObject();
+
+ // Delete an object
+ virtual bool deleteObject(OSObject* object);
+
+ // Checks if the token is consistent
+ virtual bool isValid();
+
+ // Invalidate the token (for instance if it is deleted)
+ virtual void invalidate();
+
+ // Delete the token
+ virtual bool clearToken();
+
+ // Reset the token
+ virtual bool resetToken(const ByteString& label);
+
+private:
+ DB::Connection *_connection;
+
+ // All the objects ever associated with this token
+ //
+ // This map is kept to be able to clean up when the token
+ // instance is discarded; in case the contents of a token
+ // change, some objects may disappear but we cannot simply
+ // delete them since they may still be referenced from an
+ // object outside of this class.
+ std::map<long long, OSObject*> _allObjects;
+
+ // For thread safeness
+ Mutex* _tokenMutex;
+};
+
+#endif // !_SOFTHSM_V2_DBTOKEN_H
+
diff --git a/SoftHSMv2/src/lib/object_store/Directory.cpp b/SoftHSMv2/src/lib/object_store/Directory.cpp
new file mode 100644
index 0000000..e964d32
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/Directory.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ Directory.cpp
+
+ Helper functions for accessing directories.
+ *****************************************************************************/
+
+#include "config.h"
+#include "Directory.h"
+#include "OSPathSep.h"
+#include "log.h"
+#include <string>
+#include <vector>
+#ifndef _WIN32
+#include <dirent.h>
+#include <unistd.h>
+#else
+#include <direct.h>
+#include <io.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// Constructor
+Directory::Directory(std::string inPath)
+{
+ path = inPath;
+ dirMutex = MutexFactory::i()->getMutex();
+
+ valid = (dirMutex != NULL) && refresh();
+}
+
+// Destructor
+Directory::~Directory()
+{
+ MutexFactory::i()->recycleMutex(dirMutex);
+}
+
+// Check if the directory is valid
+bool Directory::isValid()
+{
+ return valid;
+}
+
+// Return a list of all files in a directory
+std::vector<std::string> Directory::getFiles()
+{
+ // Make sure that no other thread is in the process of changing
+ // the file list when we return it
+ MutexLocker lock(dirMutex);
+
+ return files;
+}
+
+// Return a list of all subdirectories in a directory
+std::vector<std::string> Directory::getSubDirs()
+{
+ // Make sure that no other thread is in the process of changing
+ // the subdirectory list when we return it
+ MutexLocker lock(dirMutex);
+
+ return subDirs;
+}
+
+// Refresh the directory listing
+bool Directory::refresh()
+{
+ // Prevent concurrent call until valid is reset
+ MutexLocker lock(dirMutex);
+
+ // Reset the state
+ valid = false;
+
+ subDirs.clear();
+ files.clear();
+
+#ifndef _WIN32
+ // Enumerate the directory
+ DIR* dir = opendir(path.c_str());
+
+ if (dir == NULL)
+ {
+ DEBUG_MSG("Failed to open directory %s", path.c_str());
+
+ return false;
+ }
+
+ // Enumerate the directory
+ struct dirent* entry = NULL;
+
+ while ((entry = readdir(dir)) != NULL)
+ {
+ bool pushed = false;
+
+ // Check if this is the . or .. entry
+ if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
+ {
+ continue;
+ }
+
+ // Convert the name of the entry to a C++ string
+ std::string name(entry->d_name);
+
+#if defined(_DIRENT_HAVE_D_TYPE) && defined(_BSD_SOURCE)
+ // Determine the type of the entry
+ switch(entry->d_type)
+ {
+ case DT_DIR:
+ // This is a directory
+ subDirs.push_back(name);
+ pushed = true;
+ break;
+ case DT_REG:
+ // This is a regular file
+ files.push_back(name);
+ pushed = true;
+ break;
+ default:
+ break;
+ }
+#endif
+ if (!pushed) {
+ // The entry type has to be determined using lstat
+ struct stat entryStatus;
+
+ std::string fullPath = path + OS_PATHSEP + name;
+
+ if (!lstat(fullPath.c_str(), &entryStatus))
+ {
+ if (S_ISDIR(entryStatus.st_mode))
+ {
+ subDirs.push_back(name);
+ }
+ else if (S_ISREG(entryStatus.st_mode))
+ {
+ files.push_back(name);
+ }
+ else
+ {
+ DEBUG_MSG("File not used %s", name.c_str());
+ }
+ }
+ }
+ }
+
+ // Close the directory
+ closedir(dir);
+
+#else
+ // Enumerate the directory
+ std::string pattern;
+ intptr_t h;
+ struct _finddata_t fi;
+
+ if ((path.back() == '/') || (path.back() == '\\'))
+ pattern = path + "*";
+ else
+ pattern = path + "/*";
+ memset(&fi, 0, sizeof(fi));
+ h = _findfirst(pattern.c_str(), &fi);
+ if (h == -1)
+ {
+ // empty directory
+ if (errno == ENOENT)
+ goto finished;
+
+ DEBUG_MSG("Failed to open directory %s", path.c_str());
+
+ return false;
+ }
+
+ // scan files & subdirs
+ do {
+ // Check if this is the . or .. entry
+ if (!strcmp(fi.name, ".") || !strcmp(fi.name, ".."))
+ continue;
+
+ if ((fi.attrib & _A_SUBDIR) == 0)
+ files.push_back(fi.name);
+ else
+ subDirs.push_back(fi.name);
+
+ memset(&fi, 0, sizeof(fi));
+ } while (_findnext(h, &fi) == 0);
+
+ (void) _findclose(h);
+
+ finished:
+#endif
+
+ valid = true;
+
+ return true;
+}
+
+// Create a new subdirectory
+bool Directory::mkdir(std::string name)
+{
+ std::string fullPath = path + OS_PATHSEP + name;
+
+#ifndef _WIN32
+ int rv = ::mkdir(fullPath.c_str(), S_IFDIR | S_IRWXU);
+#else
+ int rv = _mkdir(fullPath.c_str());
+#endif
+
+ if (rv != 0)
+ {
+ ERROR_MSG("Failed to create the directory (%s): %s", strerror(errno), fullPath.c_str());
+
+ return false;
+ }
+
+ return refresh();
+}
+
+// Delete a subdirectory in the directory
+bool Directory::rmdir(std::string name, bool doRefresh /* = false */)
+{
+ std::string fullPath;
+
+ if (name.empty())
+ fullPath = path;
+ else
+ fullPath = path + OS_PATHSEP + name;
+
+#ifndef _WIN32
+ if (::rmdir(fullPath.c_str()) != 0)
+ return false;
+#else
+ if (_rmdir(fullPath.c_str()) != 0)
+ return false;
+#endif
+ if (doRefresh)
+ return refresh();
+ return true;
+}
+
+// Delete a file in the directory
+bool Directory::remove(std::string name)
+{
+ std::string fullPath = path + OS_PATHSEP + name;
+
+#ifndef _WIN32
+ return (!::remove(fullPath.c_str()) && refresh());
+#else
+ return (!_unlink(fullPath.c_str()) && refresh());
+#endif
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/Directory.h b/SoftHSMv2/src/lib/object_store/Directory.h
new file mode 100644
index 0000000..d7681a1
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/Directory.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ Directory.h
+
+ Helper functions for accessing directories.
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DIRECTORY_H
+#define _SOFTHSM_V2_DIRECTORY_H
+
+#include "config.h"
+#include "MutexFactory.h"
+#include <string>
+#include <vector>
+
+class Directory
+{
+public:
+ // Constructor
+ Directory(std::string inPath);
+
+ // Destructor
+ virtual ~Directory();
+
+ // Check if the directory is valid
+ bool isValid();
+
+ // Return a list of all files in a directory
+ std::vector<std::string> getFiles();
+
+ // Return a list of all subdirectories in a directory
+ std::vector<std::string> getSubDirs();
+
+ // Refresh the directory listing
+ bool refresh();
+
+ // Create a new subdirectory
+ bool mkdir(std::string name);
+
+ // Delete a subdirectory in the directory
+ bool rmdir(std::string name, bool doRefresh = false);
+
+ // Delete a file in the directory
+ bool remove(std::string name);
+
+private:
+ // The directory path
+ std::string path;
+
+ // The status
+ bool valid;
+
+ // All files in the directory
+ std::vector<std::string> files;
+
+ // All subdirectories in the directory
+ std::vector<std::string> subDirs;
+
+ // For thread safeness
+ Mutex* dirMutex;
+};
+
+#endif // !_SOFTHSM_V2_DIRECTORY_H
+
diff --git a/SoftHSMv2/src/lib/object_store/File.cpp b/SoftHSMv2/src/lib/object_store/File.cpp
new file mode 100644
index 0000000..2af8ea9
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/File.cpp
@@ -0,0 +1,765 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this vector of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this vector of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ File.h
+
+ This class wraps standard C file I/O in a convenient way for the object store
+ *****************************************************************************/
+
+#include "config.h"
+#include "File.h"
+#include "log.h"
+#include <string>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+#include <sys/file.h>
+#include <unistd.h>
+#else
+#include <io.h>
+#define F_SETLK 12
+#define F_SETLKW 13
+#define F_RDLCK 1
+#define F_UNLCK 2
+#define F_WRLCK 3
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+enum AttributeKind {
+ akUnknown,
+ akBoolean,
+ akInteger,
+ akBinary,
+ akAttrMap,
+ akMechSet
+};
+
+// Constructor
+//
+// N.B.: the create flag only has a function when a file is opened read/write
+// N.B.: the truncate flag only has a function when the create one is true
+File::File(std::string inPath, bool forRead /* = true */, bool forWrite /* = false */, bool create /* = false */, bool truncate /* = true */)
+{
+ stream = NULL;
+
+ isReadable = forRead;
+ isWritable = forWrite;
+ locked = false;
+
+ path = inPath;
+ valid = false;
+
+ if (forRead || forWrite)
+ {
+ std::string fileMode = "";
+ int flags, fd;
+
+#ifndef _WIN32
+ flags = 0;
+ if (forRead && !forWrite) flags |= O_RDONLY;
+ if (!forRead && forWrite) flags |= O_WRONLY | O_CREAT | O_TRUNC;
+ if (forRead && forWrite) flags |= O_RDWR;
+ if (forRead && forWrite && create) flags |= O_CREAT;
+ if (forRead && forWrite && create && truncate) flags |= O_TRUNC;
+ // Open the file
+ fd = open(path.c_str(), flags, 0600);
+ if (fd == -1)
+ {
+ ERROR_MSG("Could not open the file (%s): %s", strerror(errno), path.c_str());
+ valid = false;
+ return;
+ }
+
+ if (forRead && !forWrite) fileMode = "r";
+ if (!forRead && forWrite) fileMode = "w";
+ if (forRead && forWrite && !create) fileMode = "r+";
+ if (forRead && forWrite && create) fileMode = "w+";
+ // Open the stream
+ valid = ((stream = fdopen(fd, fileMode.c_str())) != NULL);
+#else
+ flags = _O_BINARY;
+ if (forRead && !forWrite) flags |= _O_RDONLY;
+ if (!forRead && forWrite) flags |= _O_WRONLY | _O_CREAT | _O_TRUNC;
+ if (forRead && forWrite) flags |= _O_RDWR;
+ if (forRead && forWrite && create) flags |= _O_CREAT;
+ if (forRead && forWrite && create && truncate) flags |= _O_TRUNC;
+ // Open the file
+ fd = _open(path.c_str(), flags, _S_IREAD | _S_IWRITE);
+ if (fd == -1)
+ {
+ ERROR_MSG("Could not open the file (%s): %s", strerror(errno), path.c_str());
+ valid = false;
+ return;
+ }
+
+ if (forRead && !forWrite) fileMode = "rb";
+ if (!forRead && forWrite) fileMode = "wb";
+ if (forRead && forWrite && !create) fileMode = "rb+";
+ if (forRead && forWrite && create) fileMode = "wb+";
+ // Open the stream
+ valid = ((stream = _fdopen(fd, fileMode.c_str())) != NULL);
+#endif
+ }
+}
+
+// Destructor
+File::~File()
+{
+ if (locked)
+ {
+ unlock();
+ }
+
+ if (stream != NULL)
+ {
+ fclose(stream);
+ }
+}
+
+// Check if the file is valid
+bool File::isValid()
+{
+ return valid;
+}
+
+// Check if the file is readable
+bool File::isRead()
+{
+ return isReadable;
+}
+
+// Check if the file is writable
+bool File::isWrite()
+{
+ return isWritable;
+}
+
+// Check if the file is empty
+bool File::isEmpty()
+{
+#ifndef _WIN32
+ struct stat s;
+
+ if (fstat(fileno(stream), &s) != 0)
+ {
+ valid = false;
+
+ return false;
+ }
+
+ return (s.st_size == 0);
+#else
+ struct _stat s;
+
+ if (_fstat(_fileno(stream), &s) != 0)
+ {
+ valid = false;
+
+ return false;
+ }
+
+ return (s.st_size == 0);
+#endif
+}
+
+// Check if the end-of-file was reached
+bool File::isEOF()
+{
+ return valid && feof(stream);
+}
+
+// Read an unsigned long value; warning: not thread safe without locking!
+bool File::readULong(unsigned long& value)
+{
+ if (!valid) return false;
+
+ ByteString ulongVal;
+
+ ulongVal.resize(8);
+
+ if (fread(&ulongVal[0], 1, 8, stream) != 8)
+ {
+ return false;
+ }
+
+ value = ulongVal.long_val();
+
+ return true;
+}
+
+// Read a ByteString value; warning: not thread safe without locking!
+bool File::readByteString(ByteString& value)
+{
+ if (!valid) return false;
+
+ // Retrieve the length to read from the file
+ unsigned long len;
+
+ if (!readULong(len))
+ {
+ return false;
+ }
+
+ // Read the byte string from the file
+ value.resize(len);
+
+ if (len == 0)
+ {
+ return true;
+ }
+
+ if (fread(&value[0], 1, len, stream) != len)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Read a boolean value; warning: not thread safe without locking!
+bool File::readBool(bool& value)
+{
+ if (!valid) return false;
+
+ // Read the boolean from the file
+ unsigned char boolValue;
+
+ if (fread(&boolValue, 1, 1, stream) != 1)
+ {
+ return false;
+ }
+
+ value = boolValue ? true : false;
+
+ return true;
+}
+
+// Read a mechanism type set value; warning: not thread safe without locking!
+bool File::readMechanismTypeSet(std::set<CK_MECHANISM_TYPE>& value)
+{
+ if (!valid) return false;
+
+ unsigned long count;
+ if (!readULong(count)) return false;
+
+ for (unsigned long i = 0; i < count; i++)
+ {
+ unsigned long mechType;
+ if (!readULong(mechType))
+ {
+ return false;
+ }
+
+ value.insert((CK_MECHANISM_TYPE) mechType);
+ }
+
+ return true;
+}
+
+// Read an attribute map value; warning: not thread safe without locking!
+bool File::readAttributeMap(std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value)
+{
+ if (!valid) return false;
+
+ // Retrieve the length to read from the file
+ unsigned long len;
+
+ if (!readULong(len))
+ {
+ return false;
+ }
+
+ while (len != 0)
+ {
+ unsigned long attrType;
+ if (!readULong(attrType))
+ {
+ return false;
+ }
+ if (8 > len)
+ {
+ return false;
+ }
+ len -= 8;
+
+ unsigned long attrKind;
+ if (!readULong(attrKind))
+ {
+ return false;
+ }
+ if (8 > len)
+ {
+ return false;
+ }
+ len -= 8;
+
+ switch (attrKind)
+ {
+ case akBoolean:
+ {
+ bool val;
+ if (!readBool(val))
+ {
+ return false;
+ }
+ if (1 > len)
+ {
+ return false;
+ }
+ len -= 1;
+
+ value.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, val));
+ }
+ break;
+
+ case akInteger:
+ {
+ unsigned long val;
+ if (!readULong(val))
+ {
+ return false;
+ }
+ if (8 > len)
+ {
+ return false;
+ }
+ len -= 8;
+
+ value.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, val));
+ }
+ break;
+
+ case akBinary:
+ {
+ ByteString val;
+ if (!readByteString(val))
+ {
+ return false;
+ }
+ if (8 + val.size() > len)
+ {
+ return false;
+ }
+ len -= 8 + val.size();
+
+ value.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, val));
+ }
+ break;
+
+ case akMechSet:
+ {
+ std::set<CK_MECHANISM_TYPE> val;
+ if (!readMechanismTypeSet(val))
+ {
+ return false;
+ }
+ if (8 + val.size() * 8 > len)
+ {
+ return false;
+ }
+ len -= 8 + val.size() * 8;
+
+ value.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (attrType, val));
+ }
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Read a string value; warning: not thread safe without locking!
+bool File::readString(std::string& value)
+{
+ if (!valid) return false;
+
+ // Retrieve the length to read from the file
+ unsigned long len;
+
+ if (!readULong(len))
+ {
+ return false;
+ }
+
+ // Read the string from the file
+ value.resize(len);
+
+ if (fread(&value[0], 1, len, stream) != len)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Write an unsigned long value; warning: not thread safe without locking!
+bool File::writeULong(const unsigned long value)
+{
+ if (!valid) return false;
+
+ ByteString toWrite(value);
+
+ // Write the value to the file
+ if (fwrite(toWrite.const_byte_str(), 1, toWrite.size(), stream) != toWrite.size())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Write a ByteString value; warning: not thread safe without locking!
+bool File::writeByteString(const ByteString& value)
+{
+ if (!valid) return false;
+
+ ByteString toWrite = value.serialise();
+
+ // Write the value to the file
+ if (fwrite(toWrite.const_byte_str(), 1, toWrite.size(), stream) != toWrite.size())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Write a string value; warning: not thread safe without locking!
+bool File::writeString(const std::string& value)
+{
+ if (!valid) return false;
+
+ ByteString toWrite((const unsigned long) value.size());
+
+ // Write the value to the file
+ if ((fwrite(toWrite.const_byte_str(), 1, toWrite.size(), stream) != toWrite.size()) ||
+ (fwrite(&value[0], 1, value.size(), stream) != value.size()))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Write a mechanism type set value; warning: not thread safe without locking!
+bool File::writeMechanismTypeSet(const std::set<CK_MECHANISM_TYPE>& value)
+{
+ if (!valid) return false;
+
+ // write length
+ if (!writeULong(value.size()))
+ {
+ return false;
+ }
+
+ // write each value
+ for (std::set<CK_MECHANISM_TYPE>::const_iterator i = value.begin(); i != value.end(); ++i)
+ {
+ if (!writeULong(*i)) return false;
+ }
+
+ return true;
+}
+
+// Write an attribute map value; warning: not thread safe without locking!
+bool File::writeAttributeMap(const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value)
+{
+ if (!valid) return false;
+
+ // compute length
+ unsigned long len = 0;
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute>::const_iterator i = value.begin(); i != value.end(); ++i)
+ {
+ OSAttribute attr = i->second;
+ // count attribute type and kind
+ len += 8 + 8;
+
+ if (attr.isBooleanAttribute())
+ {
+ len += 1;
+ }
+ else if (attr.isUnsignedLongAttribute())
+ {
+ len += 8;
+ }
+ else if (attr.isByteStringAttribute())
+ {
+ ByteString val = attr.getByteStringValue();
+ len += 8 + val.size();
+ }
+ else if (attr.isMechanismTypeSetAttribute())
+ {
+ std::set<CK_MECHANISM_TYPE> val = attr.getMechanismTypeSetValue();
+ len += 8 + val.size() * 8;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // write length
+ if (!writeULong(len))
+ {
+ return false;
+ }
+
+ // write each attribute
+ for (std::map<CK_ATTRIBUTE_TYPE,OSAttribute>::const_iterator i = value.begin(); i != value.end(); ++i)
+ {
+ OSAttribute attr = i->second;
+ unsigned long attrType = (unsigned long) i->first;
+ if (!writeULong(attrType))
+ {
+ return false;
+ }
+
+ if (attr.isBooleanAttribute())
+ {
+ unsigned long attrKind = akBoolean;
+ if (!writeULong(attrKind))
+ {
+ return false;
+ }
+
+ bool val = attr.getBooleanValue();
+ if (!writeBool(val))
+ {
+ return false;
+ }
+ }
+ else if (attr.isUnsignedLongAttribute())
+ {
+ unsigned long attrKind = akInteger;
+ if (!writeULong(attrKind))
+ {
+ return false;
+ }
+
+ unsigned long val = attr.getUnsignedLongValue();
+ if (!writeULong(val))
+ {
+ return false;
+ }
+ }
+ else if (attr.isByteStringAttribute())
+ {
+ unsigned long attrKind = akBinary;
+ if (!writeULong(attrKind))
+ {
+ return false;
+ }
+
+ ByteString val = attr.getByteStringValue();
+ if (!writeByteString(val))
+ {
+ return false;
+ }
+ }
+ else if (attr.isMechanismTypeSetAttribute())
+ {
+ unsigned long attrKind = akMechSet;
+ if (!writeULong(attrKind))
+ {
+ return false;
+ }
+
+ std::set<CK_MECHANISM_TYPE> val = attr.getMechanismTypeSetValue();
+ if (!writeMechanismTypeSet(val))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// Write a boolean value; warning: not thread safe without locking!
+bool File::writeBool(const bool value)
+{
+ if (!valid) return false;
+
+ unsigned char toWrite = value ? 0xFF : 0x00;
+
+ // Write the value to the file
+ if (fwrite(&toWrite, 1, 1, stream) != 1)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+// Rewind the file
+bool File::rewind()
+{
+ if (!valid) return false;
+
+ ::rewind(stream);
+
+ return true;
+}
+
+// Truncate the file
+bool File::truncate()
+{
+ if (!valid) return false;
+
+#ifndef _WIN32
+ return (::ftruncate(fileno(stream), 0) == 0);
+#else
+ return (_chsize(_fileno(stream), 0) == 0);
+#endif
+}
+
+// Seek to the specified position relative to the start of the file; if no
+// argument is specified this operation seeks to the end of the file
+bool File::seek(long offset /* = -1 */)
+{
+ if (offset == -1)
+ {
+ return valid && (valid = !fseek(stream, 0, SEEK_END));
+ }
+ else
+ {
+ return valid && (valid = !fseek(stream, offset, SEEK_SET));
+ }
+}
+
+// Lock the file
+bool File::lock(bool block /* = true */)
+{
+#ifndef _WIN32
+ struct flock fl;
+ fl.l_type = isWrite() ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = 0;
+
+ if (locked || !valid) return false;
+
+ if (fcntl(fileno(stream), block ? F_SETLKW : F_SETLK, &fl) != 0)
+ {
+ ERROR_MSG("Could not lock the file: %s", strerror(errno));
+ return false;
+ }
+#else
+ HANDLE hFile;
+ DWORD flags = 0;
+ OVERLAPPED o;
+
+ if (isWrite()) flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ if (!block) flags |= LOCKFILE_FAIL_IMMEDIATELY;
+
+ if (locked || !valid) return false;
+
+ hFile = (HANDLE) _get_osfhandle(_fileno(stream));
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ ERROR_MSG("Invalid handle");
+ return false;
+ }
+
+ memset(&o, 0, sizeof(o));
+ if (!LockFileEx(hFile, flags, 0, 1, 0, &o))
+ {
+ DWORD rv = GetLastError();
+
+ ERROR_MSG("Could not lock the file: 0x%08x", rv);
+ return false;
+ }
+#endif
+
+ locked = true;
+
+ return true;
+}
+
+// Unlock the file
+bool File::unlock()
+{
+#ifndef _WIN32
+ struct flock fl;
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = 0;
+
+ if (!locked || !valid) return false;
+
+ if (fcntl(fileno(stream), F_SETLK, &fl) != 0)
+ {
+ valid = false;
+
+ ERROR_MSG("Could not unlock the file: %s", strerror(errno));
+ return false;
+ }
+#else
+ HANDLE hFile;
+ OVERLAPPED o;
+
+ if (!locked || !valid) return false;
+
+ hFile = (HANDLE) _get_osfhandle(_fileno(stream));
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ ERROR_MSG("Invalid handle");
+ return false;
+ }
+
+ memset(&o, 0, sizeof(o));
+ if (!UnlockFileEx(hFile, 0, 1, 0, &o))
+ {
+ DWORD rv = GetLastError();
+
+ valid = false;
+
+ ERROR_MSG("Could not unlock the file: 0x%08x", rv);
+ return false;
+ }
+#endif
+
+
+ locked = false;
+
+ return valid;
+}
+
+// Flush the buffered stream to background storage
+bool File::flush()
+{
+ return valid && !fflush(stream);
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/File.h b/SoftHSMv2/src/lib/object_store/File.h
new file mode 100644
index 0000000..da27af6
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/File.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ File.h
+
+ This class wraps standard C file I/O in a convenient way for the object store
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_FILE_H
+#define _SOFTHSM_V2_FILE_H
+
+#include "config.h"
+#include "OSAttribute.h"
+#include <stdio.h>
+#include <string>
+
+class File
+{
+public:
+ // Constructor
+ File(std::string inPath, bool forRead = true, bool forWrite = false, bool create = false, bool truncate = true);
+
+ // Destructor
+ virtual ~File();
+
+ // Check if the file is valid
+ bool isValid();
+
+ // Check if the file is readable
+ bool isRead();
+
+ // Check if the file is writable
+ bool isWrite();
+
+ // Check if the file is empty
+ bool isEmpty();
+
+ // Check if the end-of-file was reached
+ bool isEOF();
+
+ // Read an unsigned long value; warning: not thread safe without locking!
+ bool readULong(unsigned long& value);
+
+ // Read a ByteString value; warning: not thread safe without locking!
+ bool readByteString(ByteString& value);
+
+ // Read a string value; warning: not thread safe without locking!
+ bool readString(std::string& value);
+
+ // Read a boolean value; warning: not thread safe without locking!
+ bool readBool(bool& value);
+
+ // Read a mechanism type set value; warning: not thread safe without locking!
+ bool readMechanismTypeSet(std::set<CK_MECHANISM_TYPE>& value);
+
+ // Read an array value; warning: not thread safe without locking!
+ bool readAttributeMap(std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value);
+
+ // Write an unsigned long value; warning: not thread safe without locking!
+ bool writeULong(const unsigned long value);
+
+ // Write a ByteString value; warning: not thread safe without locking!
+ bool writeByteString(const ByteString& value);
+
+ // Write a string value; warning: not thread safe without locking!
+ bool writeString(const std::string& value);
+
+ // Write a boolean value; warning: not thread safe without locking!
+ bool writeBool(const bool value);
+
+ // Write a mechanism type set value; warning: not thread safe without locking!
+ bool writeMechanismTypeSet(const std::set<CK_MECHANISM_TYPE>& value);
+
+ // Write an attribute map value; warning: not thread safe without locking!
+ bool writeAttributeMap(const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value);
+
+ // Rewind the file
+ bool rewind();
+
+ // Truncate the file
+ bool truncate();
+
+ // Seek to the specified position relative to the start of the file; if no
+ // argument is specified this operation seeks to the end of the file
+ bool seek(long offset = -1);
+
+ // Lock the file
+ bool lock(bool block = true);
+
+ // Unlock the file
+ bool unlock();
+
+ // Flush the buffered stream to background storage
+ bool flush();
+
+private:
+ // The file path
+ std::string path;
+
+ // The status
+ bool valid;
+ bool locked;
+
+ // Read, write or both?
+ bool isReadable, isWritable;
+
+ // The FILE stream
+ FILE* stream;
+};
+
+#endif // !_SOFTHSM_V2_FILE_H
+
diff --git a/SoftHSMv2/src/lib/object_store/FindOperation.cpp b/SoftHSMv2/src/lib/object_store/FindOperation.cpp
new file mode 100644
index 0000000..db6dda1
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/FindOperation.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2012 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ FindOperation.cpp
+
+ This class represents the find operation that can be used to collect
+ objects that match the attributes contained in a given template.
+ *****************************************************************************/
+
+#include "config.h"
+#include "FindOperation.h"
+
+FindOperation::FindOperation()
+{
+}
+
+FindOperation *FindOperation::create()
+{
+ return new FindOperation();
+}
+
+void FindOperation::recycle()
+{
+ delete this;
+}
+
+void FindOperation::setHandles(const std::set<CK_OBJECT_HANDLE> &handles)
+{
+ _handles = handles;
+}
+
+CK_ULONG FindOperation::retrieveHandles(CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulCount)
+{
+ CK_ULONG ulReturn = 0;
+ std::set<CK_OBJECT_HANDLE>::const_iterator it;
+ for (it=_handles.begin(); it != _handles.end(); ++it) {
+ if (ulReturn >= ulCount) break;
+
+ phObject[ulReturn++] = *it;
+ }
+ return ulReturn;
+}
+
+CK_ULONG FindOperation::eraseHandles(CK_ULONG ulIndex, CK_ULONG ulCount)
+{
+ std::set<CK_OBJECT_HANDLE>::const_iterator it;
+ for (it=_handles.begin(); it != _handles.end() && ulIndex != 0; --ulIndex) {
+ ++it;
+ }
+
+ CK_ULONG ulReturn = 0;
+ for ( ; it != _handles.end() && ulReturn < ulCount; ++ulReturn) {
+ _handles.erase(it++);
+ }
+ return ulReturn;
+}
diff --git a/SoftHSMv2/src/lib/object_store/FindOperation.h b/SoftHSMv2/src/lib/object_store/FindOperation.h
new file mode 100644
index 0000000..ea6410a
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/FindOperation.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ FindOperation.h
+
+ This class represents the find operation that can be used to collect
+ objects that match the attributes contained in a given template.
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_FINDOPERATION_H
+#define _SOFTHSM_V2_FINDOPERATION_H
+
+#include "config.h"
+
+#include <set>
+#include "OSObject.h"
+
+class FindOperation
+{
+public:
+ // Factory method creates a new find operation
+ static FindOperation* create();
+
+ // Hand this operation back to the factory for recycling.
+ void recycle();
+
+ // Add the objects from thet set that match the attributes in the given template to the find operation.
+ void setHandles(const std::set<CK_OBJECT_HANDLE> &handles);
+
+ // Retrieve handles
+ CK_ULONG retrieveHandles(CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulCount);
+
+ // Erase handles from the handles set.
+ CK_ULONG eraseHandles(CK_ULONG ulIndex, CK_ULONG ulCount);
+
+protected:
+ // Use a protected constructor to force creation via factory method.
+ FindOperation();
+
+ std::set<CK_OBJECT_HANDLE> _handles;
+};
+
+#endif // _SOFTHSM_V2_FINDOPERATION_H
diff --git a/SoftHSMv2/src/lib/object_store/Generation.cpp b/SoftHSMv2/src/lib/object_store/Generation.cpp
new file mode 100644
index 0000000..196dac2
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/Generation.cpp
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ Generation.cpp
+
+ Helper for generation number handling.
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "Generation.h"
+
+// Factory
+Generation* Generation::create(const std::string path, bool isToken /* = false */)
+{
+ Generation* gen = new Generation(path, isToken);
+ if ((gen != NULL) && isToken && (gen->genMutex == NULL))
+ {
+ delete gen;
+
+ return NULL;
+ }
+ return gen;
+}
+
+// Destructor
+Generation::~Generation()
+{
+ if (isToken)
+ {
+ MutexFactory::i()->recycleMutex(genMutex);
+ }
+}
+
+// Synchronize from locked disk file
+bool Generation::sync(File &objectFile)
+{
+ if (isToken)
+ {
+ ERROR_MSG("Generation sync() called for a token");
+
+ return false;
+ }
+
+ unsigned long onDisk;
+
+ if (!objectFile.readULong(onDisk))
+ {
+ if (objectFile.isEOF())
+ {
+ onDisk = 0;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ currentValue = onDisk;
+
+ return objectFile.seek(0L);
+}
+
+// Check if the target was updated
+bool Generation::wasUpdated()
+{
+ if (isToken)
+ {
+ MutexLocker lock(genMutex);
+
+ File genFile(path);
+
+ if (!genFile.isValid())
+ {
+ return true;
+ }
+
+ genFile.lock();
+
+ unsigned long onDisk;
+
+ if (!genFile.readULong(onDisk))
+ {
+ return true;
+ }
+
+ if (onDisk != currentValue)
+ {
+ currentValue = onDisk;
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ File objectFile(path);
+
+ if (!objectFile.isValid())
+ {
+ return true;
+ }
+
+ objectFile.lock();
+
+ unsigned long onDisk;
+
+ if (!objectFile.readULong(onDisk))
+ {
+ return true;
+ }
+
+ return (onDisk != currentValue);
+ }
+}
+
+// Update
+void Generation::update()
+{
+ pendingUpdate = true;
+}
+
+// Commit
+void Generation::commit()
+{
+ if (isToken)
+ {
+ MutexLocker lock(genMutex);
+
+ File genFile(path, true, true, true, false);
+
+ if (!genFile.isValid())
+ {
+ return;
+ }
+
+ genFile.lock();
+
+ if (genFile.isEmpty())
+ {
+ currentValue++;
+
+ if (currentValue == 0)
+ {
+ currentValue++;
+ }
+
+ pendingUpdate = false;
+
+ (void) genFile.writeULong(currentValue);
+
+ genFile.unlock();
+
+ return;
+ }
+
+ unsigned long onDisk;
+
+ bool bOK = true;
+
+ bOK = bOK && genFile.readULong(onDisk);
+ bOK = bOK && genFile.seek(0L);
+
+ if (pendingUpdate)
+ {
+ onDisk++;
+
+ if (onDisk == 0)
+ {
+ onDisk++;
+ }
+ }
+
+ bOK = bOK && genFile.writeULong(onDisk);
+
+ if (bOK)
+ {
+ currentValue = onDisk;
+
+ pendingUpdate = false;
+ }
+
+ genFile.unlock();
+ }
+}
+
+// Set the current value when read from disk
+void Generation::set(unsigned long onDisk)
+{
+ currentValue = onDisk;
+}
+
+// Return new value
+unsigned long Generation::get()
+{
+ pendingUpdate = false;
+
+ currentValue++;
+
+ if (currentValue == 0)
+ {
+ currentValue = 1;
+ }
+
+ return currentValue;
+}
+
+// Rollback (called when the new value failed to be written)
+void Generation::rollback()
+{
+ pendingUpdate = true;
+
+ if (currentValue != 1)
+ {
+ currentValue--;
+ }
+}
+
+// Constructor
+Generation::Generation(const std::string inPath, bool inIsToken)
+{
+ path = inPath;
+ isToken = inIsToken;
+ pendingUpdate = false;
+ currentValue = 0;
+ genMutex = NULL;
+
+ if (isToken)
+ {
+ genMutex = MutexFactory::i()->getMutex();
+
+ if (genMutex != NULL)
+ {
+ commit();
+ }
+ }
+}
diff --git a/SoftHSMv2/src/lib/object_store/Generation.h b/SoftHSMv2/src/lib/object_store/Generation.h
new file mode 100644
index 0000000..106f34b
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/Generation.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ Generation.h
+
+ Helper for generation number handling.
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_GENERATION_H
+#define _SOFTHSM_V2_GENERATION_H
+
+#include "config.h"
+#include <string>
+#include "File.h"
+#include "MutexFactory.h"
+
+class Generation
+{
+public:
+ // Factory
+ static Generation* create(const std::string inPath, bool inIsToken = false);
+
+ // Destructor
+ virtual ~Generation();
+
+ // Synchronize from locked disk file
+ bool sync(File &objectfile);
+
+ // Check if the target was updated
+ bool wasUpdated();
+
+ // Note pending update
+ void update();
+
+ // Commit (for the token case)
+ void commit();
+
+ // Set the current value when read from disk
+ void set(unsigned long onDisk);
+
+ // Return new value
+ unsigned long get();
+
+ // Rollback (called when the new value failed to be written)
+ void rollback();
+
+private:
+ // Constructor
+ Generation(const std::string path, bool isToken);
+
+ // The file path
+ std::string path;
+
+ // isToken
+ bool isToken;
+
+ // Pending update
+ bool pendingUpdate;
+
+ // Current value
+ unsigned long currentValue;
+
+ // For thread safeness
+ Mutex* genMutex;
+};
+
+#endif // !_SOFTHSM_V2_GENERATION_H
+
diff --git a/SoftHSMv2/src/lib/object_store/Makefile.am b/SoftHSMv2/src/lib/object_store/Makefile.am
new file mode 100644
index 0000000..d3e89d3
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/Makefile.am
@@ -0,0 +1,34 @@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+AM_CPPFLAGS = -I$(srcdir)/.. \
+ -I$(srcdir)/../common \
+ -I$(srcdir)/../crypto \
+ -I$(srcdir)/../data_mgr \
+ -I$(srcdir)/../pkcs11 \
+ @SQLITE3_INCLUDES@
+
+noinst_LTLIBRARIES = libsofthsm_objectstore.la
+libsofthsm_objectstore_la_SOURCES = ObjectStore.cpp \
+ UUID.cpp \
+ Directory.cpp \
+ File.cpp \
+ Generation.cpp \
+ OSAttribute.cpp \
+ OSToken.cpp \
+ ObjectFile.cpp \
+ SessionObject.cpp \
+ SessionObjectStore.cpp \
+ FindOperation.cpp \
+ ObjectStoreToken.cpp
+
+if BUILD_OBJECTSTORE_BACKEND_DB
+libsofthsm_objectstore_la_SOURCES += DB.cpp \
+ DBObject.cpp \
+ DBToken.cpp
+endif
+
+libsofthsm_objectstore_la_LDFLAGS = @SQLITE3_LIBS@
+
+SUBDIRS = test
+
+EXTRA_DIST = $(srcdir)/*.h
diff --git a/SoftHSMv2/src/lib/object_store/OSAttribute.cpp b/SoftHSMv2/src/lib/object_store/OSAttribute.cpp
new file mode 100644
index 0000000..9d7e5a3
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSAttribute.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSAttribute.cpp
+
+ This class represents the object store view on an object's attribute
+ *****************************************************************************/
+
+#include "config.h"
+#include "OSAttribute.h"
+
+// Copy constructor
+OSAttribute::OSAttribute(const OSAttribute& in)
+{
+ attributeType = in.attributeType;
+ boolValue = in.boolValue;
+ ulongValue = in.ulongValue;
+ byteStrValue = in.byteStrValue;
+ mechSetValue = in.mechSetValue;
+ attrMapValue = in.attrMapValue;
+}
+
+// Constructor for a boolean type attribute
+OSAttribute::OSAttribute(const bool value)
+{
+ boolValue = value;
+ attributeType = BOOL;
+
+ ulongValue = 0;
+}
+
+// Constructor for an unsigned long type attribute
+OSAttribute::OSAttribute(const unsigned long value)
+{
+ ulongValue = value;
+ attributeType = ULONG;
+
+ boolValue = false;
+}
+
+// Constructor for a byte string type attribute
+OSAttribute::OSAttribute(const ByteString& value)
+{
+ byteStrValue = value;
+ attributeType = BYTESTR;
+
+ boolValue = false;
+ ulongValue = 0;
+}
+
+// Constructor for a mechanism type set attribute
+OSAttribute::OSAttribute(const std::set<CK_MECHANISM_TYPE>& value)
+{
+ mechSetValue = value;
+ attributeType = MECHSET;
+
+ boolValue = false;
+ ulongValue = 0;
+}
+
+// Constructor for an attribute map type attribute
+OSAttribute::OSAttribute(const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value)
+{
+ attrMapValue = value;
+ attributeType = ATTRMAP;
+
+ boolValue = false;
+ ulongValue = 0;
+}
+
+// Check the attribute type
+bool OSAttribute::isBooleanAttribute() const
+{
+ return (attributeType == BOOL);
+}
+
+bool OSAttribute::isUnsignedLongAttribute() const
+{
+ return (attributeType == ULONG);
+}
+
+bool OSAttribute::isByteStringAttribute() const
+{
+ return (attributeType == BYTESTR);
+}
+
+bool OSAttribute::isMechanismTypeSetAttribute() const
+{
+ return (attributeType == MECHSET);
+}
+
+bool OSAttribute::isAttributeMapAttribute() const
+{
+ return (attributeType == ATTRMAP);
+}
+
+// Retrieve the attribute value
+bool OSAttribute::getBooleanValue() const
+{
+ return boolValue;
+}
+
+unsigned long OSAttribute::getUnsignedLongValue() const
+{
+ return ulongValue;
+}
+
+const ByteString& OSAttribute::getByteStringValue() const
+{
+ return byteStrValue;
+}
+
+const std::set<CK_MECHANISM_TYPE>& OSAttribute::getMechanismTypeSetValue() const
+{
+ return mechSetValue;
+}
+
+const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& OSAttribute::getAttributeMapValue() const
+{
+ return attrMapValue;
+}
+
+// Helper for template (aka array) matching
+
+bool OSAttribute::peekValue(ByteString& value) const
+{
+ size_t counter = 0;
+ CK_MECHANISM_TYPE mech;
+
+ switch (attributeType)
+ {
+ case BOOL:
+ value.resize(sizeof(boolValue));
+ memcpy(&value[0], &boolValue, value.size());
+ return true;
+
+ case ULONG:
+ value.resize(sizeof(ulongValue));
+ memcpy(&value[0], &ulongValue, value.size());
+ return true;
+
+ case BYTESTR:
+ value.resize(byteStrValue.size());
+ memcpy(&value[0], byteStrValue.const_byte_str(), value.size());
+ return true;
+
+ case MECHSET:
+ value.resize(mechSetValue.size() * sizeof(mech));
+ for (std::set<CK_MECHANISM_TYPE>::const_iterator i = mechSetValue.begin(); i != mechSetValue.end(); ++i)
+ {
+ mech = *i;
+ memcpy(&value[0] + counter * sizeof(mech), &mech, sizeof(mech));
+ counter++;
+ }
+ return true;
+
+ case ATTRMAP:
+ return false;
+
+ default:
+ return false;
+ }
+}
diff --git a/SoftHSMv2/src/lib/object_store/OSAttribute.h b/SoftHSMv2/src/lib/object_store/OSAttribute.h
new file mode 100644
index 0000000..303a5b9
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSAttribute.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSAttribute.h
+
+ This class represents the object store view on an object's attribute
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OSATTRIBUTE_H
+#define _SOFTHSM_V2_OSATTRIBUTE_H
+
+#include "config.h"
+#include "ByteString.h"
+#include <map>
+#include <set>
+
+class OSAttribute
+{
+public:
+ // Copy constructor
+ OSAttribute(const OSAttribute& in);
+
+ // Constructor for a boolean type attribute
+ OSAttribute(const bool value);
+
+ // Constructor for an unsigned long type attribute
+ OSAttribute(const unsigned long value);
+
+ // Constructor for a byte string type attribute
+ OSAttribute(const ByteString& value);
+
+ // Constructor for a mechanism type set type attribute
+ OSAttribute(const std::set<CK_MECHANISM_TYPE>& value);
+
+ // Constructor for an attribute map type attribute
+ OSAttribute(const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value);
+
+ // Destructor
+ virtual ~OSAttribute() { }
+
+ // Check the attribute type
+ bool isBooleanAttribute() const;
+ bool isUnsignedLongAttribute() const;
+ bool isByteStringAttribute() const;
+ bool isMechanismTypeSetAttribute() const;
+ bool isAttributeMapAttribute() const;
+
+ // Retrieve the attribute value
+ bool getBooleanValue() const;
+ unsigned long getUnsignedLongValue() const;
+ const ByteString& getByteStringValue() const;
+ const std::set<CK_MECHANISM_TYPE>& getMechanismTypeSetValue() const;
+ const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& getAttributeMapValue() const;
+
+ // Helper for template (aka array) matching
+ bool peekValue(ByteString& value) const;
+
+private:
+ // The attribute type
+ enum
+ {
+ BOOL,
+ ULONG,
+ BYTESTR,
+ MECHSET,
+ ATTRMAP
+ }
+ attributeType;
+
+ // The attribute value
+ bool boolValue;
+ unsigned long ulongValue;
+ ByteString byteStrValue;
+ std::set<CK_MECHANISM_TYPE> mechSetValue;
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> attrMapValue;
+};
+
+#endif // !_SOFTHSM_V2_OSATTRIBUTE_H
+
diff --git a/SoftHSMv2/src/lib/object_store/OSAttributes.h b/SoftHSMv2/src/lib/object_store/OSAttributes.h
new file mode 100644
index 0000000..dfc5869
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSAttributes.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSAttributes.h
+
+ Specifies vendor defined attributes for use in internal object store files
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OSATTRIBUTES_H
+#define _SOFTHSM_V2_OSATTRIBUTES_H
+
+#include "config.h"
+#include "cryptoki.h"
+
+// Define vendor tag; presumably the one below is reasonably unique
+#define CKA_VENDOR_SOFTHSM (CKA_VENDOR_DEFINED + 0x5348) // 'SH'
+
+// Vendor defined attribute types for the token file
+#define CKA_OS_TOKENLABEL (CKA_VENDOR_SOFTHSM + 1)
+#define CKA_OS_TOKENSERIAL (CKA_VENDOR_SOFTHSM + 2)
+#define CKA_OS_TOKENFLAGS (CKA_VENDOR_SOFTHSM + 3)
+#define CKA_OS_SOPIN (CKA_VENDOR_SOFTHSM + 4)
+#define CKA_OS_USERPIN (CKA_VENDOR_SOFTHSM + 5)
+
+#endif // !_SOFTHSM_V2_OSATTRIBUTES_H
+
diff --git a/SoftHSMv2/src/lib/object_store/OSObject.h b/SoftHSMv2/src/lib/object_store/OSObject.h
new file mode 100644
index 0000000..6efaa6d
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSObject.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSObject.h
+
+ This file contains the abstract interface for ObjectStore objects. It is
+ implemented by persistent objects in the form of the ObjectFile class and
+ by session objects in the form of the SessionObject class
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OSOBJECT_H
+#define _SOFTHSM_V2_OSOBJECT_H
+
+#include "config.h"
+#include "OSAttribute.h"
+#include "cryptoki.h"
+
+class OSObject
+{
+public:
+ // Destructor
+ virtual ~OSObject() { }
+
+ // Check if the specified attribute exists
+ virtual bool attributeExists(CK_ATTRIBUTE_TYPE type) = 0;
+
+ // Retrieve the specified attribute
+ virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type) = 0;
+ virtual bool getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val) = 0;
+ virtual unsigned long getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val) = 0;
+ virtual ByteString getByteStringValue(CK_ATTRIBUTE_TYPE type) = 0;
+
+ // Retrieve the next attribute type
+ virtual CK_ATTRIBUTE_TYPE nextAttributeType(CK_ATTRIBUTE_TYPE type) = 0;
+
+ // Set the specified attribute
+ virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute) = 0;
+
+ // Delete the specified attribute
+ virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type) = 0;
+
+ // The validity state of the object
+ virtual bool isValid() = 0;
+
+ // Start an attribute set transaction; this method is used when - for
+ // example - a key is generated and all its attributes need to be
+ // persisted in one go.
+ //
+ // N.B.: Starting a transaction locks the object!
+ //
+ // Function returns false in case a transaction is already in progress
+ enum Access {
+ ReadOnly,
+ ReadWrite
+ };
+ virtual bool startTransaction(Access access = ReadWrite) = 0;
+
+ // Commit an attribute transaction; returns false if no transaction is in progress
+ virtual bool commitTransaction() = 0;
+
+ // Abort an attribute transaction; loads back the previous version of the object from disk;
+ // returns false if no transaction was in progress
+ virtual bool abortTransaction() = 0;
+
+ // Destroys the object (warning, any pointers to the object are no longer
+ // valid after this call because delete is called!)
+ virtual bool destroyObject() = 0;
+};
+
+#endif // !_SOFTHSM_V2_OSOBJECT_H
+
diff --git a/SoftHSMv2/src/lib/object_store/OSPathSep.h b/SoftHSMv2/src/lib/object_store/OSPathSep.h
new file mode 100644
index 0000000..f714d24
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSPathSep.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSPathSep.h
+
+ Determine the OS specific path separator
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OSPATHSEP_H
+#define _SOFTHSM_V2_OSPATHSEP_H
+
+#include "config.h"
+
+#ifdef _WIN32
+#define OS_PATHSEP "\\"
+#else
+#define OS_PATHSEP "/"
+#endif
+
+#endif // !_SOFTHSM_V2_OSPATHSEP_H
+
diff --git a/SoftHSMv2/src/lib/object_store/OSToken.cpp b/SoftHSMv2/src/lib/object_store/OSToken.cpp
new file mode 100644
index 0000000..13b1eaa
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSToken.cpp
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSToken.cpp
+
+ The token class; a token is stored in a directory containing several files.
+ Each object is stored in a separate file and a token object is present that
+ has the token specific attributes
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "OSAttributes.h"
+#include "OSAttribute.h"
+#include "ObjectFile.h"
+#include "Directory.h"
+#include "Generation.h"
+#include "UUID.h"
+#include "cryptoki.h"
+#include "OSToken.h"
+#include "OSPathSep.h"
+#include <vector>
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+#include <stdio.h>
+
+// Constructor
+OSToken::OSToken(const std::string inTokenPath)
+{
+ tokenPath = inTokenPath;
+
+ tokenDir = new Directory(tokenPath);
+ gen = Generation::create(tokenPath + OS_PATHSEP + "generation", true);
+ tokenObject = new ObjectFile(this, tokenPath + OS_PATHSEP + "token.object", tokenPath + OS_PATHSEP + "token.lock");
+ tokenMutex = MutexFactory::i()->getMutex();
+ valid = (gen != NULL) && (tokenMutex != NULL) && tokenDir->isValid() && tokenObject->valid;
+
+ DEBUG_MSG("Opened token %s", tokenPath.c_str());
+
+ index(true);
+}
+
+// Create a new token
+/*static*/ OSToken* OSToken::createToken(const std::string basePath, const std::string tokenDir, const ByteString& label, const ByteString& serial)
+{
+ Directory baseDir(basePath);
+
+ if (!baseDir.isValid())
+ {
+ ERROR_MSG("Could not create the Directory object");
+ return NULL;
+ }
+
+ // Create the token directory
+ if (!baseDir.mkdir(tokenDir))
+ {
+ // Error msg is generated by mkdir
+ return NULL;
+ }
+
+ // Create the token object
+ ObjectFile tokenObject(NULL, basePath + OS_PATHSEP + tokenDir + OS_PATHSEP + "token.object", basePath + OS_PATHSEP + tokenDir + OS_PATHSEP + "token.lock", true);
+
+ if (!tokenObject.valid)
+ {
+ std::string tokenPath = basePath + OS_PATHSEP + tokenDir + OS_PATHSEP + "token.[object|lock]";
+ ERROR_MSG("Failed to create the token object: %s", tokenPath.c_str());
+
+ baseDir.rmdir(tokenDir);
+
+ return NULL;
+ }
+
+ // Set the initial attributes
+ CK_ULONG flags =
+ CKF_RNG |
+ CKF_LOGIN_REQUIRED | // FIXME: check
+ CKF_RESTORE_KEY_NOT_NEEDED |
+ CKF_TOKEN_INITIALIZED |
+ CKF_SO_PIN_LOCKED |
+ CKF_SO_PIN_TO_BE_CHANGED;
+
+ OSAttribute tokenLabel(label);
+ OSAttribute tokenSerial(serial);
+ OSAttribute tokenFlags(flags);
+
+ if (!tokenObject.setAttribute(CKA_OS_TOKENLABEL, tokenLabel) ||
+ !tokenObject.setAttribute(CKA_OS_TOKENSERIAL, tokenSerial) ||
+ !tokenObject.setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
+ {
+ ERROR_MSG("Failed to set the token attributes");
+
+ baseDir.remove(tokenDir + OS_PATHSEP + "token.object");
+ baseDir.remove(tokenDir + OS_PATHSEP + "token.lock");
+ baseDir.rmdir(tokenDir);
+
+ return NULL;
+ }
+
+ DEBUG_MSG("Created new token %s", tokenDir.c_str());
+
+ return new OSToken(basePath + OS_PATHSEP + tokenDir);
+}
+
+// Access an existing token
+/*static*/ OSToken *OSToken::accessToken(const std::string &basePath, const std::string &tokenDir)
+{
+ return new OSToken(basePath + OS_PATHSEP + tokenDir);
+}
+
+// Destructor
+OSToken::~OSToken()
+{
+ // Clean up
+ std::set<OSObject*> cleanUp = allObjects;
+ allObjects.clear();
+
+ for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ delete *i;
+ }
+
+ delete tokenDir;
+ if (gen != NULL) delete gen;
+ MutexFactory::i()->recycleMutex(tokenMutex);
+ delete tokenObject;
+}
+
+// Set the SO PIN
+bool OSToken::setSOPIN(const ByteString& soPINBlob)
+{
+ if (!valid) return false;
+
+ OSAttribute soPIN(soPINBlob);
+
+ CK_ULONG flags;
+
+ if (tokenObject->setAttribute(CKA_OS_SOPIN, soPIN) &&
+ getTokenFlags(flags))
+ {
+ flags &= ~CKF_SO_PIN_COUNT_LOW;
+ flags &= ~CKF_SO_PIN_FINAL_TRY;
+ flags &= ~CKF_SO_PIN_LOCKED;
+ flags &= ~CKF_SO_PIN_TO_BE_CHANGED;
+
+ return setTokenFlags(flags);
+ }
+
+ return false;
+}
+
+// Get the SO PIN
+bool OSToken::getSOPIN(ByteString& soPINBlob)
+{
+ if (!valid || !tokenObject->isValid())
+ {
+ return false;
+ }
+
+ if (tokenObject->attributeExists(CKA_OS_SOPIN))
+ {
+ soPINBlob = tokenObject->getAttribute(CKA_OS_SOPIN).getByteStringValue();
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Set the user PIN
+bool OSToken::setUserPIN(ByteString userPINBlob)
+{
+ if (!valid) return false;
+
+ OSAttribute userPIN(userPINBlob);
+
+ CK_ULONG flags;
+
+ if (tokenObject->setAttribute(CKA_OS_USERPIN, userPIN) &&
+ getTokenFlags(flags))
+ {
+ flags |= CKF_USER_PIN_INITIALIZED;
+ flags &= ~CKF_USER_PIN_COUNT_LOW;
+ flags &= ~CKF_USER_PIN_FINAL_TRY;
+ flags &= ~CKF_USER_PIN_LOCKED;
+ flags &= ~CKF_USER_PIN_TO_BE_CHANGED;
+
+ return setTokenFlags(flags);
+ }
+
+ return false;
+}
+
+// Get the user PIN
+bool OSToken::getUserPIN(ByteString& userPINBlob)
+{
+ if (!valid || !tokenObject->isValid())
+ {
+ return false;
+ }
+
+ if (tokenObject->attributeExists(CKA_OS_USERPIN))
+ {
+ userPINBlob = tokenObject->getAttribute(CKA_OS_USERPIN).getByteStringValue();
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Retrieve the token label
+bool OSToken::getTokenLabel(ByteString& label)
+{
+ if (!valid || !tokenObject->isValid())
+ {
+ return false;
+ }
+
+ if (tokenObject->attributeExists(CKA_OS_TOKENLABEL))
+ {
+ label = tokenObject->getAttribute(CKA_OS_TOKENLABEL).getByteStringValue();
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Retrieve the token serial
+bool OSToken::getTokenSerial(ByteString& serial)
+{
+ if (!valid || !tokenObject->isValid())
+ {
+ return false;
+ }
+
+ if (tokenObject->attributeExists(CKA_OS_TOKENSERIAL))
+ {
+ serial = tokenObject->getAttribute(CKA_OS_TOKENSERIAL).getByteStringValue();
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Get the token flags
+bool OSToken::getTokenFlags(CK_ULONG& flags)
+{
+ if (!valid || !tokenObject->isValid())
+ {
+ return false;
+ }
+
+ if (tokenObject->attributeExists(CKA_OS_TOKENFLAGS))
+ {
+ flags = tokenObject->getAttribute(CKA_OS_TOKENFLAGS).getUnsignedLongValue();
+
+ // Check if the user PIN is initialised
+ if (tokenObject->attributeExists(CKA_OS_USERPIN))
+ {
+ flags |= CKF_USER_PIN_INITIALIZED;
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Set the token flags
+bool OSToken::setTokenFlags(const CK_ULONG flags)
+{
+ if (!valid) return false;
+
+ OSAttribute tokenFlags(flags);
+
+ return tokenObject->setAttribute(CKA_OS_TOKENFLAGS, tokenFlags);
+}
+
+// Retrieve objects
+std::set<OSObject*> OSToken::getObjects()
+{
+ index();
+
+ // Make sure that no other thread is in the process of changing
+ // the object list when we return it
+ MutexLocker lock(tokenMutex);
+
+ return objects;
+}
+
+void OSToken::getObjects(std::set<OSObject*> &inObjects)
+{
+ index();
+
+ // Make sure that no other thread is in the process of changing
+ // the object list when we return it
+ MutexLocker lock(tokenMutex);
+
+ inObjects.insert(objects.begin(),objects.end());
+}
+
+// Create a new object
+OSObject* OSToken::createObject()
+{
+ if (!valid) return NULL;
+
+ // Generate a name for the object
+ std::string objectUUID = UUID::newUUID();
+ std::string objectPath = tokenPath + OS_PATHSEP + objectUUID + ".object";
+ std::string lockPath = tokenPath + OS_PATHSEP + objectUUID + ".lock";
+
+ // Create the new object file
+ ObjectFile* newObject = new ObjectFile(this, objectPath, lockPath, true);
+
+ if (!newObject->valid)
+ {
+ ERROR_MSG("Failed to create new object %s", objectPath.c_str());
+
+ delete newObject;
+
+ return NULL;
+ }
+
+ // Now add it to the set of objects
+ MutexLocker lock(tokenMutex);
+
+ objects.insert(newObject);
+ allObjects.insert(newObject);
+ currentFiles.insert(newObject->getFilename());
+
+ DEBUG_MSG("(0x%08X) Created new object %s (0x%08X)", this, objectPath.c_str(), newObject);
+
+ gen->update();
+
+ gen->commit();
+
+ return newObject;
+}
+
+// Delete an object
+bool OSToken::deleteObject(OSObject* object)
+{
+ if (!valid) return false;
+
+ if (objects.find(object) == objects.end())
+ {
+ ERROR_MSG("Cannot delete non-existent object 0x%08X", object);
+
+ return false;
+ }
+
+ MutexLocker lock(tokenMutex);
+
+ ObjectFile* fileObject = dynamic_cast<ObjectFile*>(object);
+ if (fileObject == NULL)
+ {
+ ERROR_MSG("Object type not compatible with this token class 0x%08X", object);
+
+ return false;
+ }
+
+ // Invalidate the object instance
+ fileObject->invalidate();
+
+ // Retrieve the filename of the object
+ std::string objectFilename = fileObject->getFilename();
+
+ // Attempt to delete the file
+ if (!tokenDir->remove(objectFilename))
+ {
+ ERROR_MSG("Failed to delete object file %s", objectFilename.c_str());
+
+ return false;
+ }
+
+ // Retrieve the filename of the lock
+ std::string lockFilename = fileObject->getLockname();
+
+ // Attempt to delete the lock
+ if (!tokenDir->remove(lockFilename))
+ {
+ ERROR_MSG("Failed to delete lock file %s", lockFilename.c_str());
+
+ return false;
+ }
+
+ objects.erase(object);
+
+ DEBUG_MSG("Deleted object %s", objectFilename.c_str());
+
+ gen->update();
+
+ gen->commit();
+
+ return true;
+}
+
+// Checks if the token is consistent
+bool OSToken::isValid()
+{
+ return valid;
+}
+
+// Invalidate the token (for instance if it is deleted)
+void OSToken::invalidate()
+{
+ valid = false;
+}
+
+// Delete the token
+bool OSToken::clearToken()
+{
+ MutexLocker lock(tokenMutex);
+
+ // Invalidate the token
+ invalidate();
+
+ // First, clear out all objects
+ objects.clear();
+
+ // Now, delete all files in the token directory
+ if (!tokenDir->refresh())
+ {
+ return false;
+ }
+
+ std::vector<std::string> tokenFiles = tokenDir->getFiles();
+
+ for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++)
+ {
+ if (!tokenDir->remove(*i))
+ {
+ ERROR_MSG("Failed to remove %s from token directory %s", i->c_str(), tokenPath.c_str());
+
+ return false;
+ }
+ }
+
+ // Now remove the token directory
+ if (!tokenDir->rmdir(""))
+ {
+ ERROR_MSG("Failed to remove the token directory %s", tokenPath.c_str());
+
+ return false;
+ }
+
+ DEBUG_MSG("Token instance %s was succesfully cleared", tokenPath.c_str());
+
+ return true;
+}
+
+// Reset the token
+bool OSToken::resetToken(const ByteString& label)
+{
+ CK_ULONG flags;
+
+ if (!getTokenFlags(flags))
+ {
+ ERROR_MSG("Failed to get the token attributes");
+
+ return false;
+ }
+
+ // Clean up
+ std::set<OSObject*> cleanUp = getObjects();
+
+ MutexLocker lock(tokenMutex);
+
+ for (std::set<OSObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ ObjectFile* fileObject = dynamic_cast<ObjectFile*>(*i);
+ if (fileObject == NULL)
+ {
+ ERROR_MSG("Object type not compatible with this token class 0x%08X", *i);
+
+ return false;
+ }
+
+ // Invalidate the object instance
+ fileObject->invalidate();
+
+ // Retrieve the filename of the object
+ std::string objectFilename = fileObject->getFilename();
+
+ // Attempt to delete the file
+ if (!tokenDir->remove(objectFilename))
+ {
+ ERROR_MSG("Failed to delete object file %s", objectFilename.c_str());
+
+ return false;
+ }
+
+ // Retrieve the filename of the lock
+ std::string lockFilename = fileObject->getLockname();
+
+ // Attempt to delete the lock
+ if (!tokenDir->remove(lockFilename))
+ {
+ ERROR_MSG("Failed to delete lock file %s", lockFilename.c_str());
+
+ return false;
+ }
+
+ objects.erase(*i);
+
+ DEBUG_MSG("Deleted object %s", objectFilename.c_str());
+ }
+
+ // The user PIN has been removed
+ flags &= ~CKF_USER_PIN_INITIALIZED;
+ flags &= ~CKF_USER_PIN_COUNT_LOW;
+ flags &= ~CKF_USER_PIN_FINAL_TRY;
+ flags &= ~CKF_USER_PIN_LOCKED;
+ flags &= ~CKF_USER_PIN_TO_BE_CHANGED;
+
+ // Set new token attributes
+ OSAttribute tokenLabel(label);
+ OSAttribute tokenFlags(flags);
+
+ if (!tokenObject->setAttribute(CKA_OS_TOKENLABEL, tokenLabel) ||
+ !tokenObject->setAttribute(CKA_OS_TOKENFLAGS, tokenFlags))
+ {
+ ERROR_MSG("Failed to set the token attributes");
+
+ return false;
+ }
+
+ if (tokenObject->attributeExists(CKA_OS_USERPIN) &&
+ !tokenObject->deleteAttribute(CKA_OS_USERPIN))
+ {
+ ERROR_MSG("Failed to remove USERPIN");
+
+ return false;
+ }
+
+ DEBUG_MSG("Token instance %s was succesfully reset", tokenPath.c_str());
+
+ gen->update();
+ gen->commit();
+
+ return true;
+}
+
+// Index the token
+bool OSToken::index(bool isFirstTime /* = false */)
+{
+ // No access to object mutable fields before
+ MutexLocker lock(tokenMutex);
+
+ // Check if re-indexing is required
+ if (!isFirstTime && (!valid || !gen->wasUpdated()))
+ {
+ DEBUG_MSG("No re-indexing is required");
+
+ return true;
+ }
+
+ // Check the integrity
+ if (!tokenDir->refresh() || !tokenObject->valid)
+ {
+ ERROR_MSG("Token integrity check failed");
+
+ valid = false;
+
+ return false;
+ }
+
+ DEBUG_MSG("Token %s has changed", tokenPath.c_str());
+
+ // Retrieve the directory listing
+ std::vector<std::string> tokenFiles = tokenDir->getFiles();
+
+ // Filter out the objects
+ std::set<std::string> newSet;
+
+ for (std::vector<std::string>::iterator i = tokenFiles.begin(); i != tokenFiles.end(); i++)
+ {
+ if ((i->size() > 7) &&
+ (!(i->substr(i->size() - 7).compare(".object"))) &&
+ (i->compare("token.object")))
+ {
+ newSet.insert(*i);
+ }
+ else
+ {
+ DEBUG_MSG("Ignored file %s", i->c_str());
+ }
+ }
+
+ // Compute the changes compared to the last list of files
+ std::set<std::string> addedFiles;
+ std::set<std::string> removedFiles;
+
+ if (!isFirstTime)
+ {
+ // First compute which files were added
+ for (std::set<std::string>::iterator i = newSet.begin(); i != newSet.end(); i++)
+ {
+ if (currentFiles.find(*i) == currentFiles.end())
+ {
+ addedFiles.insert(*i);
+ }
+ }
+
+ // Now compute which files were removed
+ for (std::set<std::string>::iterator i = currentFiles.begin(); i != currentFiles.end(); i++)
+ {
+ if (newSet.find(*i) == newSet.end())
+ {
+ removedFiles.insert(*i);
+ }
+ }
+ }
+ else
+ {
+ addedFiles = newSet;
+ }
+
+ currentFiles = newSet;
+
+ DEBUG_MSG("%d objects were added and %d objects were removed", addedFiles.size(), removedFiles.size());
+ DEBUG_MSG("Current directory set contains %d objects", currentFiles.size());
+
+ // Now update the set of objects
+
+ // Add new objects
+ for (std::set<std::string>::iterator i = addedFiles.begin(); i != addedFiles.end(); i++)
+ {
+ if ((i->find_last_of('.') == std::string::npos) ||
+ (i->substr(i->find_last_of('.')) != ".object"))
+ {
+ continue;
+ }
+
+ std::string lockName(*i);
+ lockName.replace(lockName.find_last_of('.'), std::string::npos, ".lock");
+
+ // Create a new token object for the added file
+ ObjectFile* newObject = new ObjectFile(this, tokenPath + OS_PATHSEP + *i, tokenPath + OS_PATHSEP + lockName);
+
+ // Add the object, even invalid ones.
+ // This is so the we can read the attributes once
+ // the other process has finished writing to disc.
+ DEBUG_MSG("(0x%08X) New object %s (0x%08X) added", this, newObject->getFilename().c_str(), newObject);
+ objects.insert(newObject);
+ allObjects.insert(newObject);
+ }
+
+ // Remove deleted objects
+ std::set<OSObject*> newObjects;
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ObjectFile* fileObject = dynamic_cast<ObjectFile*>((*i));
+ if (fileObject == NULL)
+ {
+ ERROR_MSG("Object type not compatible with this token class 0x%08X", (*i));
+
+ return false;
+ }
+
+ DEBUG_MSG("Processing %s (0x%08X)", fileObject->getFilename().c_str(), *i);
+
+ if (removedFiles.find(fileObject->getFilename()) == removedFiles.end())
+ {
+ DEBUG_MSG("Adding object %s", fileObject->getFilename().c_str());
+ // This object gets to stay in the set
+ newObjects.insert(*i);
+ }
+ else
+ {
+ fileObject->invalidate();
+ }
+ }
+
+ // Set the new objects
+ objects = newObjects;
+
+ DEBUG_MSG("The token now contains %d objects", objects.size());
+
+ return true;
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/OSToken.h b/SoftHSMv2/src/lib/object_store/OSToken.h
new file mode 100644
index 0000000..4ba5b2c
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/OSToken.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSToken.h
+
+ The token class; a token is stored in a directory containing several files.
+ Each object is stored in a separate file and a token object is present that
+ has the token specific attributes
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OSTOKEN_H
+#define _SOFTHSM_V2_OSTOKEN_H
+
+#include "config.h"
+#include "ObjectStoreToken.h"
+#include "OSAttribute.h"
+#include "ObjectFile.h"
+#include "Directory.h"
+#include "Generation.h"
+#include "UUID.h"
+#include "MutexFactory.h"
+#include "cryptoki.h"
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+
+class OSToken : public ObjectStoreToken
+{
+public:
+ // Constructor
+ OSToken(const std::string inTokenPath);
+
+ // Create a new token
+ static OSToken* createToken(const std::string basePath, const std::string tokenDir, const ByteString& label, const ByteString& serial);
+
+ // Access an existing token
+ static OSToken* accessToken(const std::string &basePath, const std::string &tokenDir);
+
+ // Constructor for new tokens
+ OSToken(const std::string tokenPath, const ByteString& label, const ByteString& serialNumber);
+
+ // Set the SO PIN
+ virtual bool setSOPIN(const ByteString& soPINBlob);
+
+ // Get the SO PIN
+ virtual bool getSOPIN(ByteString& soPINBlob);
+
+ // Set the user PIN
+ virtual bool setUserPIN(ByteString userPINBlob);
+
+ // Get the user PIN
+ virtual bool getUserPIN(ByteString& userPINBlob);
+
+ // Get the token flags
+ virtual bool getTokenFlags(CK_ULONG& flags);
+
+ // Set the token flags
+ virtual bool setTokenFlags(const CK_ULONG flags);
+
+ // Retrieve the token label
+ virtual bool getTokenLabel(ByteString& label);
+
+ // Retrieve the token serial
+ virtual bool getTokenSerial(ByteString& serial);
+
+ // Retrieve objects
+ virtual std::set<OSObject*> getObjects();
+
+ // Insert objects into the given set
+ virtual void getObjects(std::set<OSObject*> &inObjects);
+
+ // Create a new object
+ virtual OSObject* createObject();
+
+ // Delete an object
+ virtual bool deleteObject(OSObject* object);
+
+ // Destructor
+ virtual ~OSToken();
+
+ // Checks if the token is consistent
+ virtual bool isValid();
+
+ // Invalidate the token (for instance if it is deleted)
+ virtual void invalidate();
+
+ // Delete the token
+ virtual bool clearToken();
+
+ // Reset the token
+ virtual bool resetToken(const ByteString& label);
+
+private:
+ // ObjectFile instances can call the index() function
+ friend class ObjectFile;
+
+ // Index the token
+ bool index(bool isFirstTime = false);
+
+ // Is the token consistent and valid?
+ bool valid;
+
+ // The token path
+ std::string tokenPath;
+
+ // The current objects of the token
+ std::set<OSObject*> objects;
+
+ // All the objects ever associated with this token
+ //
+ // This set is kept to be able to clean up when the token
+ // instance is discarded; in case the contents of a token
+ // change, some objects may disappear but we cannot simply
+ // delete them since they may still be referenced from an
+ // object outside of this class.
+ std::set<OSObject*> allObjects;
+
+ // The current list of files
+ std::set<std::string> currentFiles;
+
+ // The token object
+ ObjectFile* tokenObject;
+
+ // Generation control
+ Generation* gen;
+
+ // The directory object for this token
+ Directory* tokenDir;
+
+ // For thread safeness
+ Mutex* tokenMutex;
+};
+
+#endif // !_SOFTHSM_V2_OSTOKEN_H
+
diff --git a/SoftHSMv2/src/lib/object_store/ObjectFile.cpp b/SoftHSMv2/src/lib/object_store/ObjectFile.cpp
new file mode 100644
index 0000000..f3becbe
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/ObjectFile.cpp
@@ -0,0 +1,870 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectFile.h
+
+ This class represents object files
+ *****************************************************************************/
+
+#include "config.h"
+#include "ObjectFile.h"
+#include "OSToken.h"
+#include "OSPathSep.h"
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <set>
+
+// Attribute types
+#define BOOLEAN_ATTR 0x1
+#define ULONG_ATTR 0x2
+#define BYTESTR_ATTR 0x3
+#define ATTRMAP_ATTR 0x4
+#define MECHSET_ATTR 0x5
+
+// Constructor
+ObjectFile::ObjectFile(OSToken* parent, std::string inPath, std::string inLockpath, bool isNew /* = false */)
+{
+ path = inPath;
+ gen = Generation::create(path);
+ objectMutex = MutexFactory::i()->getMutex();
+ valid = (gen != NULL) && (objectMutex != NULL);
+ token = parent;
+ inTransaction = false;
+ transactionLockFile = NULL;
+ lockpath = inLockpath;
+
+ if (!valid) return;
+
+ if (!isNew)
+ {
+ DEBUG_MSG("Opened existing object %s", path.c_str());
+
+ refresh(true);
+ }
+ else
+ {
+ DEBUG_MSG("Created new object %s", path.c_str());
+
+ // Create an empty object file
+ store();
+ }
+
+}
+
+// Destructor
+ObjectFile::~ObjectFile()
+{
+ discardAttributes();
+
+ if (gen != NULL)
+ {
+ delete gen;
+ }
+
+ MutexFactory::i()->recycleMutex(objectMutex);
+}
+
+// Check if the specified attribute exists
+bool ObjectFile::attributeExists(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ return valid && (attributes[type] != NULL);
+}
+
+// Retrieve the specified attribute
+OSAttribute ObjectFile::getAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return OSAttribute((unsigned long)0);
+ }
+
+ return *attr;
+}
+
+bool ObjectFile::getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val)
+{
+ MutexLocker lock(objectMutex);
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return val;
+ }
+
+ if (attr->isBooleanAttribute())
+ {
+ return attr->getBooleanValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not a boolean: 0x%08X", type);
+ return val;
+ }
+}
+
+unsigned long ObjectFile::getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val)
+{
+ MutexLocker lock(objectMutex);
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return val;
+ }
+
+ if (attr->isUnsignedLongAttribute())
+ {
+ return attr->getUnsignedLongValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not an unsigned long: 0x%08X", type);
+ return val;
+ }
+}
+
+ByteString ObjectFile::getByteStringValue(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ ByteString val;
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return val;
+ }
+
+ if (attr->isByteStringAttribute())
+ {
+ return attr->getByteStringValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not a byte string: 0x%08X", type);
+ return val;
+ }
+}
+
+// Retrieve the next attribute type
+CK_ATTRIBUTE_TYPE ObjectFile::nextAttributeType(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ std::map<CK_ATTRIBUTE_TYPE, OSAttribute*>::iterator n = attributes.upper_bound(type);
+
+ // skip null attributes
+ while ((n != attributes.end()) && (n->second == NULL))
+ ++n;
+
+ // return type or CKA_CLASS (= 0)
+ if (n == attributes.end())
+ {
+ return CKA_CLASS;
+ }
+ else
+ {
+ return n->first;
+ }
+}
+
+// Set the specified attribute
+bool ObjectFile::setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute)
+{
+ if (!valid)
+ {
+ DEBUG_MSG("Cannot update invalid object %s", path.c_str());
+
+ return false;
+ }
+
+ {
+ MutexLocker lock(objectMutex);
+
+ if (attributes[type] != NULL)
+ {
+ delete attributes[type];
+
+ attributes[type] = NULL;
+ }
+
+ attributes[type] = new OSAttribute(attribute);
+ }
+
+ store();
+
+ return valid;
+}
+
+// Delete the specified attribute
+bool ObjectFile::deleteAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ if (!valid)
+ {
+ DEBUG_MSG("Cannot update invalid object %s", path.c_str());
+
+ return false;
+ }
+
+ {
+ MutexLocker lock(objectMutex);
+
+ if (attributes[type] == NULL)
+ {
+ DEBUG_MSG("Cannot delete attribute that doesn't exist in object %s", path.c_str());
+
+ return false;
+ }
+
+ delete attributes[type];
+ attributes.erase(type);
+ }
+
+ store();
+
+ return valid;
+}
+
+// The validity state of the object (refresh from disk as a side effect)
+bool ObjectFile::isValid()
+{
+ refresh();
+
+ return valid;
+}
+
+// Invalidate the object file externally; this method is normally
+// only called by the OSToken class in case an object file has
+// been deleted.
+void ObjectFile::invalidate()
+{
+ valid = false;
+
+ discardAttributes();
+}
+
+// Refresh the object if necessary
+void ObjectFile::refresh(bool isFirstTime /* = false */)
+{
+ // Check if we're in the middle of a transaction
+ if (inTransaction)
+ {
+ DEBUG_MSG("The object is in a transaction");
+
+ return;
+ }
+
+ // Refresh the associated token if set
+ if (!isFirstTime && (token != NULL))
+ {
+ // This may cause this instance to become invalid
+ token->index();
+ }
+
+ // Check the generation
+ if (!isFirstTime && (gen == NULL || !gen->wasUpdated()))
+ {
+ DEBUG_MSG("The object generation has not been updated");
+
+ return;
+ }
+
+ File objectFile(path);
+
+ if (!objectFile.isValid())
+ {
+ DEBUG_MSG("Object %s is invalid", path.c_str());
+
+ valid = false;
+
+ return;
+ }
+
+ objectFile.lock();
+
+ if (objectFile.isEmpty())
+ {
+ DEBUG_MSG("Object %s is empty", path.c_str());
+
+ valid = false;
+
+ return;
+ }
+
+ DEBUG_MSG("Object %s has changed", path.c_str());
+
+ // Discard the existing set of attributes
+ discardAttributes();
+
+ MutexLocker lock(objectMutex);
+
+ // Read back the generation number
+ unsigned long curGen;
+
+ if (!objectFile.readULong(curGen))
+ {
+ if (!objectFile.isEOF())
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+ }
+ else
+ {
+ gen->set(curGen);
+ }
+
+ // Read back the attributes
+ while (!objectFile.isEOF())
+ {
+ unsigned long p11AttrType;
+ unsigned long osAttrType;
+
+ if (!objectFile.readULong(p11AttrType))
+ {
+ if (objectFile.isEOF())
+ {
+ break;
+ }
+
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ if (!objectFile.readULong(osAttrType))
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ // Depending on the type, read back the actual value
+ if (osAttrType == BOOLEAN_ATTR)
+ {
+ bool value;
+
+ if (!objectFile.readBool(value))
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ if (attributes[p11AttrType] != NULL)
+ {
+ delete attributes[p11AttrType];
+ }
+
+ attributes[p11AttrType] = new OSAttribute(value);
+ }
+ else if (osAttrType == ULONG_ATTR)
+ {
+ unsigned long value;
+
+ if (!objectFile.readULong(value))
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ if (attributes[p11AttrType] != NULL)
+ {
+ delete attributes[p11AttrType];
+ }
+
+ attributes[p11AttrType] = new OSAttribute(value);
+ }
+ else if (osAttrType == BYTESTR_ATTR)
+ {
+ ByteString value;
+
+ if (!objectFile.readByteString(value))
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ if (attributes[p11AttrType] != NULL)
+ {
+ delete attributes[p11AttrType];
+ }
+
+ attributes[p11AttrType] = new OSAttribute(value);
+ }
+ else if (osAttrType == MECHSET_ATTR)
+ {
+ std::set<CK_MECHANISM_TYPE> value;
+
+ if (!objectFile.readMechanismTypeSet(value))
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ if (attributes[p11AttrType] != NULL)
+ {
+ delete attributes[p11AttrType];
+ }
+
+ attributes[p11AttrType] = new OSAttribute(value);
+ }
+ else if (osAttrType == ATTRMAP_ATTR)
+ {
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> value;
+
+ if (!objectFile.readAttributeMap(value))
+ {
+ DEBUG_MSG("Corrupt object file %s", path.c_str());
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+
+ if (attributes[p11AttrType] != NULL)
+ {
+ delete attributes[p11AttrType];
+ }
+
+ attributes[p11AttrType] = new OSAttribute(value);
+ }
+ else
+ {
+ DEBUG_MSG("Corrupt object file %s with unknown attribute of type %d", path.c_str(), osAttrType);
+
+ valid = false;
+
+ objectFile.unlock();
+
+ return;
+ }
+ }
+
+ objectFile.unlock();
+
+ valid = true;
+}
+
+// Common write part in store()
+// called with objectFile locked and returns with objectFile unlocked
+bool ObjectFile::writeAttributes(File &objectFile)
+{
+ if (!gen->sync(objectFile))
+ {
+ DEBUG_MSG("Failed to synchronize generation number from object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+
+ if (!objectFile.truncate())
+ {
+ DEBUG_MSG("Failed to reset object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+
+ gen->update();
+
+ unsigned long newGen = gen->get();
+
+ if (!objectFile.writeULong(newGen))
+ {
+ DEBUG_MSG("Failed to write new generation number to object %s", path.c_str());
+
+ gen->rollback();
+
+ objectFile.unlock();
+
+ return false;
+ }
+
+
+ for (std::map<CK_ATTRIBUTE_TYPE, OSAttribute*>::iterator i = attributes.begin(); i != attributes.end(); i++)
+ {
+ if (i->second == NULL)
+ {
+ continue;
+ }
+
+ unsigned long p11AttrType = i->first;
+
+ if (!objectFile.writeULong(p11AttrType))
+ {
+ DEBUG_MSG("Failed to write PKCS #11 attribute type to object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+
+ if (i->second->isBooleanAttribute())
+ {
+ unsigned long osAttrType = BOOLEAN_ATTR;
+ bool value = i->second->getBooleanValue();
+
+ if (!objectFile.writeULong(osAttrType) || !objectFile.writeBool(value))
+ {
+ DEBUG_MSG("Failed to write attribute to object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+ }
+ else if (i->second->isUnsignedLongAttribute())
+ {
+ unsigned long osAttrType = ULONG_ATTR;
+ unsigned long value = i->second->getUnsignedLongValue();
+
+ if (!objectFile.writeULong(osAttrType) || !objectFile.writeULong(value))
+ {
+ DEBUG_MSG("Failed to write attribute to object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+ }
+ else if (i->second->isByteStringAttribute())
+ {
+ unsigned long osAttrType = BYTESTR_ATTR;
+ const ByteString& value = i->second->getByteStringValue();
+
+ if (!objectFile.writeULong(osAttrType) || !objectFile.writeByteString(value))
+ {
+ DEBUG_MSG("Failed to write attribute to object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+ }
+ else if (i->second->isMechanismTypeSetAttribute())
+ {
+ unsigned long osAttrType = MECHSET_ATTR;
+ const std::set<CK_MECHANISM_TYPE>& value = i->second->getMechanismTypeSetValue();
+
+ if (!objectFile.writeULong(osAttrType) || !objectFile.writeMechanismTypeSet(value))
+ {
+ DEBUG_MSG("Failed to write attribute to object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+ }
+ else if (i->second->isAttributeMapAttribute())
+ {
+ unsigned long osAttrType = ATTRMAP_ATTR;
+ const std::map<CK_ATTRIBUTE_TYPE,OSAttribute>& value = i->second->getAttributeMapValue();
+
+ if (!objectFile.writeULong(osAttrType) || !objectFile.writeAttributeMap(value))
+ {
+ DEBUG_MSG("Failed to write attribute to object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+ }
+ else
+ {
+ DEBUG_MSG("Unknown attribute type for object %s", path.c_str());
+
+ objectFile.unlock();
+
+ return false;
+ }
+ }
+
+ objectFile.unlock();
+
+ return true;
+}
+
+// Write the object to background storage
+void ObjectFile::store(bool isCommit /* = false */)
+{
+ // Check if we're in the middle of a transaction
+ if (!isCommit && inTransaction)
+ {
+ return;
+ }
+
+ if (!valid)
+ {
+ DEBUG_MSG("Cannot write back an invalid object %s", path.c_str());
+
+ return;
+ }
+
+ File objectFile(path, true, true, true, false);
+
+ if (!objectFile.isValid())
+ {
+ DEBUG_MSG("Cannot open object %s for writing", path.c_str());
+
+ valid = false;
+
+ return;
+ }
+
+ objectFile.lock();
+
+ if (!isCommit) {
+ MutexLocker lock(objectMutex);
+ File lockFile(lockpath, false, true, true);
+
+ if (!writeAttributes(objectFile))
+ {
+ valid = false;
+
+ return;
+ }
+ }
+ else
+ {
+ if (!writeAttributes(objectFile))
+ {
+ valid = false;
+
+ return;
+ }
+ }
+
+ valid = true;
+}
+
+// Discard the cached attributes
+void ObjectFile::discardAttributes()
+{
+ MutexLocker lock(objectMutex);
+
+ std::map<CK_ATTRIBUTE_TYPE, OSAttribute*> cleanUp = attributes;
+ attributes.clear();
+
+ for (std::map<CK_ATTRIBUTE_TYPE, OSAttribute*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ if (i->second == NULL)
+ {
+ continue;
+ }
+
+ delete i->second;
+ i->second = NULL;
+ }
+}
+
+
+// Returns the file name of the object
+std::string ObjectFile::getFilename() const
+{
+ if ((path.find_last_of(OS_PATHSEP) != std::string::npos) &&
+ (path.find_last_of(OS_PATHSEP) < path.size()))
+ {
+ return path.substr(path.find_last_of(OS_PATHSEP) + 1);
+ }
+ else
+ {
+ return path;
+ }
+}
+
+// Returns the file name of the lock
+std::string ObjectFile::getLockname() const
+{
+ if ((lockpath.find_last_of(OS_PATHSEP) != std::string::npos) &&
+ (lockpath.find_last_of(OS_PATHSEP) < lockpath.size()))
+ {
+ return lockpath.substr(lockpath.find_last_of(OS_PATHSEP) + 1);
+ }
+ else
+ {
+ return lockpath;
+ }
+}
+
+// Start an attribute set transaction; this method is used when - for
+// example - a key is generated and all its attributes need to be
+// persisted in one go.
+//
+// N.B.: Starting a transaction locks the object!
+bool ObjectFile::startTransaction(Access)
+{
+ MutexLocker lock(objectMutex);
+
+ if (inTransaction)
+ {
+ return false;
+ }
+
+ transactionLockFile = new File(lockpath, false, true, true);
+
+ if (!transactionLockFile->isValid() || !transactionLockFile->lock())
+ {
+ delete transactionLockFile;
+ transactionLockFile = NULL;
+
+ ERROR_MSG("Failed to lock file %s for attribute transaction", lockpath.c_str());
+
+ return false;
+ }
+
+ inTransaction = true;
+
+ return true;
+}
+
+// Commit an attribute transaction
+bool ObjectFile::commitTransaction()
+{
+ MutexLocker lock(objectMutex);
+
+ if (!inTransaction)
+ {
+ return false;
+ }
+
+ if (transactionLockFile == NULL)
+ {
+ ERROR_MSG("Transaction lock file instance invalid!");
+
+ return false;
+ }
+
+ // Special store case
+ store(true);
+
+ if (!valid)
+ {
+ return false;
+ }
+
+ transactionLockFile->unlock();
+
+ delete transactionLockFile;
+ transactionLockFile = NULL;
+ inTransaction = false;
+
+ return true;
+}
+
+// Abort an attribute transaction; loads back the previous version of the object from disk
+bool ObjectFile::abortTransaction()
+{
+ {
+ MutexLocker lock(objectMutex);
+
+ if (!inTransaction)
+ {
+ return false;
+ }
+
+ if (transactionLockFile == NULL)
+ {
+ ERROR_MSG("Transaction lock file instance invalid!");
+
+ return false;
+ }
+
+ transactionLockFile->unlock();
+
+ delete transactionLockFile;
+ transactionLockFile = NULL;
+ inTransaction = false;
+ }
+
+ // Force reload from disk
+ refresh(true);
+
+ return true;
+}
+
+// Destroy the object; WARNING: pointers to the object become invalid after this call
+bool ObjectFile::destroyObject()
+{
+ if (token == NULL)
+ {
+ ERROR_MSG("Cannot destroy an object that is not associated with a token");
+
+ return false;
+ }
+
+ return token->deleteObject(this);
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/ObjectFile.h b/SoftHSMv2/src/lib/object_store/ObjectFile.h
new file mode 100644
index 0000000..a13d835
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/ObjectFile.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectFile.h
+
+ This class represents object files
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OBJECTFILE_H
+#define _SOFTHSM_V2_OBJECTFILE_H
+
+#include "config.h"
+#include "File.h"
+#include "Generation.h"
+#include "ByteString.h"
+#include "OSAttribute.h"
+#include "MutexFactory.h"
+#include <string>
+#include <map>
+#include <time.h>
+#include "cryptoki.h"
+#include "OSObject.h"
+
+// OSToken forward declaration
+class OSToken;
+
+class ObjectFile : public OSObject
+{
+public:
+ // Constructor
+ ObjectFile(OSToken* parent, const std::string inPath, const std::string inLockpath, bool isNew = false);
+
+ // Destructor
+ virtual ~ObjectFile();
+
+ // Check if the specified attribute exists
+ virtual bool attributeExists(CK_ATTRIBUTE_TYPE type);
+
+ // Retrieve the specified attribute
+ virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type);
+ virtual bool getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val);
+ virtual unsigned long getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val);
+ virtual ByteString getByteStringValue(CK_ATTRIBUTE_TYPE type);
+
+ // Retrieve the next attribute type
+ virtual CK_ATTRIBUTE_TYPE nextAttributeType(CK_ATTRIBUTE_TYPE type);
+
+ // Set the specified attribute
+ virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute);
+
+ // Delete the specified attribute
+ virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type);
+
+ // The validity state of the object (refresh from disk as a side effect)
+ virtual bool isValid();
+
+ // Invalidate the object file externally; this method is normally
+ // only called by the OSToken class in case an object file has
+ // been deleted.
+ void invalidate();
+
+ // Returns the file name of the object
+ std::string getFilename() const;
+
+ // Returns the file name of the lock
+ std::string getLockname() const;
+
+ // Start an attribute set transaction; this method is used when - for
+ // example - a key is generated and all its attributes need to be
+ // persisted in one go.
+ //
+ // N.B.: Starting a transaction locks the object!
+ //
+ // Function returns false in case a transaction is already in progress
+ virtual bool startTransaction(Access access);
+
+ // Commit an attribute transaction; returns false if no transaction is in progress
+ virtual bool commitTransaction();
+
+ // Abort an attribute transaction; loads back the previous version of the object from disk;
+ // returns false if no transaction was in progress
+ virtual bool abortTransaction();
+
+ // Destroys the object; WARNING: pointers to the object become invalid after this
+ // call!
+ virtual bool destroyObject();
+
+private:
+ // OSToken instances can read valid (vs calling IsValid() from index())
+ friend class OSToken;
+
+ // Refresh the object if necessary
+ void refresh(bool isFirstTime = false);
+
+ // Write the object to background storage
+ void store(bool isCommit = false);
+
+ // Store subroutine
+ bool writeAttributes(File &objectFile);
+
+ // Discard the cached attributes
+ void discardAttributes();
+
+ // The path to the file
+ std::string path;
+
+ // The Generation object that is used to detect changes in the
+ // object file from other SoftHSM instances
+ Generation* gen;
+
+ // The object's raw attributes
+ std::map<CK_ATTRIBUTE_TYPE, OSAttribute*> attributes;
+
+ // The object's validity state
+ bool valid;
+
+ // The token this object is associated with
+ OSToken* token;
+
+ // Mutex object for thread-safeness
+ Mutex* objectMutex;
+
+ // Is the object undergoing an attribute transaction?
+ bool inTransaction;
+ File* transactionLockFile;
+ std::string lockpath;
+};
+
+#endif // !_SOFTHSM_V2_OBJECTFILE_H
+
diff --git a/SoftHSMv2/src/lib/object_store/ObjectStore.cpp b/SoftHSMv2/src/lib/object_store/ObjectStore.cpp
new file mode 100644
index 0000000..3855d9d
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/ObjectStore.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectStore.h
+
+ The object store manages the separate tokens that the SoftHSM supports. Each
+ token is organised as a directory containing files that are contain the
+ token's objects. The object store is initialised with a root directory from
+ which it enumerates the tokens.
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "ObjectStore.h"
+#include "Directory.h"
+#include "ObjectStoreToken.h"
+#include "OSPathSep.h"
+#include "UUID.h"
+#include <stdio.h>
+
+// Constructor
+ObjectStore::ObjectStore(std::string inStorePath)
+{
+ storePath = inStorePath;
+ valid = false;
+ storeMutex = MutexFactory::i()->getMutex();
+
+ MutexLocker lock(storeMutex);
+
+ // Find all tokens in the specified path
+ Directory storeDir(storePath);
+
+ if (!storeDir.isValid())
+ {
+ WARNING_MSG("Failed to enumerate object store in %s", storePath.c_str());
+
+ return;
+ }
+
+ // Assume that all subdirectories are tokens
+ std::vector<std::string> dirs = storeDir.getSubDirs();
+
+ for (std::vector<std::string>::iterator i = dirs.begin(); i != dirs.end(); i++)
+ {
+ // Create a token instance
+ ObjectStoreToken* token = ObjectStoreToken::accessToken(storePath, *i);
+
+ if (!token->isValid())
+ {
+ ERROR_MSG("Failed to open token %s", i->c_str());
+
+ delete token;
+
+ // Silently ignore tokens that we do not have access to
+ continue;
+ }
+
+ tokens.push_back(token);
+ allTokens.push_back(token);
+ }
+
+ valid = true;
+}
+
+// Destructor
+ObjectStore::~ObjectStore()
+{
+ {
+ MutexLocker lock(storeMutex);
+
+ // Clean up
+ tokens.clear();
+
+ for (std::vector<ObjectStoreToken*>::iterator i = allTokens.begin(); i != allTokens.end(); i++)
+ {
+ delete *i;
+ }
+ }
+
+ MutexFactory::i()->recycleMutex(storeMutex);
+}
+
+// Check if the object store is valid
+bool ObjectStore::isValid()
+{
+ return valid;
+}
+
+// Return the number of tokens that is present
+size_t ObjectStore::getTokenCount()
+{
+ MutexLocker lock(storeMutex);
+
+ return tokens.size();
+}
+
+// Return a pointer to the n-th token (counting starts at 0)
+ObjectStoreToken* ObjectStore::getToken(size_t whichToken)
+{
+ MutexLocker lock(storeMutex);
+
+ if (whichToken >= tokens.size())
+ {
+ return NULL;
+ }
+
+ return tokens[whichToken];
+}
+
+// Create a new token
+ObjectStoreToken* ObjectStore::newToken(const ByteString& label)
+{
+ MutexLocker lock(storeMutex);
+
+ // Generate a UUID for the token
+ std::string tokenUUID = UUID::newUUID();
+
+ // Convert the UUID to a serial number
+ std::string serialNumber = tokenUUID.substr(19, 4) + tokenUUID.substr(24);
+ ByteString serial((const unsigned char*) serialNumber.c_str(), serialNumber.size());
+
+ // Create the token
+ ObjectStoreToken* newToken = ObjectStoreToken::createToken(storePath, tokenUUID, label, serial);
+
+ if (newToken != NULL)
+ {
+ tokens.push_back(newToken);
+ allTokens.push_back(newToken);
+ }
+
+ return newToken;
+}
+
+// Destroy a token
+bool ObjectStore::destroyToken(ObjectStoreToken *token)
+{
+ MutexLocker lock(storeMutex);
+
+ // Find the token
+ for (std::vector<ObjectStoreToken*>::iterator i = tokens.begin(); i != tokens.end(); i++)
+ {
+ if (*i == token)
+ {
+ // Found the token, now destroy the token
+ if (!token->clearToken())
+ {
+ ERROR_MSG("Failed to clear token instance");
+
+ return false;
+ }
+
+ // And remove it from the vector
+ tokens.erase(i);
+
+ return true;
+ }
+ }
+
+ ERROR_MSG("Could not find the token instance to destroy");
+
+ return false;
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/ObjectStore.h b/SoftHSMv2/src/lib/object_store/ObjectStore.h
new file mode 100644
index 0000000..e8ecd1a
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/ObjectStore.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectStore.h
+
+ The object store manages the separate tokens that the SoftHSM supports. Each
+ token is organised as a directory containing files that are contain the
+ token's objects. The object store is initialised with a root directory from
+ which it enumerates the tokens.
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OBJECTSTORE_H
+#define _SOFTHSM_V2_OBJECTSTORE_H
+
+#include "config.h"
+#include "ByteString.h"
+#include "ObjectStoreToken.h"
+#include "MutexFactory.h"
+#include <string>
+#include <vector>
+
+class ObjectStore
+{
+public:
+ // Constructor
+ ObjectStore(std::string inStorePath);
+
+ // Destructor
+ virtual ~ObjectStore();
+
+ // Return the number of tokens that is present
+ size_t getTokenCount();
+
+ // Return a pointer to the n-th token (counting starts at 0)
+ ObjectStoreToken* getToken(size_t whichToken);
+
+ // Create a new token
+ ObjectStoreToken* newToken(const ByteString& label);
+
+ // Destroy a token
+ bool destroyToken(ObjectStoreToken* token);
+
+ // Check if the object store is valid
+ bool isValid();
+
+private:
+ // The tokens
+ std::vector<ObjectStoreToken*> tokens;
+
+ // All tokens
+ std::vector<ObjectStoreToken*> allTokens;
+
+ // The object store root directory
+ std::string storePath;
+
+ // The status
+ bool valid;
+
+ // Object store synchronisation
+ Mutex* storeMutex;
+};
+
+#endif // !_SOFTHSM_V2_OBJECTSTORE_H
+
diff --git a/SoftHSMv2/src/lib/object_store/ObjectStoreToken.cpp b/SoftHSMv2/src/lib/object_store/ObjectStoreToken.cpp
new file mode 100644
index 0000000..24c2049
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/ObjectStoreToken.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectStoreToken.cpp
+
+ The object store abstract token base class
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "ObjectStoreToken.h"
+
+// OSToken is a concrete implementation of ObjectStoreToken base class.
+#include "OSToken.h"
+
+#ifdef HAVE_OBJECTSTORE_BACKEND_DB
+// DBToken is a concrete implementation of ObjectSToreToken that stores the objects and attributes in an SQLite3 database.
+#include "DBToken.h"
+#endif
+
+typedef ObjectStoreToken* (*CreateToken)(const std::string , const std::string , const ByteString& , const ByteString& );
+typedef ObjectStoreToken* (*AccessToken)(const std::string &, const std::string &);
+
+static CreateToken static_createToken = reinterpret_cast<CreateToken>(OSToken::createToken);
+static AccessToken static_accessToken = reinterpret_cast<AccessToken>(OSToken::accessToken);
+
+// Create a new token
+/*static*/ bool ObjectStoreToken::selectBackend(const std::string &backend)
+{
+ if (backend == "file")
+ {
+ static_createToken = reinterpret_cast<CreateToken>(OSToken::createToken);
+ static_accessToken = reinterpret_cast<AccessToken>(OSToken::accessToken);
+ }
+#ifdef HAVE_OBJECTSTORE_BACKEND_DB
+ else if (backend == "db")
+ {
+ static_createToken = reinterpret_cast<CreateToken>(DBToken::createToken);
+ static_accessToken = reinterpret_cast<AccessToken>(DBToken::accessToken);
+ }
+#endif
+ else
+ {
+ ERROR_MSG("Unknown value (%s) for objectstore.backend in configuration", backend.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+ObjectStoreToken* ObjectStoreToken::createToken(const std::string basePath, const std::string tokenDir, const ByteString& label, const ByteString& serial)
+{
+ return static_createToken(basePath,tokenDir,label,serial);
+}
+
+// Access an existing token
+/*static*/ ObjectStoreToken *ObjectStoreToken::accessToken(const std::string &basePath, const std::string &tokenDir)
+{
+ return static_accessToken(basePath, tokenDir);
+}
diff --git a/SoftHSMv2/src/lib/object_store/ObjectStoreToken.h b/SoftHSMv2/src/lib/object_store/ObjectStoreToken.h
new file mode 100644
index 0000000..668dccc
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/ObjectStoreToken.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectStoreToken.h
+
+ The object store abstract token base class;
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OBJECTSTORETOKEN_H
+#define _SOFTHSM_V2_OBJECTSTORETOKEN_H
+
+#include "config.h"
+#include "OSObject.h"
+#include <string>
+#include <set>
+
+class ObjectStoreToken
+{
+public:
+ // Select the type of backend to use for storing token objects.
+ static bool selectBackend(const std::string& backend);
+
+ // Create a new token
+ static ObjectStoreToken* createToken(const std::string basePath, const std::string tokenDir, const ByteString& label, const ByteString& serial);
+
+ // Access an existing token
+ static ObjectStoreToken* accessToken(const std::string &basePath, const std::string &tokenDir);
+
+ // Set the SO PIN
+ virtual bool setSOPIN(const ByteString& soPINBlob) = 0;
+
+ // Get the SO PIN
+ virtual bool getSOPIN(ByteString& soPINBlob) = 0;
+
+ // Set the user PIN
+ virtual bool setUserPIN(ByteString userPINBlob) = 0;
+
+ // Get the user PIN
+ virtual bool getUserPIN(ByteString& userPINBlob) = 0;
+
+ // Get the token flags
+ virtual bool getTokenFlags(CK_ULONG& flags) = 0;
+
+ // Set the token flags
+ virtual bool setTokenFlags(const CK_ULONG flags) = 0;
+
+ // Retrieve the token label
+ virtual bool getTokenLabel(ByteString& label) = 0;
+
+ // Retrieve the token serial
+ virtual bool getTokenSerial(ByteString& serial) = 0;
+
+ // Retrieve objects
+ virtual std::set<OSObject*> getObjects() = 0;
+
+ // Insert objects into the given set
+ virtual void getObjects(std::set<OSObject*> &objects) = 0;
+
+ // Create a new object
+ virtual OSObject* createObject() = 0;
+
+ // Delete an object
+ virtual bool deleteObject(OSObject* object) = 0;
+
+ // Destructor
+ virtual ~ObjectStoreToken() {};
+
+ // Checks if the token is consistent
+ virtual bool isValid() = 0;
+
+ // Invalidate the token (for instance if it is deleted)
+ virtual void invalidate() = 0;
+
+ // Delete the token
+ virtual bool clearToken() = 0;
+
+ // Reset the token
+ virtual bool resetToken(const ByteString& label) = 0;
+};
+
+#endif // !_SOFTHSM_V2_OBJECTSTORETOKEN_H
+
diff --git a/SoftHSMv2/src/lib/object_store/SessionObject.cpp b/SoftHSMv2/src/lib/object_store/SessionObject.cpp
new file mode 100644
index 0000000..49dfa7d
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/SessionObject.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObject.cpp
+
+ This class implements session objects (i.e. objects that are non-persistent)
+ *****************************************************************************/
+
+#include "config.h"
+#include "SessionObject.h"
+#include "SessionObjectStore.h"
+
+// Constructor
+SessionObject::SessionObject(SessionObjectStore* inParent, CK_SLOT_ID inSlotID, CK_SESSION_HANDLE inHSession, bool inIsPrivate)
+{
+ hSession = inHSession;
+ slotID = inSlotID;
+ isPrivate = inIsPrivate;
+ objectMutex = MutexFactory::i()->getMutex();
+ valid = (objectMutex != NULL);
+ parent = inParent;
+}
+
+// Destructor
+SessionObject::~SessionObject()
+{
+ discardAttributes();
+
+ MutexFactory::i()->recycleMutex(objectMutex);
+}
+
+// Check if the specified attribute exists
+bool SessionObject::attributeExists(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ return valid && (attributes[type] != NULL);
+}
+
+// Retrieve the specified attribute
+OSAttribute SessionObject::getAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return OSAttribute((unsigned long)0);
+ }
+
+ return *attr;
+}
+
+bool SessionObject::getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val)
+{
+ MutexLocker lock(objectMutex);
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return val;
+ }
+
+ if (attr->isBooleanAttribute())
+ {
+ return attr->getBooleanValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not a boolean: 0x%08X", type);
+ return val;
+ }
+}
+
+unsigned long SessionObject::getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val)
+{
+ MutexLocker lock(objectMutex);
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return val;
+ }
+
+ if (attr->isUnsignedLongAttribute())
+ {
+ return attr->getUnsignedLongValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not an unsigned long: 0x%08X", type);
+ return val;
+ }
+}
+
+ByteString SessionObject::getByteStringValue(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ ByteString val;
+
+ OSAttribute* attr = attributes[type];
+ if (attr == NULL)
+ {
+ ERROR_MSG("The attribute does not exist: 0x%08X", type);
+ return val;
+ }
+
+ if (attr->isByteStringAttribute())
+ {
+ return attr->getByteStringValue();
+ }
+ else
+ {
+ ERROR_MSG("The attribute is not a byte string: 0x%08X", type);
+ return val;
+ }
+}
+
+// Retrieve the next attribute type
+CK_ATTRIBUTE_TYPE SessionObject::nextAttributeType(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ std::map<CK_ATTRIBUTE_TYPE, OSAttribute*>::iterator n = attributes.upper_bound(type);
+
+ // skip null attributes
+ while ((n != attributes.end()) && (n->second == NULL))
+ ++n;
+
+
+ // return type or CKA_CLASS (= 0)
+ if (n == attributes.end())
+ {
+ return CKA_CLASS;
+ }
+ else
+ {
+ return n->first;
+ }
+}
+
+// Set the specified attribute
+bool SessionObject::setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute)
+{
+ MutexLocker lock(objectMutex);
+
+ if (!valid)
+ {
+ DEBUG_MSG("Cannot update invalid session object 0x%08X", this);
+
+ return false;
+ }
+
+ if (attributes[type] != NULL)
+ {
+ delete attributes[type];
+
+ attributes[type] = NULL;
+ }
+
+ attributes[type] = new OSAttribute(attribute);
+
+ return true;
+}
+
+// Delete the specified attribute
+bool SessionObject::deleteAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ MutexLocker lock(objectMutex);
+
+ if (!valid)
+ {
+ DEBUG_MSG("Cannot update invalid session object 0x%08X", this);
+
+ return false;
+ }
+
+ if (attributes[type] == NULL)
+ {
+ DEBUG_MSG("Cannot delete attribute that doesn't exist in object 0x%08X", this);
+
+ return false;
+ }
+
+ delete attributes[type];
+ attributes.erase(type);
+
+ return true;
+}
+
+// The validity state of the object
+bool SessionObject::isValid()
+{
+ return valid;
+}
+
+bool SessionObject::hasSlotID(CK_SLOT_ID inSlotID)
+{
+ return slotID == inSlotID;
+}
+
+// Called by the session object store when a session is closed. If it's the
+// session this object was associated with, the function returns true and the
+// object is invalidated
+bool SessionObject::removeOnSessionClose(CK_SESSION_HANDLE inHSession)
+{
+ if (hSession == inHSession)
+ {
+ // Save space
+ discardAttributes();
+
+ valid = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// Called by the session object store when a token is logged out.
+// Remove when this session object is a private object for this token.
+bool SessionObject::removeOnAllSessionsClose(CK_SLOT_ID inSlotID)
+{
+ if (slotID == inSlotID)
+ {
+ discardAttributes();
+
+ valid = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// Called by the session object store when a token is logged out.
+// Remove when this session object is a private object for this token.
+bool SessionObject::removeOnTokenLogout(CK_SLOT_ID inSlotID)
+{
+ if (slotID == inSlotID && isPrivate)
+ {
+ discardAttributes();
+
+ valid = false;
+
+ return true;
+ }
+
+ return false;
+}
+
+// Discard the object's attributes
+void SessionObject::discardAttributes()
+{
+ MutexLocker lock(objectMutex);
+
+ std::map<CK_ATTRIBUTE_TYPE, OSAttribute*> cleanUp = attributes;
+ attributes.clear();
+
+ for (std::map<CK_ATTRIBUTE_TYPE, OSAttribute*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ if (i->second == NULL)
+ {
+ continue;
+ }
+
+ delete i->second;
+ i->second = NULL;
+ }
+}
+
+// These functions are just stubs for session objects
+bool SessionObject::startTransaction(Access)
+{
+ return true;
+}
+
+bool SessionObject::commitTransaction()
+{
+ return true;
+}
+
+bool SessionObject::abortTransaction()
+{
+ return true;
+}
+
+bool SessionObject::destroyObject()
+{
+ if (parent == NULL)
+ {
+ ERROR_MSG("Cannot destroy object that is not associated with a session object store");
+
+ return false;
+ }
+
+ return parent->deleteObject(this);
+}
+
+// Invalidate the object
+void SessionObject::invalidate()
+{
+ valid = false;
+ discardAttributes();
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/SessionObject.h b/SoftHSMv2/src/lib/object_store/SessionObject.h
new file mode 100644
index 0000000..caadb64
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/SessionObject.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObject.h
+
+ This class implements session objects (i.e. objects that are non-persistent)
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_SESSIONOBJECT_H
+#define _SOFTHSM_V2_SESSIONOBJECT_H
+
+#include "config.h"
+#include "ByteString.h"
+#include "OSAttribute.h"
+#include "MutexFactory.h"
+#include <string>
+#include <map>
+#include "cryptoki.h"
+#include "OSObject.h"
+
+// Forward declaration of the session object store
+class SessionObjectStore;
+
+class SessionObject : public OSObject
+{
+public:
+ // Constructor
+ SessionObject(SessionObjectStore* inParent, CK_SLOT_ID inSlotID, CK_SESSION_HANDLE inHSession, bool inIsPrivate = false);
+
+ // Destructor
+ virtual ~SessionObject();
+
+ // Check if the specified attribute exists
+ virtual bool attributeExists(CK_ATTRIBUTE_TYPE type);
+
+ // Retrieve the specified attribute
+ virtual OSAttribute getAttribute(CK_ATTRIBUTE_TYPE type);
+ virtual bool getBooleanValue(CK_ATTRIBUTE_TYPE type, bool val);
+ virtual unsigned long getUnsignedLongValue(CK_ATTRIBUTE_TYPE type, unsigned long val);
+ virtual ByteString getByteStringValue(CK_ATTRIBUTE_TYPE type);
+
+ // Retrieve the next attribute type
+ virtual CK_ATTRIBUTE_TYPE nextAttributeType(CK_ATTRIBUTE_TYPE type);
+
+ // Set the specified attribute
+ virtual bool setAttribute(CK_ATTRIBUTE_TYPE type, const OSAttribute& attribute);
+
+ // Delete the specified attribute
+ virtual bool deleteAttribute(CK_ATTRIBUTE_TYPE type);
+
+ // The validity state of the object
+ virtual bool isValid();
+
+ bool hasSlotID(CK_SLOT_ID inSlotID);
+
+ // Called by the session object store when a session is closed. If it's the
+ // session this object was associated with, the function returns true and the
+ // object is invalidated
+ bool removeOnSessionClose(CK_SESSION_HANDLE inHSession);
+
+ // Called by the session object store when all the sessions for a token
+ // have been closed.
+ bool removeOnAllSessionsClose(CK_SLOT_ID inSlotID);
+
+ // Called by the session object store when a token is logged out.
+ // Remove when this session object is a private object for this token.
+ bool removeOnTokenLogout(CK_SLOT_ID inSlotID);
+
+ // These functions are just stubs for session objects
+ virtual bool startTransaction(Access access);
+ virtual bool commitTransaction();
+ virtual bool abortTransaction();
+
+ // Destroys the object; WARNING: pointers to the object become invalid after this
+ // call!
+ virtual bool destroyObject();
+
+ // Invalidate the object
+ void invalidate();
+
+private:
+ // Discard the object's attributes
+ void discardAttributes();
+
+ // The object's raw attributes
+ std::map<CK_ATTRIBUTE_TYPE, OSAttribute*> attributes;
+
+ // The object's validity state
+ bool valid;
+
+ // Mutex object for thread-safeness
+ Mutex* objectMutex;
+
+ // The slotID of the object is associated with.
+ CK_SLOT_ID slotID;
+
+ // The session the object is associated with.
+ CK_SESSION_HANDLE hSession;
+
+ // Indicates whether this object is private
+ bool isPrivate;
+
+ // The parent SessionObjectStore
+ SessionObjectStore* parent;
+};
+
+#endif // !_SOFTHSM_V2_SESSIONOBJECT_H
+
diff --git a/SoftHSMv2/src/lib/object_store/SessionObjectStore.cpp b/SoftHSMv2/src/lib/object_store/SessionObjectStore.cpp
new file mode 100644
index 0000000..3370d20
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/SessionObjectStore.cpp
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObjectStore.cpp
+
+ The token class; a token is stored in a directory containing several files.
+ Each object is stored in a separate file and a token object is present that
+ has the token specific attributes
+ *****************************************************************************/
+
+#include "config.h"
+#include "log.h"
+#include "OSAttributes.h"
+#include "OSAttribute.h"
+#include "SessionObject.h"
+#include "cryptoki.h"
+#include "SessionObjectStore.h"
+#include <vector>
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+
+// Constructor
+SessionObjectStore::SessionObjectStore()
+{
+ storeMutex = MutexFactory::i()->getMutex();
+}
+
+// Destructor
+SessionObjectStore::~SessionObjectStore()
+{
+ // Clean up
+ objects.clear();
+ std::set<SessionObject*> cleanUp = allObjects;
+ allObjects.clear();
+
+ for (std::set<SessionObject*>::iterator i = cleanUp.begin(); i != cleanUp.end(); i++)
+ {
+ if ((*i) == NULL) continue;
+
+ SessionObject* that = *i;
+ delete that;
+ }
+
+ MutexFactory::i()->recycleMutex(storeMutex);
+}
+
+// Retrieve objects
+std::set<SessionObject*> SessionObjectStore::getObjects()
+{
+ // Make sure that no other thread is in the process of changing
+ // the object list when we return it
+ MutexLocker lock(storeMutex);
+
+ return objects;
+}
+
+void SessionObjectStore::getObjects(CK_SLOT_ID slotID, std::set<OSObject*> &inObjects)
+{
+ // Make sure that no other thread is in the process of changing
+ // the object list when we return it
+ MutexLocker lock(storeMutex);
+
+ std::set<SessionObject*>::iterator it;
+ for (it=objects.begin(); it!=objects.end(); ++it) {
+ if ((*it)->hasSlotID(slotID))
+ inObjects.insert(*it);
+ }
+}
+
+// Create a new object
+SessionObject* SessionObjectStore::createObject(CK_SLOT_ID slotID, CK_SESSION_HANDLE hSession, bool isPrivate)
+{
+ // Create the new object file
+ SessionObject* newObject = new SessionObject(this, slotID, hSession, isPrivate);
+
+ if (!newObject->isValid())
+ {
+ ERROR_MSG("Failed to create new object");
+
+ delete newObject;
+
+ return NULL;
+ }
+
+ // Now add it to the set of objects
+ MutexLocker lock(storeMutex);
+
+ objects.insert(newObject);
+ allObjects.insert(newObject);
+
+ DEBUG_MSG("(0x%08X) Created new object (0x%08X)", this, newObject);
+
+ return newObject;
+}
+
+// Delete an object
+bool SessionObjectStore::deleteObject(SessionObject* object)
+{
+ if (objects.find(object) == objects.end())
+ {
+ ERROR_MSG("Cannot delete non-existent object 0x%08X", object);
+
+ return false;
+ }
+
+ MutexLocker lock(storeMutex);
+
+ // Invalidate the object instance
+ object->invalidate();
+
+ objects.erase(object);
+
+ return true;
+}
+
+// Indicate that a session has been closed; invalidates all objects
+// associated with this session
+void SessionObjectStore::sessionClosed(CK_SESSION_HANDLE hSession)
+{
+ MutexLocker lock(storeMutex);
+
+ std::set<SessionObject*> checkObjects = objects;
+
+ for (std::set<SessionObject*>::iterator i = checkObjects.begin(); i != checkObjects.end(); i++)
+ {
+ if ((*i)->removeOnSessionClose(hSession))
+ {
+ // Since the object remains in the allObjects set, any pointers to it will
+ // remain valid but it will no longer be returned when the set of objects
+ // is requested
+ objects.erase(*i);
+ }
+ }
+}
+
+void SessionObjectStore::allSessionsClosed(CK_SLOT_ID slotID)
+{
+ MutexLocker lock(storeMutex);
+
+ std::set<SessionObject*> checkObjects = objects;
+
+ for (std::set<SessionObject*>::iterator i = checkObjects.begin(); i != checkObjects.end(); i++)
+ {
+ if ((*i)->removeOnAllSessionsClose(slotID))
+ {
+ // Since the object remains in the allObjects set, any pointers to it will
+ // remain valid but it will no longer be returned when the set of objects
+ // is requested
+ objects.erase(*i);
+ }
+ }
+}
+
+void SessionObjectStore::tokenLoggedOut(CK_SLOT_ID slotID)
+{
+ MutexLocker lock(storeMutex);
+
+ std::set<SessionObject*> checkObjects = objects;
+
+ for (std::set<SessionObject*>::iterator i = checkObjects.begin(); i != checkObjects.end(); i++)
+ {
+ if ((*i)->removeOnTokenLogout(slotID))
+ {
+ // Since the object remains in the allObjects set, any pointers to it will
+ // remain valid but it will no longer be returned when the set of objects
+ // is requested
+ objects.erase(*i);
+ }
+ }
+}
+
+// Clear the whole store
+void SessionObjectStore::clearStore()
+{
+ MutexLocker lock(storeMutex);
+
+ objects.clear();
+ std::set<SessionObject*> clearObjects = allObjects;
+ allObjects.clear();
+
+ for (std::set<SessionObject*>::iterator i = clearObjects.begin(); i != clearObjects.end(); i++)
+ {
+ delete *i;
+ }
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/SessionObjectStore.h b/SoftHSMv2/src/lib/object_store/SessionObjectStore.h
new file mode 100644
index 0000000..2ec4bc5
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/SessionObjectStore.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObjectStore.h
+
+ The token class; a token is stored in a directory containing several files.
+ Each object is stored in a separate file and a token object is present that
+ has the token specific attributes
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_SESSIONOBJECTSTORE_H
+#define _SOFTHSM_V2_SESSIONOBJECTSTORE_H
+
+#include "config.h"
+#include "OSAttribute.h"
+#include "SessionObject.h"
+#include "MutexFactory.h"
+#include "cryptoki.h"
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+#include <memory>
+
+class SessionObjectStore
+{
+public:
+ // Constructor
+ SessionObjectStore();
+
+ // Retrieve objects
+ std::set<SessionObject*> getObjects();
+
+ // Insert the session objects for the given slotID into the given OSObject set
+ void getObjects(CK_SLOT_ID slotID, std::set<OSObject*> &inObjects);
+
+ // Create a new object
+ SessionObject* createObject(CK_SLOT_ID slotID, CK_SESSION_HANDLE hSession, bool isPrivate = false);
+
+ // Delete an object
+ bool deleteObject(SessionObject* object);
+
+ // Indicate that a session has been closed; invalidates all objects
+ // associated with this session.
+ void sessionClosed(CK_SESSION_HANDLE hSession);
+
+ // Indicate that for a token all sessions have been closed.
+ // Invalidates all objects associated with the token.
+ void allSessionsClosed(CK_SLOT_ID slotID);
+
+ // Indicate that a token has been logged out; invalidates all private
+ // objects associated with this token.
+ void tokenLoggedOut(CK_SLOT_ID slotID);
+
+ // Destructor
+ virtual ~SessionObjectStore();
+
+ // Clears the store; should be called when all sessions are closed
+ void clearStore();
+
+private:
+ // The current objects in the store
+ std::set<SessionObject*> objects;
+
+ // All the objects ever kept in the store
+ std::set<SessionObject*> allObjects;
+
+ // The current list of files
+ std::set<std::string> currentFiles;
+
+ // For thread safeness
+ Mutex* storeMutex;
+};
+
+#endif // !_SOFTHSM_V2_SESSIONOBJECTSTORE_H
+
diff --git a/SoftHSMv2/src/lib/object_store/UUID.cpp b/SoftHSMv2/src/lib/object_store/UUID.cpp
new file mode 100644
index 0000000..9e9f541
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/UUID.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ UUID.cpp
+
+ UUID generation helper functions
+ *****************************************************************************/
+
+#include "config.h"
+#include "UUID.h"
+#include "CryptoFactory.h"
+#include "RNG.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+// Generate a new UUID string
+std::string UUID::newUUID()
+{
+ RNG* rng = CryptoFactory::i()->getRNG();
+
+ ByteString uuid;
+
+ if (!rng->generateRandom(uuid, 16))
+ {
+ ERROR_MSG("Fatal, could not generate random UUID");
+
+ throw -1;
+ }
+
+ // Convert it to a string
+ char uuidStr[37];
+
+ sprintf(uuidStr, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid[0], uuid[1], uuid[2], uuid[3],
+ uuid[4], uuid[5],
+ uuid[6], uuid[7],
+ uuid[8], uuid[9],
+ uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+
+ return std::string(uuidStr);
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/UUID.h b/SoftHSMv2/src/lib/object_store/UUID.h
new file mode 100644
index 0000000..569bc00
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/UUID.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ UUID.h
+
+ UUID generation helper functions; for now, this just wraps the OSF/DCE's
+ UUID generation implementation, but if SoftHSM gets ported to non UNIX/BSD-
+ like OSes this may incorporate other implementations
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_UUID_H
+#define _SOFTHSM_V2_UUID_H
+
+#include "config.h"
+#include <string>
+
+namespace UUID
+{
+ // Generate a new UUID string
+ std::string newUUID();
+};
+
+#endif // !_SOFTHSM_V2_UUID_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.cpp b/SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.cpp
new file mode 100644
index 0000000..2cc8360
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBObjectStoreTests.cpp
+
+ Contains test cases to test the object store implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "DBObjectStoreTests.h"
+
+#include <cstdio>
+
+#ifndef HAVE_SQLITE3_H
+#error expected sqlite3 to be available
+#endif
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_newly_created_object_store);
+
+void test_a_newly_created_object_store::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+
+ ObjectStoreToken::selectBackend("db");
+
+ store = new ObjectStore("testdir");
+ nulltoken = NULL;
+}
+
+void test_a_newly_created_object_store::tearDown()
+{
+ delete store;
+
+ ObjectStoreToken::selectBackend("file");
+
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+
+void test_a_newly_created_object_store::contains_no_items()
+{
+ CPPUNIT_ASSERT_EQUAL(store->getTokenCount(), (size_t)0);
+}
+
+void test_a_newly_created_object_store::can_create_a_new_token()
+{
+ ByteString label1 = "DEADC0FFEE";
+
+ ObjectStoreToken *token1 = store->newToken(label1);
+ CPPUNIT_ASSERT(token1 != nulltoken);
+ CPPUNIT_ASSERT_EQUAL(store->getTokenCount(), (size_t)1);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_newly_created_object_store_containing_two_tokens);
+
+
+void test_a_newly_created_object_store_containing_two_tokens::setUp()
+{
+ test_a_newly_created_object_store::setUp();
+
+ ByteString label1 = "DEADC0FFEE";
+ ByteString label2 = "DEADBEEF";
+
+ ObjectStoreToken* token1 = store->newToken(label1);
+ CPPUNIT_ASSERT(token1 != nulltoken);
+ CPPUNIT_ASSERT_EQUAL(store->getTokenCount(), (size_t)1);
+
+ ObjectStoreToken* token2 = store->newToken(label2);
+ CPPUNIT_ASSERT(token2 != nulltoken);
+ CPPUNIT_ASSERT_EQUAL(store->getTokenCount(), (size_t)2);
+}
+
+void test_a_newly_created_object_store_containing_two_tokens::tearDown()
+{
+ ObjectStoreToken* token1 = store->getToken(0);
+ ObjectStoreToken* token2 = store->getToken(1);
+ CPPUNIT_ASSERT(store->destroyToken(token1));
+ CPPUNIT_ASSERT(store->destroyToken(token2));
+
+ test_a_newly_created_object_store::tearDown();
+}
+
+void test_a_newly_created_object_store_containing_two_tokens::has_two_tokens()
+{
+ CPPUNIT_ASSERT_EQUAL(store->getTokenCount(), (size_t)2);
+}
+
+void test_a_newly_created_object_store_containing_two_tokens::can_access_both_tokens()
+{
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* token1 = store->getToken(0);
+ ObjectStoreToken* token2 = store->getToken(1);
+
+ CPPUNIT_ASSERT(token1 != nulltoken);
+ CPPUNIT_ASSERT(token2 != nulltoken);
+}
+
+void test_a_newly_created_object_store_containing_two_tokens::assigned_labels_correctly_to_tokens()
+{
+ ByteString label1 = "DEADC0FFEE";
+ ByteString label2 = "DEADBEEF";
+
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* token1 = store->getToken(0);
+ ObjectStoreToken* token2 = store->getToken(1);
+
+ ByteString retrieveLabel1, retrieveLabel2;
+
+ CPPUNIT_ASSERT(token1->getTokenLabel(retrieveLabel1));
+ CPPUNIT_ASSERT(token2->getTokenLabel(retrieveLabel2));
+
+ CPPUNIT_ASSERT(label1 == retrieveLabel1 || label1 == retrieveLabel2);
+ CPPUNIT_ASSERT(label2 == retrieveLabel1 || label2 == retrieveLabel2);
+ CPPUNIT_ASSERT(label1 != label2);
+}
+
+void test_a_newly_created_object_store_containing_two_tokens::assigned_a_unique_serial_number_to_each_token()
+{
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* token1 = store->getToken(0);
+ ObjectStoreToken* token2 = store->getToken(1);
+
+ ByteString retrieveSerial1, retrieveSerial2;
+
+ CPPUNIT_ASSERT(token1->getTokenSerial(retrieveSerial1));
+ CPPUNIT_ASSERT(token2->getTokenSerial(retrieveSerial2));
+
+ CPPUNIT_ASSERT(retrieveSerial1 != retrieveSerial2);
+}
diff --git a/SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.h b/SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.h
new file mode 100644
index 0000000..7d100c8
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBObjectStoreTests.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBObjectStoreTests.h
+
+ Contains test cases to test the object store implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DBOBJECTSTORETESTS_H
+#define _SOFTHSM_V2_DBOBJECTSTORETESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include "ObjectStore.h"
+#include "ObjectStoreToken.h"
+
+class test_a_newly_created_object_store : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(test_a_newly_created_object_store);
+ CPPUNIT_TEST(contains_no_items);
+ CPPUNIT_TEST(can_create_a_new_token);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void contains_no_items();
+ void can_create_a_new_token();
+protected:
+ ObjectStore *store;
+ ObjectStoreToken *nulltoken;
+
+private:
+};
+
+class test_a_newly_created_object_store_containing_two_tokens : public test_a_newly_created_object_store
+{
+ CPPUNIT_TEST_SUITE(test_a_newly_created_object_store_containing_two_tokens);
+ CPPUNIT_TEST(has_two_tokens);
+ CPPUNIT_TEST(can_access_both_tokens);
+ CPPUNIT_TEST(assigned_labels_correctly_to_tokens);
+ CPPUNIT_TEST(assigned_a_unique_serial_number_to_each_token);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void has_two_tokens();
+ void can_access_both_tokens();
+ void assigned_labels_correctly_to_tokens();
+ void assigned_a_unique_serial_number_to_each_token();
+};
+
+#endif // !_SOFTHSM_V2_DBOBJECTSTORETESTS_H
diff --git a/SoftHSMv2/src/lib/object_store/test/DBObjectTests.cpp b/SoftHSMv2/src/lib/object_store/test/DBObjectTests.cpp
new file mode 100644
index 0000000..d856b06
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBObjectTests.cpp
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBObjectTests.cpp
+
+ Contains test cases to test the database token object implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "DBObjectTests.h"
+#include "DBObject.h"
+
+#include <cstdio>
+
+#ifndef HAVE_SQLITE3_H
+#error expected sqlite3 to be available
+#endif
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_dbobject);
+
+void test_a_dbobject::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+ connection = DB::Connection::Create("testdir","TestToken");
+ CPPUNIT_ASSERT(connection != NULL);
+ CPPUNIT_ASSERT(connection->connect("<1>"));
+ connection->setBusyTimeout(10);
+
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.startTransaction(DBObject::ReadWrite));
+ CPPUNIT_ASSERT(testObject.createTables());
+ CPPUNIT_ASSERT(testObject.commitTransaction());
+
+ connection2 = DB::Connection::Create("testdir","TestToken");
+ CPPUNIT_ASSERT(connection2 != NULL);
+ CPPUNIT_ASSERT(connection2->connect("<2>"));
+ connection2->setBusyTimeout(10);
+}
+
+void test_a_dbobject::tearDown()
+{
+ CPPUNIT_ASSERT(connection != NULL);
+ connection->close();
+ delete connection;
+
+ CPPUNIT_ASSERT(connection2 != NULL);
+ connection2->close();
+ delete connection2;
+
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void test_a_dbobject::should_be_insertable()
+{
+ DBObject tokenObject(connection);
+ CPPUNIT_ASSERT(!tokenObject.isValid());
+ CPPUNIT_ASSERT(tokenObject.insert());
+ CPPUNIT_ASSERT(tokenObject.isValid());
+ CPPUNIT_ASSERT_EQUAL(tokenObject.objectId(), (long long)1);
+}
+
+void test_a_dbobject::should_be_selectable()
+{
+ should_be_insertable();
+
+ DBObject tokenObject(connection);
+ CPPUNIT_ASSERT(tokenObject.find(1));
+ CPPUNIT_ASSERT(tokenObject.isValid());
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_dbobject_with_an_object);
+
+void test_a_dbobject_with_an_object::setUp()
+{
+ test_a_dbobject::setUp();
+ DBObject tokenObject(connection);
+ CPPUNIT_ASSERT(tokenObject.startTransaction(DBObject::ReadWrite));
+ CPPUNIT_ASSERT(!tokenObject.isValid());
+ CPPUNIT_ASSERT(tokenObject.insert());
+ CPPUNIT_ASSERT(tokenObject.isValid());
+ CPPUNIT_ASSERT_EQUAL(tokenObject.objectId(), (long long)1);
+ CPPUNIT_ASSERT(tokenObject.commitTransaction());
+
+}
+
+void test_a_dbobject_with_an_object::tearDown()
+{
+ test_a_dbobject::tearDown();
+}
+
+void test_a_dbobject_with_an_object::should_store_boolean_attributes()
+{
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ bool value2 = false;
+ bool value3 = true;
+ bool value4 = true;
+ bool value5 = false;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SENSITIVE, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_EXTRACTABLE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_NEVER_EXTRACTABLE, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr5));
+ }
+
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SENSITIVE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_EXTRACTABLE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_NEVER_EXTRACTABLE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SIGN));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SENSITIVE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_EXTRACTABLE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_NEVER_EXTRACTABLE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue());
+ CPPUNIT_ASSERT(!testObject.getAttribute(CKA_SENSITIVE).getBooleanValue());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_EXTRACTABLE).getBooleanValue());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_NEVER_EXTRACTABLE).getBooleanValue());
+ CPPUNIT_ASSERT(!testObject.getAttribute(CKA_SIGN).getBooleanValue());
+
+ bool value6 = true;
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VERIFY, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VERIFY).isBooleanAttribute());
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_VERIFY).getBooleanValue(), value6);
+ CPPUNIT_ASSERT_EQUAL(testObject.getBooleanValue(CKA_VERIFY, false), value6);
+ }
+}
+
+
+void test_a_dbobject_with_an_object::should_store_unsigned_long_attributes()
+{
+ // Add unsigned long attributes to the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ unsigned long value1 = 0x12345678;
+ unsigned long value2 = 0x87654321;
+ unsigned long value3 = 0x01010101;
+ unsigned long value4 = 0x10101010;
+ unsigned long value5 = 0xABCDEF;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_MODULUS_BITS, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_AUTH_PIN_FLAGS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBPRIME_BITS, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_KEY_TYPE, attr5));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_MODULUS_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_AUTH_PIN_FLAGS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBPRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_KEY_TYPE));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_AUTH_PIN_FLAGS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBPRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_KEY_TYPE).isUnsignedLongAttribute());
+
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_MODULUS_BITS).getUnsignedLongValue(), (unsigned long)0x12345678);
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue(), (unsigned long)0x87654321);
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_AUTH_PIN_FLAGS).getUnsignedLongValue(), (unsigned long)0x01010101);
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_SUBPRIME_BITS).getUnsignedLongValue(), (unsigned long)0x10101010);
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_KEY_TYPE).getUnsignedLongValue(), (unsigned long)0xABCDEF);
+
+ unsigned long value6 = 0x90909090;
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_CLASS, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_CLASS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_CLASS).getUnsignedLongValue(), value6);
+ CPPUNIT_ASSERT_EQUAL(testObject.getUnsignedLongValue(CKA_CLASS, 0x0), value6);
+ }
+}
+
+void test_a_dbobject_with_an_object::should_store_binary_attributes()
+{
+ ByteString value1 = "010203040506070809";
+ ByteString value2 = "ABABABABABABABABABABABABABABABABAB";
+ unsigned long value3 = 0xBDED;
+ ByteString value4 = "98A7E5D798A7E5D798A7E5D798A7E5D798A7E5D798A7E5D7";
+ ByteString value5 = "ABCDABCDABCDABCDABCDABCDABCDABCD";
+
+ // Create the test object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_MODULUS, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_COEFFICIENT, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PUBLIC_EXPONENT, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBJECT, attr5));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_MODULUS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_COEFFICIENT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PUBLIC_EXPONENT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBJECT));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_COEFFICIENT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PUBLIC_EXPONENT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS).getByteStringValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_COEFFICIENT).getByteStringValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PUBLIC_EXPONENT).getByteStringValue() == value4);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).getByteStringValue() == value5);
+
+ ByteString value6 = "909090908080808080807070707070FF";
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ISSUER, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ISSUER).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getByteStringValue(CKA_ISSUER) == value6);
+ }
+}
+
+void test_a_dbobject_with_an_object::should_store_mechtypeset_attributes()
+{
+
+ // Create the test object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ std::set<CK_MECHANISM_TYPE> set;
+ set.insert(CKM_SHA256);
+ set.insert(CKM_SHA512);
+ OSAttribute attr(set);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ std::set<CK_MECHANISM_TYPE> retrieved =
+ testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue();
+
+ CPPUNIT_ASSERT(retrieved.size() == 2);
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA256) != retrieved.end());
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA384) == retrieved.end());
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA512) != retrieved.end());
+ }
+}
+
+void test_a_dbobject_with_an_object::should_store_attrmap_attributes()
+{
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> mattr;
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_TOKEN, attr1));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_PRIME_BITS, attr2));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_VALUE, attr3));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_ALLOWED_MECHANISMS, attr4));
+ OSAttribute attra(mattr);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_WRAP_TEMPLATE, attra));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_WRAP_TEMPLATE));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_UNWRAP_TEMPLATE));
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> mattrb =
+ testObject.getAttribute(CKA_WRAP_TEMPLATE).getAttributeMapValue();
+ CPPUNIT_ASSERT(mattrb.size() == 4);
+ CPPUNIT_ASSERT(mattrb.find(CKA_TOKEN) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(mattrb.find(CKA_PRIME_BITS) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(mattrb.find(CKA_VALUE) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(mattrb.find(CKA_ALLOWED_MECHANISMS) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+ }
+}
+
+void test_a_dbobject_with_an_object::should_store_mixed_attributes()
+{
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+ unsigned long value3 = 0xBDEBDBED;
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue());
+ CPPUNIT_ASSERT_EQUAL(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue(), value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+ }
+}
+
+void test_a_dbobject_with_an_object::should_store_double_attributes()
+{
+ bool value1 = true;
+ bool value1a = false;
+
+ // Create the test object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr1));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SIGN));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue());
+
+ OSAttribute attr1(value1a);
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr1));
+
+ // Check the attributes
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue() == value1a);
+ }
+
+ // Now re-read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SIGN));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue() == value1a);
+ }
+}
+
+void test_a_dbobject_with_an_object::can_refresh_attributes()
+{
+ bool value1 = true;
+ bool value1a = false;
+ ByteString value2 = "BDEBDBEDBBDBEBDEBE792759537328";
+ ByteString value2a = "466487346943785684957634";
+ ByteString value3 = "0102010201020102010201020102010201020102";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+ std::set<CK_MECHANISM_TYPE> value4a;
+ value4a.insert(CKM_SHA384);
+ value4a.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr4(value4);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBJECT, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+ }
+
+ // Now read back the object
+ {
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SIGN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBJECT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).getByteStringValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+
+ OSAttribute attr1(value1a);
+ OSAttribute attr2(value2a);
+ OSAttribute attr4(value4a);
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBJECT, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+
+ // Check the attributes
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).getByteStringValue() == value2a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Open the object a second time
+ DBObject testObject2(connection);
+ CPPUNIT_ASSERT(testObject2.find(1));
+ CPPUNIT_ASSERT(testObject2.isValid());
+
+ // Check the attributes on the second instance
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_SIGN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_SUBJECT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_SIGN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_SUBJECT).getByteStringValue() == value2a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Add an attribute on the second object
+ OSAttribute attr3(value3);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ID, attr3));
+
+ // Check the attribute
+ CPPUNIT_ASSERT(testObject2.attributeExists(CKA_ID));
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).getByteStringValue() == value3);
+
+ // Now check that the first instance also knows about it
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ID));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).getByteStringValue() == value3);
+ }
+}
+
+void test_a_dbobject_with_an_object::should_cleanup_statements_during_transactions()
+{
+ // Create an object for accessing object 1 on the first connection.
+ DBObject testObject(connection);
+ // check transaction start(ro)/abort sequence
+ CPPUNIT_ASSERT(testObject.startTransaction(OSObject::ReadOnly));
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+ CPPUNIT_ASSERT(testObject.abortTransaction());
+}
+
+void test_a_dbobject_with_an_object::should_use_transactions()
+{
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+ unsigned long value3 = 0xBDEBDBED;
+ ByteString value4 = "AAAAAAAAAAAAAAAFFFFFFFFFFFFFFF";
+ std::set<CK_MECHANISM_TYPE> value5;
+ value5.insert(CKM_SHA256);
+ value5.insert(CKM_SHA512);
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ID, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr5));
+
+ // Create secondary instance for the same object.
+ // This needs to have a different connection to the database to simulate
+ // another process accessing the data.
+ DBObject testObject2(connection2);
+ CPPUNIT_ASSERT(testObject2.find(1));
+ CPPUNIT_ASSERT(testObject2.isValid());
+
+ // Check that it has the same attributes
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ // Check that the attributes have the same values as set on testObject.
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).getByteStringValue() == value4);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5);
+
+ // New values
+ bool value1a = false;
+ unsigned long value2a = 0x12345678;
+ unsigned long value3a = 0xABABABAB;
+ ByteString value4a = "EDEDEDEDEDEDEDEDEDEDEDEDEDEDED";
+ std::set<CK_MECHANISM_TYPE> value5a;
+ value5a.insert(CKM_SHA384);
+ value5a.insert(CKM_SHA512);
+
+ OSAttribute attr1a(value1a);
+ OSAttribute attr2a(value2a);
+ OSAttribute attr3a(value3a);
+ OSAttribute attr4a(value4a);
+ OSAttribute attr5a(value5a);
+
+ // Start transaction on object
+ CPPUNIT_ASSERT(testObject.startTransaction(DBObject::ReadWrite));
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ID, attr4a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr5a));
+
+ // Verify that the attributes were set
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).getByteStringValue() == value4a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5a);
+
+ // Verify that they are unchanged on the other instance
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).getByteStringValue() == value4);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5);
+
+ // Commit the transaction
+ CPPUNIT_ASSERT(testObject.commitTransaction());
+
+ // Verify that non-modifiable attributes did not propagate but modifiable attributes
+ // have now changed on the other instance
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ // NOTE: 3 attributes below cannot be modified after creation and therefore are not required to propagate.
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() != value1a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() != value2a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() != value3a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() != value5a);
+
+ // CKA_ID attribute can be modified after creation and therefore should have propagated.
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).getByteStringValue() == value4a);
+
+ // Start transaction on object
+ CPPUNIT_ASSERT(testObject.startTransaction(DBObject::ReadWrite));
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ID, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr5));
+
+ // Verify that the attributes were set
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).getByteStringValue() == value4);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5);
+
+ // Create a fresh third instance for the same object to force the data to be retrieved from the database.
+ DBObject testObject3(connection2);
+ CPPUNIT_ASSERT(testObject3.find(1));
+ CPPUNIT_ASSERT(testObject3.isValid());
+
+ // Verify that they are unchanged on the other instance, while the transaction is still in progress.
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ // Verify that the attributes from the database are still hodling the same value as when the transaction started.
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ID).getByteStringValue() == value4a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5a);
+
+ // Abort the transaction
+ CPPUNIT_ASSERT(testObject.abortTransaction());
+
+ // Verify that after aborting the transaction the values in testObject have reverted back to their
+ // original state.
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ // After aborting a transaction the testObject should be back to pre transaction state.
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).getByteStringValue() == value4a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5a);
+
+ // Verify that testObject3 still has the original values.
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_VALUE_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ // Verify that testObject3 still has the original values.
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_VALUE_BITS).getUnsignedLongValue() == value3a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ID).getByteStringValue() == value4a);
+ CPPUNIT_ASSERT(testObject3.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value5a);
+}
+
+void test_a_dbobject_with_an_object::should_fail_to_delete()
+{
+ DBObject testObject(connection);
+ CPPUNIT_ASSERT(testObject.find(1));
+ CPPUNIT_ASSERT(testObject.isValid());
+ // We don't attach the object to a token, and therefore should not be able to destroy it.
+ CPPUNIT_ASSERT(!testObject.destroyObject());
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/DBObjectTests.h b/SoftHSMv2/src/lib/object_store/test/DBObjectTests.h
new file mode 100644
index 0000000..136fa81
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBObjectTests.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBObjectTests.h
+
+ Contains test cases to test the database token object implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DBOBJECTTESTS_H
+#define _SOFTHSM_V2_DBOBJECTTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include "DB.h"
+
+class test_a_dbobject : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(test_a_dbobject);
+ CPPUNIT_TEST(should_be_insertable);
+ CPPUNIT_TEST(should_be_selectable);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void should_be_insertable();
+ void should_be_selectable();
+
+protected:
+ DB::Connection *connection;
+ DB::Connection *connection2;
+
+private:
+};
+
+class test_a_dbobject_with_an_object : public test_a_dbobject
+{
+ CPPUNIT_TEST_SUITE(test_a_dbobject_with_an_object);
+ CPPUNIT_TEST(should_store_boolean_attributes);
+ CPPUNIT_TEST(should_store_unsigned_long_attributes);
+ CPPUNIT_TEST(should_store_binary_attributes);
+ CPPUNIT_TEST(should_store_mechtypeset_attributes);
+ CPPUNIT_TEST(should_store_attrmap_attributes);
+ CPPUNIT_TEST(should_store_mixed_attributes);
+ CPPUNIT_TEST(should_store_double_attributes);
+ CPPUNIT_TEST(can_refresh_attributes);
+ CPPUNIT_TEST(should_cleanup_statements_during_transactions);
+ CPPUNIT_TEST(should_use_transactions);
+ CPPUNIT_TEST(should_fail_to_delete);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void should_store_boolean_attributes();
+ void should_store_unsigned_long_attributes();
+ void should_store_binary_attributes();
+ void should_store_mechtypeset_attributes();
+ void should_store_attrmap_attributes();
+ void should_store_mixed_attributes();
+ void should_store_double_attributes();
+ void can_refresh_attributes();
+ void should_cleanup_statements_during_transactions();
+ void should_use_transactions();
+ void should_fail_to_delete();
+};
+
+#endif // !_SOFTHSM_V2_DBOBJECTTESTS_H
diff --git a/SoftHSMv2/src/lib/object_store/test/DBTests.cpp b/SoftHSMv2/src/lib/object_store/test/DBTests.cpp
new file mode 100644
index 0000000..d787a83
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBTests.cpp
@@ -0,0 +1,648 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBTests.cpp
+
+ Contains lowest level test cases for the database backend implementation.
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "DBTests.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db);
+
+static int dummy_print(const char *, va_list )
+{
+ return 0;
+}
+
+void test_a_db::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+ null = NULL;
+}
+
+void test_a_db::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void test_a_db::checks_for_empty_connection_parameters()
+{
+ DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
+
+ DB::Connection *connection = DB::Connection::Create("","TestToken");
+ CPPUNIT_ASSERT_EQUAL(connection, null);
+
+ connection = DB::Connection::Create("testdir","");
+ CPPUNIT_ASSERT_EQUAL(connection, null);
+
+ connection = DB::Connection::Create("","");
+ CPPUNIT_ASSERT_EQUAL(connection, null);
+
+ DB::setLogErrorHandler(eh);
+}
+
+void test_a_db::can_be_connected_to_database()
+{
+
+ DB::Connection *connection = DB::Connection::Create("testdir","TestToken");
+ CPPUNIT_ASSERT(connection != null);
+ bool isConnected = connection->connect();
+ delete connection;
+ CPPUNIT_ASSERT(isConnected);
+#ifndef _WIN32
+ CPPUNIT_ASSERT_EQUAL(system("test -f ./testdir/TestToken"), 0);
+#else
+ CPPUNIT_ASSERT(GetFileAttributes("testdir\\TestToken") != INVALID_FILE_ATTRIBUTES);
+#endif
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db_with_a_connection);
+
+void test_a_db_with_a_connection::setUp()
+{
+ test_a_db::setUp();
+ connection = DB::Connection::Create("testdir","TestToken");
+ CPPUNIT_ASSERT(connection != null);
+ CPPUNIT_ASSERT(connection->connect());
+}
+
+void test_a_db_with_a_connection::tearDown()
+{
+ CPPUNIT_ASSERT(connection != null);
+ connection->close();
+ delete connection;
+ test_a_db::tearDown();
+}
+
+void test_a_db_with_a_connection::can_prepare_statements()
+{
+ DB::Statement statement = connection->prepare("PRAGMA database_list;");
+ CPPUNIT_ASSERT(statement.isValid());
+}
+
+void test_a_db_with_a_connection::can_perform_statements()
+{
+ DB::Statement statement = connection->prepare("PRAGMA database_list;");
+ CPPUNIT_ASSERT(statement.isValid());
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+ // only expect a single row in the result, so nextRow should now fail
+ CPPUNIT_ASSERT(!result.nextRow());
+}
+
+void test_a_db_with_a_connection::maintains_correct_refcounts()
+{
+ DB::Statement statement = connection->prepare("PRAGMA database_list;");
+ CPPUNIT_ASSERT_EQUAL(statement.refcount(), 1);
+ {
+ DB::Statement statement1 = statement;
+ DB::Statement statement2 = statement;
+ CPPUNIT_ASSERT_EQUAL(statement.refcount(), 3);
+ CPPUNIT_ASSERT(statement1.isValid());
+ CPPUNIT_ASSERT(statement2.isValid());
+ }
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT_EQUAL(statement.refcount(), 1);
+
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ // Statement is referenced by the result because it provides the query record cursor state.
+ CPPUNIT_ASSERT_EQUAL(statement.refcount(), 2);
+
+ result = DB::Result();
+ CPPUNIT_ASSERT_EQUAL(statement.refcount(), 1);
+}
+void test_a_db_with_a_connection::can_create_tables()
+{
+ CPPUNIT_ASSERT(!connection->tableExists("object"));
+ DB::Statement cr_object = connection->prepare("create table object (id integer primary key autoincrement);");
+ CPPUNIT_ASSERT(connection->execute(cr_object));
+ CPPUNIT_ASSERT(connection->tableExists("object"));
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db_with_a_connection_with_tables);
+
+void test_a_db_with_a_connection_with_tables::setUp()
+{
+ test_a_db_with_a_connection::setUp();
+ can_create_tables();
+
+ // attribute_text
+ CPPUNIT_ASSERT(!connection->tableExists("attribute_text"));
+ DB::Statement cr_attr_text = connection->prepare(
+ "create table attribute_text ("
+ "value text,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ CPPUNIT_ASSERT(connection->execute(cr_attr_text));
+ CPPUNIT_ASSERT(connection->tableExists("attribute_text"));
+
+ // attribute_integer
+ CPPUNIT_ASSERT(!connection->tableExists("attribute_integer"));
+ DB::Statement cr_attr_integer = connection->prepare(
+ "create table attribute_integer ("
+ "value integer,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ CPPUNIT_ASSERT(connection->execute(cr_attr_integer));
+ CPPUNIT_ASSERT(connection->tableExists("attribute_integer"));
+
+ // attribute_blob
+ CPPUNIT_ASSERT(!connection->tableExists("attribute_blob"));
+ DB::Statement cr_attr_blob = connection->prepare(
+ "create table attribute_blob ("
+ "value blob,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ CPPUNIT_ASSERT(connection->execute(cr_attr_blob));
+ CPPUNIT_ASSERT(connection->tableExists("attribute_blob"));
+
+ // attribute_boolean
+ CPPUNIT_ASSERT(!connection->tableExists("attribute_boolean"));
+ DB::Statement cr_attr_boolean = connection->prepare(
+ "create table attribute_boolean ("
+ "value boolean,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ CPPUNIT_ASSERT(connection->execute(cr_attr_boolean));
+ CPPUNIT_ASSERT(connection->tableExists("attribute_boolean"));
+
+ // attribute_datetime
+ CPPUNIT_ASSERT(!connection->tableExists("attribute_datetime"));
+ DB::Statement cr_attr_datetime = connection->prepare(
+ "create table attribute_datetime ("
+ "value datetime,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ CPPUNIT_ASSERT(connection->execute(cr_attr_datetime));
+ CPPUNIT_ASSERT(connection->tableExists("attribute_datetime"));
+
+ // attribute_real
+ CPPUNIT_ASSERT(!connection->tableExists("attribute_real"));
+ DB::Statement cr_attr_real = connection->prepare(
+ "create table attribute_real ("
+ "value real,"
+ "type integer,"
+ "object_id integer references object(id) on delete cascade,"
+ "id integer primary key autoincrement)"
+ );
+ CPPUNIT_ASSERT(connection->execute(cr_attr_real));
+ CPPUNIT_ASSERT(connection->tableExists("attribute_real"));
+}
+
+void test_a_db_with_a_connection_with_tables::tearDown()
+{
+ test_a_db_with_a_connection::tearDown();
+}
+
+void test_a_db_with_a_connection_with_tables::can_insert_records()
+{
+ DB::Statement statement = connection->prepare("insert into object default values");
+ CPPUNIT_ASSERT(connection->execute(statement));
+ long long object_id = connection->lastInsertRowId();
+ CPPUNIT_ASSERT(object_id != 0);
+
+ statement = connection->prepare(
+ "insert into attribute_text (value,type,object_id) values ('%s',%d,%lld)",
+ "testing testing testing",
+ 1234,
+ object_id);
+ CPPUNIT_ASSERT(connection->execute(statement));
+}
+
+void test_a_db_with_a_connection_with_tables::can_retrieve_records()
+{
+ can_insert_records();
+
+ DB::Statement statement = connection->prepare(
+ "select value from attribute_text as t where t.type=%d",
+ 1234);
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT_EQUAL(std::string(result.getString(1)), std::string("testing testing testing"));
+}
+
+void test_a_db_with_a_connection_with_tables::can_cascade_delete_objects_and_attributes()
+{
+ can_insert_records();
+
+ DB::Statement statement = connection->prepare("select id from object");
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ long long object_id = result.getLongLong(1);
+
+ statement = connection->prepare("delete from object where id=%lld",object_id);
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ statement = connection->prepare("select * from attribute_text where object_id=%lld",object_id);
+ result = connection->perform(statement);
+
+ // Check cascade delete was successful.
+ CPPUNIT_ASSERT(!result.isValid());
+}
+
+
+void test_a_db_with_a_connection_with_tables::can_update_text_attribute()
+{
+ can_insert_records();
+
+ // query all objects
+ DB::Statement statement = connection->prepare("select id from object");
+ CPPUNIT_ASSERT(statement.isValid());
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ long long object_id = result.getLongLong(1); // field indices start at 1
+
+ statement = connection->prepare(
+ "update attribute_text set value='test test test' where type=%d and object_id=%lld",
+ 1234,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+}
+
+void test_a_db_with_a_connection_with_tables::can_update_text_attribute_bound_value()
+{
+ can_insert_records();
+
+ // query all objects
+ DB::Statement statement = connection->prepare("select id from object");
+ CPPUNIT_ASSERT(statement.isValid());
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ long long object_id = result.getLongLong(1); // field indices start at 1
+
+ statement = connection->prepare(
+ "update attribute_text set value=? where type=%d and object_id=%lld",
+ 1234,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+
+ std::string msg("testing quote ' and accents é.");
+
+ CPPUNIT_ASSERT(DB::Bindings(statement).bindText(1,msg.c_str(),msg.size(),NULL));
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ statement = connection->prepare(
+ "select value from attribute_text as t where t.type=%d and t.object_id=%lld",
+ 1234,
+ object_id);
+ result = connection->perform(statement);
+ CPPUNIT_ASSERT_EQUAL(std::string(result.getString(1)), msg);
+}
+
+void test_a_db_with_a_connection_with_tables::can_update_integer_attribute_bound_value()
+{
+ // insert new object
+ DB::Statement statement = connection->prepare(
+ "insert into object default values");
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+ long long object_id = connection->lastInsertRowId();
+ CPPUNIT_ASSERT(object_id != 0);
+
+ // insert integer attribute
+ statement = connection->prepare(
+ "insert into attribute_integer (value,type,object_id) values (%lld,%d,%lld)",
+ 1111,
+ 1235,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // prepare update integer attribute statement
+ statement = connection->prepare(
+ "update attribute_integer set value=? where type=%d and object_id=%lld",
+ 1235,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+
+ // bind long long value to the parameter an update the record
+ CPPUNIT_ASSERT(DB::Bindings(statement).bindInt64(1,2222));
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // Retrieve the value from the record
+ DB::Statement retrieveStmt = connection->prepare(
+ "select value from attribute_integer as t where t.type=%d and t.object_id=%lld",
+ 1235,
+ object_id);
+ CPPUNIT_ASSERT(retrieveStmt.isValid());
+ DB::Result result = connection->perform(retrieveStmt);
+ CPPUNIT_ASSERT_EQUAL(result.getLongLong(1), (long long)2222);
+
+ // verify that binding to a parameter before resetting the statement will fail.
+ DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
+ DB::Bindings bindings(statement);
+ CPPUNIT_ASSERT(!bindings.bindInt(1,3333));
+ DB::setLogErrorHandler(eh);
+
+ // reset statement and bind another value to the statement
+ CPPUNIT_ASSERT(bindings.reset());
+ CPPUNIT_ASSERT(bindings.bindInt(1,3333));
+
+ // perform the update statement again with the newly bound value
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // reset the retrieve statement and perform it again to get the latest value of the integer attribute
+ CPPUNIT_ASSERT(retrieveStmt.reset());
+ result = connection->perform(retrieveStmt);
+ CPPUNIT_ASSERT(result.isValid());
+ CPPUNIT_ASSERT_EQUAL(result.getLongLong(1), (long long)3333);
+}
+
+void test_a_db_with_a_connection_with_tables::can_update_blob_attribute_bound_value()
+{
+ // insert new object
+ DB::Statement statement = connection->prepare(
+ "insert into object default values");
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+ long long object_id = connection->lastInsertRowId();
+ CPPUNIT_ASSERT(object_id != 0);
+
+ // insert blob attribute
+ statement = connection->prepare(
+ "insert into attribute_blob (value,type,object_id) values (X'012345',%d,%lld)",
+ 1236,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // prepare update blob attribute statement
+ statement = connection->prepare(
+ "update attribute_blob set value=? where type=%d and object_id=%lld",
+ 1236,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+
+ // bind blob (with embedded zero!) to the parameter
+ const char data[] = {10,11,0,12,13,14,15,16};
+ std::string msg(data,sizeof(data));
+ CPPUNIT_ASSERT(DB::Bindings(statement).bindBlob(1,msg.data(),msg.size(),NULL));
+
+ // update the blob value of the attribute
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // retrieve the blob value from the attribute
+ statement = connection->prepare(
+ "select value from attribute_blob as t where t.type=%d and t.object_id=%lld",
+ 1236,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ // check that the retrieved blob value matches the original data.
+ CPPUNIT_ASSERT_EQUAL(result.getFieldLength(1), sizeof(data));
+ std::string msgstored((const char *)result.getBinary(1),result.getFieldLength(1));
+ CPPUNIT_ASSERT_EQUAL(msg, msgstored);
+}
+
+
+void test_a_db_with_a_connection_with_tables::will_not_insert_non_existing_attribute_on_update()
+{
+ DB::Statement statement;
+ DB::Result result;
+
+ // Insert new object
+ statement = connection->prepare(
+ "insert into object default values");
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+ long long object_id = connection->lastInsertRowId();
+ CPPUNIT_ASSERT(object_id != 0);
+
+ // Updating an attribute before it is created will succeed, but will not insert an attribute.
+ statement = connection->prepare(
+ "update attribute_boolean set value=1 where type=%d and object_id=%lld",
+ 1237,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // Retrieve the boolean value from the attribute should fail
+ statement = connection->prepare(
+ "select value from attribute_boolean as t where t.type=%d and t.object_id=%lld",
+ 1237,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ result = connection->perform(statement);
+ CPPUNIT_ASSERT(!result.isValid());
+}
+
+
+void test_a_db_with_a_connection_with_tables::can_update_boolean_attribute_bound_value()
+{
+ //SQLite doesn't have a boolean data type, use 0 (false) and 1 (true)
+
+ DB::Statement statement;
+ DB::Result result;
+
+ // Insert new object
+ statement = connection->prepare(
+ "insert into object default values");
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+ long long object_id = connection->lastInsertRowId();
+ CPPUNIT_ASSERT(object_id != 0);
+
+ // insert boolean attribute
+ statement = connection->prepare(
+ "insert into attribute_boolean (value,type,object_id) values (1,%d,%lld)",
+ 1237,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // prepare update boolean attribute statement
+ statement = connection->prepare(
+ "update attribute_boolean set value=? where type=%d and object_id=%lld",
+ 1237,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+
+ // Bind 0 (false) to the first parameter
+ CPPUNIT_ASSERT(DB::Bindings(statement).bindInt(1,0));
+
+ // Execute the statement to update the attribute value.
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // Retrieve the boolean value from the attribute
+ statement = connection->prepare(
+ "select value from attribute_boolean as t where t.type=%d and t.object_id=%lld",
+ 1237,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ // check that the retrieved value matches the original value
+ CPPUNIT_ASSERT_EQUAL(result.getInt(1), 0);
+}
+
+
+void test_a_db_with_a_connection_with_tables::can_update_real_attribute_bound_value()
+{
+ // insert new object
+ DB::Statement statement = connection->prepare(
+ "insert into object default values");
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+ long long object_id = connection->lastInsertRowId();
+ CPPUNIT_ASSERT(object_id != 0);
+
+ // insert real value
+ statement = connection->prepare(
+ "insert into attribute_real (value,type,object_id) values(%f,%d,%lld)",
+ 1.238,
+ 1238,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // prepare update real attribute statement
+ statement = connection->prepare(
+ "update attribute_real set value=? where type=%d and object_id=%lld",
+ 1238,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+
+ // Bind 3333.3333 to the first parameter
+ CPPUNIT_ASSERT(DB::Bindings(statement).bindDouble(1,3333.3333));
+
+ // Execute the statement to update the attribute value
+ CPPUNIT_ASSERT(connection->execute(statement));
+
+ // Retrieve the double value from the attribute
+ statement = connection->prepare(
+ "select value from attribute_real as t where t.type=%d and t.object_id=%lld",
+ 1238,
+ object_id);
+ CPPUNIT_ASSERT(statement.isValid());
+ DB::Result result = connection->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ // check that the retrieved value matches the original value.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(result.getDouble(1), 3333.3333, 0.00001);
+}
+
+void test_a_db_with_a_connection_with_tables::supports_transactions()
+{
+ DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
+ CPPUNIT_ASSERT(!connection->rollbackTransaction());
+ DB::setLogErrorHandler(eh);
+
+ CPPUNIT_ASSERT(connection->beginTransactionRW());
+ CPPUNIT_ASSERT(connection->rollbackTransaction());
+
+ eh = DB::setLogErrorHandler(dummy_print);
+ CPPUNIT_ASSERT(!connection->commitTransaction());
+ DB::setLogErrorHandler(eh);
+
+ CPPUNIT_ASSERT(connection->beginTransactionRW());
+ can_update_real_attribute_bound_value();
+ CPPUNIT_ASSERT(connection->commitTransaction());
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_db_with_a_connection_with_tables_with_a_second_connection_open);
+
+void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::setUp()
+{
+ test_a_db_with_a_connection_with_tables::setUp();
+ connection2 = DB::Connection::Create("testdir","TestToken");
+ CPPUNIT_ASSERT(connection2 != null);
+ CPPUNIT_ASSERT(connection2->connect());
+ connection2->setBusyTimeout(10);
+}
+
+void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::tearDown()
+{
+ CPPUNIT_ASSERT(connection2 != null);
+ connection2->close();
+ delete connection2;
+ test_a_db_with_a_connection_with_tables::tearDown();
+}
+
+void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::handles_nested_transactions()
+{
+ DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
+
+ DB::Connection *connection1 = connection;
+
+ CPPUNIT_ASSERT(connection1->beginTransactionRW());
+
+ CPPUNIT_ASSERT(connection2->beginTransactionRO());
+ CPPUNIT_ASSERT(connection2->rollbackTransaction());
+ CPPUNIT_ASSERT(!connection2->beginTransactionRW());
+
+ CPPUNIT_ASSERT(connection1->commitTransaction());
+
+ DB::setLogErrorHandler(eh);
+}
+
+
+void test_a_db_with_a_connection_with_tables_with_a_second_connection_open::supports_transactions_with_other_connections_open()
+{
+ CPPUNIT_ASSERT(connection2->beginTransactionRO());
+
+ supports_transactions();
+
+ // Retrieve the double value from the attribute
+ DB::Statement statement = connection2->prepare(
+ "select value from attribute_real as t where t.type=%d and t.object_id=%lld",
+ 1238,
+ connection->lastInsertRowId());
+ CPPUNIT_ASSERT(statement.isValid());
+ DB::Result result = connection2->perform(statement);
+ CPPUNIT_ASSERT(result.isValid());
+
+ // check that the retrieved value matches the original value.
+ CPPUNIT_ASSERT_DOUBLES_EQUAL(result.getDouble(1), 3333.3333, 0.00001);
+
+ CPPUNIT_ASSERT(connection2->commitTransaction());
+}
diff --git a/SoftHSMv2/src/lib/object_store/test/DBTests.h b/SoftHSMv2/src/lib/object_store/test/DBTests.h
new file mode 100644
index 0000000..422cbce
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBTests.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBTests.h
+
+ Contains lowest level test cases for the database backend implementation.
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DBTESTS_H
+#define _SOFTHSM_V2_DBTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include "DB.h"
+
+class test_a_db : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(test_a_db);
+ CPPUNIT_TEST(checks_for_empty_connection_parameters);
+ CPPUNIT_TEST(can_be_connected_to_database);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void checks_for_empty_connection_parameters();
+ void can_be_connected_to_database();
+
+ void setUp();
+ void tearDown();
+
+protected:
+ DB::Connection *null;
+
+private:
+};
+
+class test_a_db_with_a_connection : public test_a_db
+{
+ CPPUNIT_TEST_SUITE(test_a_db_with_a_connection);
+ CPPUNIT_TEST(can_prepare_statements);
+ CPPUNIT_TEST(can_perform_statements);
+ CPPUNIT_TEST(maintains_correct_refcounts);
+ CPPUNIT_TEST(can_create_tables);
+ CPPUNIT_TEST_SUITE_END();
+public:
+ void setUp();
+ void tearDown();
+
+ void can_prepare_statements();
+ void can_perform_statements();
+ void maintains_correct_refcounts();
+ void can_create_tables();
+protected:
+ DB::Connection *connection;
+
+private:
+};
+
+class test_a_db_with_a_connection_with_tables : public test_a_db_with_a_connection
+{
+ CPPUNIT_TEST_SUITE(test_a_db_with_a_connection_with_tables);
+ CPPUNIT_TEST(can_insert_records);
+ CPPUNIT_TEST(can_retrieve_records);
+ CPPUNIT_TEST(can_cascade_delete_objects_and_attributes);
+ CPPUNIT_TEST(can_update_text_attribute);
+ CPPUNIT_TEST(can_update_text_attribute_bound_value);
+ CPPUNIT_TEST(can_update_integer_attribute_bound_value);
+ CPPUNIT_TEST(can_update_blob_attribute_bound_value);
+ CPPUNIT_TEST(will_not_insert_non_existing_attribute_on_update);
+ CPPUNIT_TEST(can_update_boolean_attribute_bound_value);
+ CPPUNIT_TEST(can_update_real_attribute_bound_value);
+ CPPUNIT_TEST(supports_transactions);
+ CPPUNIT_TEST_SUITE_END();
+public:
+ void setUp();
+ void tearDown();
+
+ void can_insert_records();
+ void can_retrieve_records();
+ void can_cascade_delete_objects_and_attributes();
+ void can_update_text_attribute();
+ void can_update_text_attribute_bound_value();
+ void can_update_integer_attribute_bound_value();
+ void can_update_blob_attribute_bound_value();
+ void will_not_insert_non_existing_attribute_on_update();
+ void can_update_boolean_attribute_bound_value();
+ void can_update_real_attribute_bound_value();
+ void supports_transactions();
+protected:
+
+private:
+};
+
+class test_a_db_with_a_connection_with_tables_with_a_second_connection_open : public test_a_db_with_a_connection_with_tables
+{
+ CPPUNIT_TEST_SUITE(test_a_db_with_a_connection_with_tables_with_a_second_connection_open);
+ CPPUNIT_TEST(handles_nested_transactions);
+ CPPUNIT_TEST(supports_transactions_with_other_connections_open);
+ CPPUNIT_TEST_SUITE_END();
+public:
+ void setUp();
+ void tearDown();
+
+ void handles_nested_transactions();
+ void supports_transactions_with_other_connections_open();
+protected:
+ DB::Connection *connection2;
+
+private:
+};
+
+#endif // !_SOFTHSM_V2_DBTESTS_H
diff --git a/SoftHSMv2/src/lib/object_store/test/DBTokenTests.cpp b/SoftHSMv2/src/lib/object_store/test/DBTokenTests.cpp
new file mode 100644
index 0000000..ab0cff1
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBTokenTests.cpp
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBTokenTests.cpp
+
+ Contains test cases to test the database token implementation
+ *****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "DBTokenTests.h"
+#include "DBToken.h"
+#include "DB.h"
+
+#include <cstdio>
+
+#ifndef HAVE_SQLITE3_H
+#error expected sqlite3 to be available
+#endif
+
+CPPUNIT_TEST_SUITE_REGISTRATION(test_a_dbtoken);
+
+static int dummy_print(const char *, va_list )
+{
+ return 0;
+}
+
+void test_a_dbtoken::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+}
+
+void test_a_dbtoken::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void test_a_dbtoken::should_be_creatable()
+{
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+
+ ObjectStoreToken* newToken = new DBToken("testdir", "newToken", label, serial);
+
+ CPPUNIT_ASSERT(newToken != NULL);
+
+ CPPUNIT_ASSERT(newToken->isValid());
+
+ delete newToken;
+}
+
+void test_a_dbtoken::should_support_pin_setting_getting()
+{
+ // Create a new token
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+
+ ObjectStoreToken* newToken = new DBToken("testdir", "newToken", label, serial);
+
+ CPPUNIT_ASSERT(newToken != NULL);
+
+ CPPUNIT_ASSERT(newToken->isValid());
+
+ // Check the flags
+ CK_ULONG flags;
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)( CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED));
+
+ // Set the SO PIN
+ ByteString soPIN = "3132333435363738"; // 12345678
+
+ CPPUNIT_ASSERT(newToken->setSOPIN(soPIN));
+
+ // Set the user PIN
+ ByteString userPIN = "31323334"; // 1234
+
+ CPPUNIT_ASSERT(newToken->setUserPIN(userPIN));
+
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ delete newToken;
+
+ // Now reopen the newly created token
+ DBToken reopenedToken("testdir","newToken");
+
+ CPPUNIT_ASSERT(reopenedToken.isValid());
+
+ // Retrieve the flags, user PIN and so PIN
+ ByteString retrievedSOPIN, retrievedUserPIN;
+
+ CPPUNIT_ASSERT(reopenedToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(reopenedToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(reopenedToken.getTokenFlags(flags));
+
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(retrievedUserPIN == userPIN);
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+}
+
+void test_a_dbtoken::should_allow_object_enumeration()
+{
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+ ByteString soPIN = "31323334"; // 1234
+ ByteString userPIN = "30303030"; // 0000
+ ByteString id[3] = { "112233445566", "AABBCCDDEEFF", "ABABABABABAB" };
+
+ {
+ // Instantiate a new token
+ ObjectStoreToken* newToken = new DBToken("testdir", "existingToken", label, serial);
+ CPPUNIT_ASSERT(newToken != NULL);
+ CPPUNIT_ASSERT(newToken->isValid());
+ CPPUNIT_ASSERT(newToken->setSOPIN(soPIN));
+ CPPUNIT_ASSERT(newToken->setUserPIN(userPIN));
+
+ // Test IDs
+ OSAttribute idAtt[3] = { id[0], id[1], id[2] };
+
+ // Create 3 objects on the token
+ OSObject* obj1 = newToken->createObject();
+ CPPUNIT_ASSERT(obj1 != NULL);
+ OSObject* obj2 = newToken->createObject();
+ CPPUNIT_ASSERT(obj2 != NULL);
+ OSObject* obj3 = newToken->createObject();
+ CPPUNIT_ASSERT(obj3 != NULL);
+
+ // Now set the IDs of the 3 objects
+ obj1->startTransaction(OSObject::ReadWrite);
+ CPPUNIT_ASSERT(obj1->setAttribute(CKA_ID, idAtt[0]));
+ obj1->commitTransaction();
+
+ obj2->startTransaction(OSObject::ReadWrite);
+ CPPUNIT_ASSERT(obj2->setAttribute(CKA_ID, idAtt[1]));
+ obj2->commitTransaction();
+
+ obj3->startTransaction(OSObject::ReadWrite);
+ CPPUNIT_ASSERT(obj3->setAttribute(CKA_ID, idAtt[2]));
+ obj3->commitTransaction();
+
+ delete newToken;
+ }
+
+ // Now open the token
+ DBToken existingToken("testdir","existingToken");
+
+ CPPUNIT_ASSERT(existingToken.isValid());
+
+ // Retrieve SO PIN, user PIN, label, serial number and flags
+ ByteString retrievedSOPIN, retrievedUserPIN, retrievedLabel, retrievedSerial;
+ CK_ULONG flags;
+
+ CPPUNIT_ASSERT(existingToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(existingToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(existingToken.getTokenLabel(retrievedLabel));
+ CPPUNIT_ASSERT(existingToken.getTokenSerial(retrievedSerial));
+ CPPUNIT_ASSERT(existingToken.getTokenFlags(flags));
+
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(retrievedUserPIN == userPIN);
+ CPPUNIT_ASSERT(retrievedLabel == label);
+ CPPUNIT_ASSERT(retrievedSerial == serial);
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ // Check that the token contains 3 objects
+ CPPUNIT_ASSERT_EQUAL(existingToken.getObjects().size(), (size_t)3);
+
+ // Check that all the tokens are presented
+ bool present[3] = { false, false, false };
+ std::set<OSObject*> objects = existingToken.getObjects();
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present[0] = true;
+ }
+ else if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[1])
+ {
+ present[1] = true;
+ }
+ else if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present[2] = true;
+ }
+ }
+
+ CPPUNIT_ASSERT(present[0]);
+ CPPUNIT_ASSERT(present[1]);
+ CPPUNIT_ASSERT(present[2]);
+}
+
+void test_a_dbtoken::should_fail_to_open_nonexistant_tokens()
+{
+ DBToken doesntExist("testdir","doesntExist");
+
+ CPPUNIT_ASSERT(!doesntExist.isValid());
+}
+
+void test_a_dbtoken::support_create_delete_objects()
+{
+ // Test IDs
+ ByteString id[5] = { "112233445566", "AABBCCDDEEFF", "ABABABABABAB", "557788991122", "005500550055" };
+ OSAttribute idAtt[5] = { id[0], id[1], id[2], id[3], id[4] };
+ ByteString label = "AABBCCDDEEFF";
+ ByteString serial = "1234567890";
+
+ // Instantiate a new token
+ ObjectStoreToken* testToken = new DBToken("testdir", "testToken", label, serial);
+ CPPUNIT_ASSERT(testToken != NULL);
+ CPPUNIT_ASSERT(testToken->isValid());
+
+ // Open the same token
+ DBToken sameToken("testdir","testToken");
+ CPPUNIT_ASSERT(sameToken.isValid());
+
+ // Create 3 objects on the token
+ OSObject* obj1 = testToken->createObject();
+ CPPUNIT_ASSERT(obj1 != NULL);
+ OSObject* obj2 = testToken->createObject();
+ CPPUNIT_ASSERT(obj2 != NULL);
+ OSObject* obj3 = testToken->createObject();
+ CPPUNIT_ASSERT(obj3 != NULL);
+
+ // Now set the IDs of the 3 objects
+ obj1->setAttribute(CKA_ID, idAtt[0]);
+ obj2->setAttribute(CKA_ID, idAtt[1]);
+ obj3->setAttribute(CKA_ID, idAtt[2]);
+
+ // Check that the token contains 3 objects
+ CPPUNIT_ASSERT_EQUAL(testToken->getObjects().size(), (size_t)3);
+
+ // Check that all three objects are distinct and present
+ std::set<OSObject*> objects = testToken->getObjects();
+ bool present1[3] = { false, false, false };
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ for (int j = 0; j < 3; j++)
+ {
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[j])
+ {
+ present1[j] = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ CPPUNIT_ASSERT(present1[j]);
+ }
+
+ // Now check that the same objects are present in the other instance of the same token
+ std::set<OSObject*> otherObjects = sameToken.getObjects();
+ CPPUNIT_ASSERT_EQUAL(otherObjects.size(), (size_t)3);
+
+ bool present2[3] = { false, false, false };
+
+ for (std::set<OSObject*>::iterator i = otherObjects.begin(); i != otherObjects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ for (int j = 0; j < 3; j++)
+ {
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[j])
+ {
+ present2[j] = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ CPPUNIT_ASSERT(present2[j]);
+ }
+
+ // Now delete the second object
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[1])
+ {
+ CPPUNIT_ASSERT(testToken->deleteObject(*i));
+ break;
+ }
+ }
+
+ // Verify that it was indeed removed
+ CPPUNIT_ASSERT_EQUAL(testToken->getObjects().size(),(size_t)2);
+
+ objects = testToken->getObjects();
+ bool present3[2] = { false, false };
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present3[0] = true;
+ }
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present3[1] = true;
+ }
+ }
+
+ for (int j = 0; j < 2; j++)
+ {
+ CPPUNIT_ASSERT(present3[j]);
+ }
+
+ // Now check the other instance
+ CPPUNIT_ASSERT_EQUAL(sameToken.getObjects().size(), (size_t)2);
+
+ otherObjects = sameToken.getObjects();
+ bool present4[2] = { false, false };
+
+ for (std::set<OSObject*>::iterator i = otherObjects.begin(); i != otherObjects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present4[0] = true;
+ }
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present4[1] = true;
+ }
+ }
+
+ for (int j = 0; j < 2; j++)
+ {
+ CPPUNIT_ASSERT(present4[j]);
+ }
+
+
+ // Release the test token
+ delete testToken;
+}
+
+void test_a_dbtoken::support_clearing_a_token()
+{
+ // Create a new token
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+
+ ObjectStoreToken* newToken = new DBToken("testdir", "newToken", label, serial);
+
+ CPPUNIT_ASSERT(newToken != NULL);
+ CPPUNIT_ASSERT(newToken->isValid());
+
+ // Check the flags
+ CK_ULONG flags;
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED));
+
+ // Set the SO PIN
+ ByteString soPIN = "3132333435363738"; // 12345678
+
+ CPPUNIT_ASSERT(newToken->setSOPIN(soPIN));
+
+ // Set the user PIN
+ ByteString userPIN = "31323334"; // 1234
+
+ CPPUNIT_ASSERT(newToken->setUserPIN(userPIN));
+
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ CPPUNIT_ASSERT(newToken->createObject() != NULL);
+
+ delete newToken;
+
+#if 1
+ // Reopen the newly created token and keep a reference around.
+ DBToken referencingToken("testdir", "newToken");
+ CPPUNIT_ASSERT(referencingToken.isValid());
+#endif
+ // Now reopen the newly created token
+ DBToken reopenedToken("testdir","newToken");
+
+ CPPUNIT_ASSERT(reopenedToken.isValid());
+
+ // Retrieve the flags, user PIN and so PIN
+ ByteString retrievedSOPIN, retrievedUserPIN;
+
+ CPPUNIT_ASSERT(reopenedToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(reopenedToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(reopenedToken.getTokenFlags(flags));
+
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(retrievedUserPIN == userPIN);
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ // Now reset the token
+ CPPUNIT_ASSERT(reopenedToken.resetToken(label));
+ CPPUNIT_ASSERT(reopenedToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(!reopenedToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(reopenedToken.getTokenFlags(flags));
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT_EQUAL(flags, (CK_ULONG)(CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED));
+ CPPUNIT_ASSERT(reopenedToken.isValid());
+
+ // Now clear the token
+ CPPUNIT_ASSERT(reopenedToken.clearToken());
+ CPPUNIT_ASSERT(!reopenedToken.isValid());
+
+ DB::LogErrorHandler eh = DB::setLogErrorHandler(dummy_print);
+
+ // Try to open it once more and make sure it has been deleted.
+ DBToken clearedToken("testdir","newToken");
+ CPPUNIT_ASSERT(!clearedToken.isValid());
+
+#if 1
+ // Verify that it is no longer possible to access the database...
+ CPPUNIT_ASSERT(!referencingToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+
+ std::set<OSObject *> objects = referencingToken.getObjects();
+ CPPUNIT_ASSERT_EQUAL(objects.size(), (size_t)0);
+
+ CPPUNIT_ASSERT(!referencingToken.isValid());
+#endif
+
+ DB::setLogErrorHandler(eh);
+}
diff --git a/SoftHSMv2/src/lib/object_store/test/DBTokenTests.h b/SoftHSMv2/src/lib/object_store/test/DBTokenTests.h
new file mode 100644
index 0000000..de25195
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DBTokenTests.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2013 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DBTokenTests.h
+
+ Contains test cases to test the database token implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DBTOKENTESTS_H
+#define _SOFTHSM_V2_DBTOKENTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+#include "DBToken.h"
+
+class test_a_dbtoken : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(test_a_dbtoken);
+ CPPUNIT_TEST(should_be_creatable);
+ CPPUNIT_TEST(should_support_pin_setting_getting);
+ CPPUNIT_TEST(should_allow_object_enumeration);
+ CPPUNIT_TEST(should_fail_to_open_nonexistant_tokens);
+ CPPUNIT_TEST(support_create_delete_objects);
+ CPPUNIT_TEST(support_clearing_a_token);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void setUp();
+ void tearDown();
+
+ void should_be_creatable();
+ void should_support_pin_setting_getting();
+ void should_allow_object_enumeration();
+ void should_fail_to_open_nonexistant_tokens();
+ void support_create_delete_objects();
+ void support_clearing_a_token();
+
+protected:
+
+private:
+};
+
+#endif // !_SOFTHSM_V2_DBTOKENTESTS_H
diff --git a/SoftHSMv2/src/lib/object_store/test/DirectoryTests.cpp b/SoftHSMv2/src/lib/object_store/test/DirectoryTests.cpp
new file mode 100644
index 0000000..2b56a5a
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DirectoryTests.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DirectoryTests.cpp
+
+ Contains test cases to test the directory implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "DirectoryTests.h"
+#include "Directory.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DirectoryTests);
+
+void DirectoryTests::setUp()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+ CPPUNIT_ASSERT(!system("mkdir testdir/anotherdir"));
+ CPPUNIT_ASSERT(!system("mkdir testdir/anotherdir2"));
+ CPPUNIT_ASSERT(!system("mkdir testdir/anotherdir3"));
+ CPPUNIT_ASSERT(!system("echo someStuff > testdir/afile"));
+ CPPUNIT_ASSERT(!system("echo someOtherStuff > testdir/anotherFile"));
+ CPPUNIT_ASSERT(!system("echo justStuff > testdir/justaFile"));
+#else
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+ CPPUNIT_ASSERT(!system("mkdir testdir\\anotherdir"));
+ CPPUNIT_ASSERT(!system("mkdir testdir\\anotherdir2"));
+ CPPUNIT_ASSERT(!system("mkdir testdir\\anotherdir3"));
+ CPPUNIT_ASSERT(!system("echo someStuff > testdir\\afile"));
+ CPPUNIT_ASSERT(!system("echo someOtherStuff > testdir\\anotherFile"));
+ CPPUNIT_ASSERT(!system("echo justStuff > testdir\\justaFile"));
+#endif
+}
+
+void DirectoryTests::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void DirectoryTests::testDirectory()
+{
+#ifndef _WIN32
+ Directory testdir("./testdir");
+#else
+ Directory testdir(".\\testdir");
+#endif
+
+ CPPUNIT_ASSERT(testdir.isValid());
+
+ std::vector<std::string> files = testdir.getFiles();
+ std::vector<std::string> subDirs = testdir.getSubDirs();
+
+ CPPUNIT_ASSERT(files.size() == 3);
+ CPPUNIT_ASSERT(subDirs.size() == 3);
+
+ CPPUNIT_ASSERT(testdir.refresh());
+
+ CPPUNIT_ASSERT(files.size() == 3);
+ CPPUNIT_ASSERT(subDirs.size() == 3);
+
+ bool fileSeen[3] = { false, false, false };
+
+ for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); i++)
+ {
+ if (!i->compare("afile"))
+ {
+ fileSeen[0] = true;
+ }
+ else if (!i->compare("anotherFile"))
+ {
+ fileSeen[1] = true;
+ }
+ else if (!i->compare("justaFile"))
+ {
+ fileSeen[2] = true;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(false);
+ }
+ }
+
+ CPPUNIT_ASSERT(fileSeen[0] && fileSeen[1] && fileSeen[2]);
+
+ bool dirSeen[3] = { false, false, false };
+
+ for (std::vector<std::string>::iterator i = subDirs.begin(); i != subDirs.end(); i++)
+ {
+ if (!i->compare("anotherdir"))
+ {
+ dirSeen[0] = true;
+ }
+ else if (!i->compare("anotherdir2"))
+ {
+ dirSeen[1] = true;
+ }
+ else if (!i->compare("anotherdir3"))
+ {
+ dirSeen[2] = true;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(false);
+ }
+ }
+
+ CPPUNIT_ASSERT(dirSeen[0] && dirSeen[1] && dirSeen[2]);
+
+ // Create a directory
+ CPPUNIT_ASSERT(testdir.mkdir("newDir"));
+
+ subDirs = testdir.getSubDirs();
+
+ bool dirSeen2[4] = { false, false, false, false };
+
+ for (std::vector<std::string>::iterator i = subDirs.begin(); i != subDirs.end(); i++)
+ {
+ if (!i->compare("anotherdir"))
+ {
+ dirSeen2[0] = true;
+ }
+ else if (!i->compare("anotherdir2"))
+ {
+ dirSeen2[1] = true;
+ }
+ else if (!i->compare("anotherdir3"))
+ {
+ dirSeen2[2] = true;
+ }
+ else if (!i->compare("newDir"))
+ {
+ dirSeen2[3] = true;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(false);
+ }
+ }
+
+ CPPUNIT_ASSERT(dirSeen2[0] && dirSeen2[1] && dirSeen2[2] && dirSeen2[3]);
+
+ // Remove a directory
+ CPPUNIT_ASSERT(testdir.rmdir("anotherdir2", true));
+
+ subDirs = testdir.getSubDirs();
+
+ bool dirSeen3[3] = { false, false, false };
+
+ for (std::vector<std::string>::iterator i = subDirs.begin(); i != subDirs.end(); i++)
+ {
+ if (!i->compare("anotherdir"))
+ {
+ dirSeen3[0] = true;
+ }
+ else if (!i->compare("newDir"))
+ {
+ dirSeen3[1] = true;
+ }
+ else if (!i->compare("anotherdir3"))
+ {
+ dirSeen3[2] = true;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(false);
+ }
+ }
+
+ CPPUNIT_ASSERT(dirSeen3[0] && dirSeen3[1] && dirSeen3[2]);
+
+ // Remove a file
+ CPPUNIT_ASSERT(testdir.remove("anotherFile"));
+
+ files = testdir.getFiles();
+
+ bool fileSeen2[2] = { false, false };
+
+ for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); i++)
+ {
+ if (!i->compare("afile"))
+ {
+ fileSeen2[0] = true;
+ }
+ else if (!i->compare("justaFile"))
+ {
+ fileSeen2[1] = true;
+ }
+ else
+ {
+ CPPUNIT_ASSERT(false);
+ }
+ }
+
+ CPPUNIT_ASSERT(fileSeen2[0] && fileSeen2[1]);
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/DirectoryTests.h b/SoftHSMv2/src/lib/object_store/test/DirectoryTests.h
new file mode 100644
index 0000000..777f1a8
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/DirectoryTests.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ DirectoryTests.h
+
+ Contains test cases to test the Directory implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_DIRECTORYTESTS_H
+#define _SOFTHSM_V2_DIRECTORYTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class DirectoryTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(DirectoryTests);
+ CPPUNIT_TEST(testDirectory);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testDirectory();
+
+ void setUp();
+ void tearDown();
+
+private:
+};
+
+#endif // !_SOFTHSM_V2_DIRECTORYTESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/FileTests.cpp b/SoftHSMv2/src/lib/object_store/test/FileTests.cpp
new file mode 100644
index 0000000..9ac0979
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/FileTests.cpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ FileTests.cpp
+
+ Contains test cases to test the file implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "FileTests.h"
+#include "File.h"
+#include "Directory.h"
+#include "CryptoFactory.h"
+#include "RNG.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(FileTests);
+
+// FIXME: all pathnames in this file are *NIX/BSD specific
+
+void FileTests::setUp()
+{
+#ifndef _WIN32
+ int rv = system("rm -rf testdir");
+#else
+ int rv = system("rmdir /s /q testdir 2> nul");
+#endif
+ (void) rv;
+
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+}
+
+void FileTests::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void FileTests::testExistNotExist()
+{
+ // Test pre-condition
+ CPPUNIT_ASSERT(!exists("nonExistentFile"));
+
+ // Attempt to open a file known not to exist
+#ifndef _WIN32
+ File doesntExist("testdir/nonExistentFile");
+#else
+ File doesntExist("testdir\\nonExistentFile");
+#endif
+
+ CPPUNIT_ASSERT(!doesntExist.isValid());
+
+ // Attempt to open a file known to exist
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("echo someStuff > testdir/existingFile"));
+#else
+ CPPUNIT_ASSERT(!system("echo someStuff > testdir\\existingFile"));
+#endif
+ CPPUNIT_ASSERT(exists("existingFile"));
+
+#ifndef _WIN32
+ File exists("testdir/existingFile");
+#else
+ File exists("testdir\\existingFile");
+#endif
+
+ CPPUNIT_ASSERT(exists.isValid());
+}
+
+void FileTests::testCreateNotCreate()
+{
+ // Test pre-condition
+ CPPUNIT_ASSERT(!exists("nonExistentFile"));
+ CPPUNIT_ASSERT(!exists("nonExistentFile2"));
+
+ // Attempt to open a file known not to exist
+#ifndef _WIN32
+ File doesntExist("testdir/nonExistentFile", true, true, false);
+#else
+ File doesntExist("testdir\\nonExistentFile", true, true, false);
+#endif
+
+ CPPUNIT_ASSERT(!doesntExist.isValid());
+ CPPUNIT_ASSERT(!exists("nonExistentFile"));
+
+ // Attempt to open a file known not to exist in create mode
+#ifndef _WIN32
+ File willBeCreated("testdir/nonExistentFile2", true, true, true);
+#else
+ File willBeCreated("testdir\\nonExistentFile2", true, true, true);
+#endif
+
+ CPPUNIT_ASSERT(willBeCreated.isValid());
+ CPPUNIT_ASSERT(exists("nonExistentFile2"));
+}
+
+void FileTests::testLockUnlock()
+{
+ // Create pre-condition
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("echo someStuff > testdir/existingFile"));
+#else
+ CPPUNIT_ASSERT(!system("echo someStuff > testdir\\existingFile"));
+#endif
+ CPPUNIT_ASSERT(exists("existingFile"));
+
+#ifndef _WIN32
+ File file1("testdir/existingFile");
+ File file2("testdir/existingFile");
+#else
+ File file1("testdir\\existingFile");
+ File file2("testdir\\existingFile");
+#endif
+
+ CPPUNIT_ASSERT(file1.lock(false));
+ CPPUNIT_ASSERT(!file1.lock(false));
+ CPPUNIT_ASSERT(file2.lock(false));
+ CPPUNIT_ASSERT(file2.unlock());
+ CPPUNIT_ASSERT(file1.unlock());
+ CPPUNIT_ASSERT(file1.lock());
+ CPPUNIT_ASSERT(file2.lock());
+ CPPUNIT_ASSERT(file2.unlock());
+ CPPUNIT_ASSERT(file1.unlock());
+}
+
+void FileTests::testWriteRead()
+{
+ // Generate some test data
+ RNG* rng = CryptoFactory::i()->getRNG();
+
+ ByteString testData1;
+
+ CPPUNIT_ASSERT(rng->generateRandom(testData1, 187));
+
+ // More test data
+ std::string testString = "This is a test of the File class";
+ std::set<CK_MECHANISM_TYPE> testSet;
+ testSet.insert(CKM_RSA_PKCS);
+ testSet.insert(CKM_SHA256_RSA_PKCS);
+
+ // Create a file for writing
+ {
+#ifndef _WIN32
+ File newFile("testdir/newFile", false, true);
+#else
+ File newFile("testdir\\newFile", false, true);
+#endif
+
+ CPPUNIT_ASSERT(newFile.isValid());
+
+ // Write two booleans into the file
+ CPPUNIT_ASSERT(newFile.writeBool(true));
+ CPPUNIT_ASSERT(newFile.writeBool(false));
+
+ // Write an ulong into the file
+ CPPUNIT_ASSERT(newFile.writeULong(0x12345678));
+
+ // Write a ByteString into the file
+ CPPUNIT_ASSERT(newFile.writeByteString(testData1));
+
+ // Write a string into the file
+ CPPUNIT_ASSERT(newFile.writeString(testString));
+
+ // Write a set into the file
+ CPPUNIT_ASSERT(newFile.writeMechanismTypeSet(testSet));
+ }
+
+ CPPUNIT_ASSERT(exists("newFile"));
+
+ // Read the created file back
+ {
+#ifndef _WIN32
+ File newFile("testdir/newFile");
+#else
+ File newFile("testdir\\newFile");
+#endif
+
+ CPPUNIT_ASSERT(newFile.isValid());
+
+ // Read back the two booleans
+ bool b1, b2;
+
+ CPPUNIT_ASSERT(newFile.readBool(b1) && newFile.readBool(b2));
+ CPPUNIT_ASSERT(b1 && !b2);
+
+ // Read back the ulong
+ unsigned long ulongValue;
+
+ CPPUNIT_ASSERT(newFile.readULong(ulongValue));
+ CPPUNIT_ASSERT(ulongValue == 0x12345678);
+
+ // Read back the byte string
+ ByteString bsValue;
+
+ CPPUNIT_ASSERT(newFile.readByteString(bsValue));
+ CPPUNIT_ASSERT(bsValue == testData1);
+
+ // Read back the string value
+ std::string stringVal;
+
+ CPPUNIT_ASSERT(newFile.readString(stringVal));
+ CPPUNIT_ASSERT(!testString.compare(stringVal));
+
+ // Read back the set value
+ std::set<CK_MECHANISM_TYPE> setVal;
+
+ CPPUNIT_ASSERT(newFile.readMechanismTypeSet(setVal));
+ CPPUNIT_ASSERT(setVal == testSet);
+
+ // Check for EOF
+ CPPUNIT_ASSERT(!newFile.readBool(b1));
+ CPPUNIT_ASSERT(newFile.isEOF());
+ }
+}
+
+void FileTests::testSeek()
+{
+ ByteString t1 = "112233445566778899"; // 9 long
+ ByteString t2 = "AABBCCDDEEFFAABBCCDDEEFF"; // 12 long
+
+ {
+ // Create the test file
+#ifndef _WIN32
+ File testFile("testdir/testFile", false, true, true);
+#else
+ File testFile("testdir\\testFile", false, true, true);
+#endif
+
+ CPPUNIT_ASSERT(testFile.isValid());
+
+ // Write the test data to the test file
+ CPPUNIT_ASSERT(testFile.writeByteString(t1) && testFile.writeByteString(t2));
+ }
+
+ // Open the test file for reading
+#ifndef _WIN32
+ File testFile("testdir/testFile");
+#else
+ File testFile("testdir\\testFile");
+#endif
+
+ CPPUNIT_ASSERT(testFile.isValid());
+
+ // First, read back the test data
+ ByteString tr1, tr2;
+
+ CPPUNIT_ASSERT(testFile.readByteString(tr1) && testFile.readByteString(tr2));
+ CPPUNIT_ASSERT(tr1 == t1);
+ CPPUNIT_ASSERT(tr2 == t2);
+
+ // Seek to the length field of the second byte string
+ CPPUNIT_ASSERT(testFile.seek(8+9));
+
+ // Read back the size as an ulong value
+ unsigned long value;
+ unsigned long expectedValue = (unsigned long)0x1122334455667788ULL;
+
+ CPPUNIT_ASSERT(testFile.readULong(value));
+ CPPUNIT_ASSERT(value == 12);
+
+ // Seek to the start of the first byte string's data
+ CPPUNIT_ASSERT(testFile.seek(8));
+
+ // Read back the ulong value stored there
+ CPPUNIT_ASSERT(testFile.readULong(value));
+
+ CPPUNIT_ASSERT(value == expectedValue);
+
+ // Seek to the start of second byte string
+ CPPUNIT_ASSERT(testFile.seek(8+9));
+
+ // Read it
+ ByteString trr2;
+
+ CPPUNIT_ASSERT(testFile.readByteString(trr2));
+ CPPUNIT_ASSERT(trr2 == t2);
+
+ // Rewind the file
+ CPPUNIT_ASSERT(testFile.rewind());
+
+ // Read back both byte strings
+ ByteString trrr1, trrr2;
+
+ CPPUNIT_ASSERT(testFile.readByteString(trrr1) && testFile.readByteString(trrr2));
+ CPPUNIT_ASSERT(trrr1 == t1);
+ CPPUNIT_ASSERT(trrr2 == t2);
+}
+
+bool FileTests::exists(std::string name)
+{
+#ifndef _WIN32
+ Directory dir("./testdir");
+#else
+ Directory dir(".\\testdir");
+#endif
+
+
+ CPPUNIT_ASSERT(dir.isValid());
+
+ std::vector<std::string> files = dir.getFiles();
+
+ for (std::vector<std::string>::iterator i = files.begin(); i != files.end(); i++)
+ {
+ if (!i->compare(name))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/FileTests.h b/SoftHSMv2/src/lib/object_store/test/FileTests.h
new file mode 100644
index 0000000..0b87f26
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/FileTests.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ FileTests.h
+
+ Contains test cases to test the File implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_FILETESTS_H
+#define _SOFTHSM_V2_FILETESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class FileTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(FileTests);
+ CPPUNIT_TEST(testExistNotExist);
+ CPPUNIT_TEST(testCreateNotCreate);
+ CPPUNIT_TEST(testLockUnlock);
+ CPPUNIT_TEST(testWriteRead);
+ CPPUNIT_TEST(testSeek);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testExistNotExist();
+ void testCreateNotCreate();
+ void testLockUnlock();
+ void testWriteRead();
+ void testSeek();
+
+ void setUp();
+ void tearDown();
+
+private:
+ bool exists(std::string path);
+};
+
+#endif // !_SOFTHSM_V2_FILETESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/Makefile.am b/SoftHSMv2/src/lib/object_store/test/Makefile.am
new file mode 100644
index 0000000..71de7dd
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/Makefile.am
@@ -0,0 +1,39 @@
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
+
+AM_CPPFLAGS = -I$(srcdir)/.. \
+ -I$(srcdir)/../.. \
+ -I$(srcdir)/../../common \
+ -I$(srcdir)/../../crypto \
+ -I$(srcdir)/../../data_mgr \
+ -I$(srcdir)/../../pkcs11 \
+ -I$(srcdir)/../../session_mgr \
+ -I$(srcdir)/../../slot_mgr \
+ @CPPUNIT_CFLAGS@ \
+ @CRYPTO_INCLUDES@
+
+check_PROGRAMS = objstoretest
+
+objstoretest_SOURCES = objstoretest.cpp \
+ DirectoryTests.cpp \
+ UUIDTests.cpp \
+ FileTests.cpp \
+ ObjectFileTests.cpp \
+ OSTokenTests.cpp \
+ ObjectStoreTests.cpp \
+ SessionObjectTests.cpp \
+ SessionObjectStoreTests.cpp
+
+if BUILD_OBJECTSTORE_BACKEND_DB
+objstoretest_SOURCES += DBTests.cpp \
+ DBObjectTests.cpp \
+ DBTokenTests.cpp \
+ DBObjectStoreTests.cpp
+endif
+
+objstoretest_LDADD = ../../libsofthsm_convarch.la
+
+objstoretest_LDFLAGS = @CRYPTO_LIBS@ @CPPUNIT_LIBS@ -no-install -pthread
+
+TESTS = objstoretest
+
+EXTRA_DIST = $(srcdir)/*.h
diff --git a/SoftHSMv2/src/lib/object_store/test/OSTokenTests.cpp b/SoftHSMv2/src/lib/object_store/test/OSTokenTests.cpp
new file mode 100644
index 0000000..5afd583
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/OSTokenTests.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSTokenTests.cpp
+
+ Contains test cases to test the object file implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "OSTokenTests.h"
+#include "OSToken.h"
+#include "ObjectFile.h"
+#include "File.h"
+#include "Directory.h"
+#include "OSAttribute.h"
+#include "OSAttributes.h"
+#include "cryptoki.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(OSTokenTests);
+
+// FIXME: all pathnames in this file are *NIX/BSD specific
+
+void OSTokenTests::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+}
+
+void OSTokenTests::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void OSTokenTests::testNewToken()
+{
+ // Create a new token
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+
+#ifndef _WIN32
+ OSToken* newToken = OSToken::createToken("./testdir", "newToken", label, serial);
+#else
+ OSToken* newToken = OSToken::createToken(".\\testdir", "newToken", label, serial);
+#endif
+
+ CPPUNIT_ASSERT(newToken != NULL);
+
+ // Check the flags
+ CK_ULONG flags;
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED));
+
+ // Set the SO PIN
+ ByteString soPIN = "3132333435363738"; // 12345678
+
+ CPPUNIT_ASSERT(newToken->setSOPIN(soPIN));
+
+ // Set the user PIN
+ ByteString userPIN = "31323334"; // 1234
+
+ CPPUNIT_ASSERT(newToken->setUserPIN(userPIN));
+
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ delete newToken;
+
+ // Now reopen the newly created token
+#ifndef _WIN32
+ OSToken reopenedToken("./testdir/newToken");
+#else
+ OSToken reopenedToken(".\\testdir\\newToken");
+#endif
+
+ CPPUNIT_ASSERT(reopenedToken.isValid());
+
+ // Retrieve the flags, user PIN and so PIN
+ ByteString retrievedSOPIN, retrievedUserPIN;
+
+ CPPUNIT_ASSERT(reopenedToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(reopenedToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(reopenedToken.getTokenFlags(flags));
+
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(retrievedUserPIN == userPIN);
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+}
+
+void OSTokenTests::testExistingToken()
+{
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+ ByteString soPIN = "31323334"; // 1234
+ ByteString userPIN = "30303030"; // 0000
+ ByteString id1 = "ABCDEF";
+ ByteString id2 = "FEDCBA";
+ ByteString id3 = "AABBCC";
+
+ {
+ // Create the token dir
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("mkdir testdir/existingToken"));
+#else
+ CPPUNIT_ASSERT(!system("mkdir testdir\\existingToken"));
+#endif
+
+ // Create the token object
+#ifndef _WIN32
+ ObjectFile tokenObject(NULL, "./testdir/existingToken/token.object", "./testdir/existingToken/token.lock", true);
+#else
+ ObjectFile tokenObject(NULL, ".\\testdir\\existingToken\\token.object", ".\\testdir\\existingToken\\token.lock", true);
+#endif
+
+ OSAttribute labelAtt(label);
+ CPPUNIT_ASSERT(tokenObject.setAttribute(CKA_OS_TOKENLABEL, labelAtt));
+ OSAttribute serialAtt(serial);
+ CPPUNIT_ASSERT(tokenObject.setAttribute(CKA_OS_TOKENSERIAL, serialAtt));
+ OSAttribute soPINAtt(soPIN);
+ CPPUNIT_ASSERT(tokenObject.setAttribute(CKA_OS_SOPIN, soPINAtt));
+ OSAttribute userPINAtt(userPIN);
+ CPPUNIT_ASSERT(tokenObject.setAttribute(CKA_OS_USERPIN, userPINAtt));
+ CK_ULONG flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED;
+ OSAttribute flagsAtt(flags);
+ CPPUNIT_ASSERT(tokenObject.setAttribute(CKA_OS_TOKENFLAGS, flagsAtt));
+
+ // Create 3 objects
+#ifndef _WIN32
+ ObjectFile obj1(NULL, "./testdir/existingToken/1.object", "./testdir/existingToken/1.lock", true);
+ ObjectFile obj2(NULL, "./testdir/existingToken/2.object", "./testdir/existingToken/2.lock", true);
+ ObjectFile obj3(NULL, "./testdir/existingToken/3.object", "./testdir/existingToken/3.lock", true);
+#else
+ ObjectFile obj1(NULL, ".\\testdir\\existingToken\\1.object", ".\\testdir\\existingToken\\1.lock", true);
+ ObjectFile obj2(NULL, ".\\testdir\\existingToken\\2.object", ".\\testdir\\existingToken\\2.lock", true);
+ ObjectFile obj3(NULL, ".\\testdir\\existingToken\\3.object", ".\\testdir\\existingToken\\3.lock", true);
+#endif
+
+ OSAttribute id1Att(id1);
+ OSAttribute id2Att(id2);
+ OSAttribute id3Att(id3);
+
+ CPPUNIT_ASSERT(obj1.setAttribute(CKA_ID, id1));
+ CPPUNIT_ASSERT(obj2.setAttribute(CKA_ID, id2));
+ CPPUNIT_ASSERT(obj3.setAttribute(CKA_ID, id3));
+ }
+
+ // Now open the token
+#ifndef _WIN32
+ OSToken existingToken("./testdir/existingToken");
+#else
+ OSToken existingToken(".\\testdir\\existingToken");
+#endif
+
+
+ CPPUNIT_ASSERT(existingToken.isValid());
+
+ // Retrieve SO PIN, user PIN, label, serial number and flags
+ ByteString retrievedSOPIN, retrievedUserPIN, retrievedLabel, retrievedSerial;
+ CK_ULONG flags;
+
+ CPPUNIT_ASSERT(existingToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(existingToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(existingToken.getTokenLabel(retrievedLabel));
+ CPPUNIT_ASSERT(existingToken.getTokenSerial(retrievedSerial));
+ CPPUNIT_ASSERT(existingToken.getTokenFlags(flags));
+
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(retrievedUserPIN == userPIN);
+ CPPUNIT_ASSERT(retrievedLabel == label);
+ CPPUNIT_ASSERT(retrievedSerial == serial);
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ // Check that the token contains 3 objects
+ CPPUNIT_ASSERT(existingToken.getObjects().size() == 3);
+
+ // Check that all the tokens are presented
+ bool present[3] = { false, false, false };
+ std::set<OSObject*> objects = existingToken.getObjects();
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id1)
+ {
+ present[0] = true;
+ }
+ else if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id2)
+ {
+ present[1] = true;
+ }
+ else if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id3)
+ {
+ present[2] = true;
+ }
+ }
+
+ CPPUNIT_ASSERT(present[0] == true);
+ CPPUNIT_ASSERT(present[1] == true);
+ CPPUNIT_ASSERT(present[2] == true);
+}
+
+void OSTokenTests::testNonExistentToken()
+{
+#ifndef _WIN32
+ OSToken doesntExist("./testdir/doesntExist");
+#else
+ OSToken doesntExist(".\\testdir\\doesntExist");
+#endif
+
+ CPPUNIT_ASSERT(!doesntExist.isValid());
+}
+
+void OSTokenTests::testCreateDeleteObjects()
+{
+ // Test IDs
+ ByteString id[5] = { "112233445566", "AABBCCDDEEFF", "ABABABABABAB", "557788991122", "005500550055" };
+ OSAttribute idAtt[5] = { id[0], id[1], id[2], id[3], id[4] };
+ ByteString label = "AABBCCDDEEFF";
+ ByteString serial = "1234567890";
+
+ // Instantiate a new token
+#ifndef _WIN32
+ OSToken* testToken = OSToken::createToken("./testdir", "testToken", label, serial);
+#else
+ OSToken* testToken = OSToken::createToken(".\\testdir", "testToken", label, serial);
+#endif
+
+ CPPUNIT_ASSERT(testToken != NULL);
+ CPPUNIT_ASSERT(testToken->isValid());
+
+ // Open the same token
+#ifndef _WIN32
+ OSToken sameToken("./testdir/testToken");
+#else
+ OSToken sameToken(".\\testdir\\testToken");
+#endif
+
+ CPPUNIT_ASSERT(sameToken.isValid());
+
+ // Create 3 objects on the token
+ OSObject* obj1 = testToken->createObject();
+ CPPUNIT_ASSERT(obj1 != NULL);
+ OSObject* obj2 = testToken->createObject();
+ CPPUNIT_ASSERT(obj2 != NULL);
+ OSObject* obj3 = testToken->createObject();
+ CPPUNIT_ASSERT(obj3 != NULL);
+
+ // Now set the IDs of the 3 objects
+ obj1->setAttribute(CKA_ID, idAtt[0]);
+ obj2->setAttribute(CKA_ID, idAtt[1]);
+ obj3->setAttribute(CKA_ID, idAtt[2]);
+
+ // Check that the token contains 3 objects
+ CPPUNIT_ASSERT(testToken->getObjects().size() == 3);
+
+ // Check that all three objects are distinct and present
+ std::set<OSObject*> objects = testToken->getObjects();
+ bool present1[3] = { false, false, false };
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ for (int j = 0; j < 3; j++)
+ {
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[j])
+ {
+ present1[j] = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ CPPUNIT_ASSERT(present1[j] == true);
+ }
+
+ // Now check that the same objects are present in the other instance of the same token
+ std::set<OSObject*> otherObjects = sameToken.getObjects();
+ CPPUNIT_ASSERT(otherObjects.size() == 3);
+
+ bool present2[3] = { false, false, false };
+
+ for (std::set<OSObject*>::iterator i = otherObjects.begin(); i != otherObjects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ for (int j = 0; j < 3; j++)
+ {
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[j])
+ {
+ present2[j] = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ CPPUNIT_ASSERT(present2[j] == true);
+ }
+
+ // Now delete the second object
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[1])
+ {
+ CPPUNIT_ASSERT(testToken->deleteObject(*i));
+ break;
+ }
+ }
+
+ // Verify that it was indeed removed
+ CPPUNIT_ASSERT(testToken->getObjects().size() == 2);
+
+ objects = testToken->getObjects();
+ bool present3[2] = { false, false };
+
+ for (std::set<OSObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present3[0] = true;
+ }
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present3[1] = true;
+ }
+ }
+
+ for (int j = 0; j < 2; j++)
+ {
+ CPPUNIT_ASSERT(present3[j] == true);
+ }
+
+ // Now check the other instance
+ CPPUNIT_ASSERT(sameToken.getObjects().size() == 2);
+
+ otherObjects = sameToken.getObjects();
+ bool present4[2] = { false, false };
+
+ for (std::set<OSObject*>::iterator i = otherObjects.begin(); i != otherObjects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present4[0] = true;
+ }
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present4[1] = true;
+ }
+ }
+
+ for (int j = 0; j < 2; j++)
+ {
+ CPPUNIT_ASSERT(present4[j] == true);
+ }
+
+
+ // Release the test token
+ delete testToken;
+}
+
+void OSTokenTests::testClearToken()
+{
+ // Create a new token
+ ByteString label = "40414243"; // ABCD
+ ByteString serial = "0102030405060708";
+
+#ifndef _WIN32
+ OSToken* newToken = OSToken::createToken("./testdir", "newToken", label, serial);
+#else
+ OSToken* newToken = OSToken::createToken(".\\testdir", "newToken", label, serial);
+#endif
+
+ CPPUNIT_ASSERT(newToken != NULL);
+
+ // Check the flags
+ CK_ULONG flags;
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_SO_PIN_LOCKED | CKF_SO_PIN_TO_BE_CHANGED));
+
+ // Set the SO PIN
+ ByteString soPIN = "3132333435363738"; // 12345678
+
+ CPPUNIT_ASSERT(newToken->setSOPIN(soPIN));
+
+ // Set the user PIN
+ ByteString userPIN = "31323334"; // 1234
+
+ CPPUNIT_ASSERT(newToken->setUserPIN(userPIN));
+
+ CPPUNIT_ASSERT(newToken->getTokenFlags(flags));
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ delete newToken;
+
+ // Now reopen the newly created token
+#ifndef _WIN32
+ OSToken reopenedToken("./testdir/newToken");
+#else
+ OSToken reopenedToken(".\\testdir\\newToken");
+#endif
+
+ CPPUNIT_ASSERT(reopenedToken.isValid());
+
+ // Retrieve the flags, user PIN and so PIN
+ ByteString retrievedSOPIN, retrievedUserPIN;
+
+ CPPUNIT_ASSERT(reopenedToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(reopenedToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(reopenedToken.getTokenFlags(flags));
+
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(retrievedUserPIN == userPIN);
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED));
+
+ // Now reset the token
+ CPPUNIT_ASSERT(reopenedToken.resetToken(label));
+ CPPUNIT_ASSERT(reopenedToken.getSOPIN(retrievedSOPIN));
+ CPPUNIT_ASSERT(!reopenedToken.getUserPIN(retrievedUserPIN));
+ CPPUNIT_ASSERT(reopenedToken.getTokenFlags(flags));
+ CPPUNIT_ASSERT(retrievedSOPIN == soPIN);
+ CPPUNIT_ASSERT(flags == (CKF_RNG | CKF_LOGIN_REQUIRED | CKF_RESTORE_KEY_NOT_NEEDED | CKF_TOKEN_INITIALIZED));
+ CPPUNIT_ASSERT(reopenedToken.isValid());
+
+ // Now clear the token
+ CPPUNIT_ASSERT(reopenedToken.clearToken());
+ CPPUNIT_ASSERT(!reopenedToken.isValid());
+
+ // Try to open it once more
+#ifndef _WIN32
+ OSToken clearedToken("./testdir/newToken");
+#else
+ OSToken clearedToken(".\\testdir\\newToken");
+#endif
+
+ CPPUNIT_ASSERT(!clearedToken.isValid());
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/OSTokenTests.h b/SoftHSMv2/src/lib/object_store/test/OSTokenTests.h
new file mode 100644
index 0000000..1155fcf
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/OSTokenTests.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ OSTokenTests.h
+
+ Contains test cases to test the object file implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OSTOKENTESTS_H
+#define _SOFTHSM_V2_OSTOKENTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class OSTokenTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(OSTokenTests);
+ CPPUNIT_TEST(testNewToken);
+ CPPUNIT_TEST(testExistingToken);
+ CPPUNIT_TEST(testNonExistentToken);
+ CPPUNIT_TEST(testCreateDeleteObjects);
+ CPPUNIT_TEST(testClearToken);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testNewToken();
+ void testExistingToken();
+ void testNonExistentToken();
+ void testCreateDeleteObjects();
+ void testClearToken();
+
+ void setUp();
+ void tearDown();
+};
+
+#endif // !_SOFTHSM_V2_OSTOKENTESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/ObjectFileTests.cpp b/SoftHSMv2/src/lib/object_store/test/ObjectFileTests.cpp
new file mode 100644
index 0000000..9f0f5bd
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/ObjectFileTests.cpp
@@ -0,0 +1,911 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectObjectFileTests.cpp
+
+ Contains test cases to test the object file implementation
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "ObjectFileTests.h"
+#include "ObjectFile.h"
+#include "File.h"
+#include "Directory.h"
+#include "OSAttribute.h"
+#include "CryptoFactory.h"
+#include "RNG.h"
+#include "cryptoki.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ObjectFileTests);
+
+// FIXME: all pathnames in this file are *NIX/BSD specific
+
+void ObjectFileTests::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+}
+
+void ObjectFileTests::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void ObjectFileTests::testBoolAttr()
+{
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ bool value2 = false;
+ bool value3 = true;
+ bool value4 = true;
+ bool value5 = false;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SENSITIVE, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_EXTRACTABLE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_NEVER_EXTRACTABLE, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr5));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SENSITIVE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_EXTRACTABLE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_NEVER_EXTRACTABLE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SIGN));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SENSITIVE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_EXTRACTABLE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_NEVER_EXTRACTABLE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SENSITIVE).getBooleanValue() == false);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_EXTRACTABLE).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_NEVER_EXTRACTABLE).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue() == false);
+
+ bool value6 = true;
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VERIFY, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VERIFY).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VERIFY).getBooleanValue() == value6);
+ CPPUNIT_ASSERT(testObject.getBooleanValue(CKA_VERIFY, false) == value6);
+ }
+}
+
+void ObjectFileTests::testULongAttr()
+{
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ unsigned long value1 = 0x12345678;
+ unsigned long value2 = 0x87654321;
+ unsigned long value3 = 0x01010101;
+ unsigned long value4 = 0x10101010;
+ unsigned long value5 = 0xABCDEF;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_MODULUS_BITS, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_AUTH_PIN_FLAGS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBPRIME_BITS, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_KEY_TYPE, attr5));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_MODULUS_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_AUTH_PIN_FLAGS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBPRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_KEY_TYPE));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_AUTH_PIN_FLAGS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBPRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_KEY_TYPE).isUnsignedLongAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS_BITS).getUnsignedLongValue() == 0x12345678);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_AUTH_PIN_FLAGS).getUnsignedLongValue() == 0x01010101);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBPRIME_BITS).getUnsignedLongValue() == 0x10101010);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_KEY_TYPE).getUnsignedLongValue() == 0xABCDEF);
+
+ unsigned long value6 = 0x90909090;
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_CLASS, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_CLASS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_CLASS).getUnsignedLongValue() == value6);
+ CPPUNIT_ASSERT(testObject.getUnsignedLongValue(CKA_CLASS, 0x0) == value6);
+ }
+}
+
+void ObjectFileTests::testByteStrAttr()
+{
+ ByteString value1 = "010203040506070809";
+ ByteString value2 = "ABABABABABABABABABABABABABABABABAB";
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ ByteString value4 = "98A7E5D798A7E5D798A7E5D798A7E5D798A7E5D798A7E5D7";
+ ByteString value5 = "ABCDABCDABCDABCDABCDABCDABCDABCD";
+
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_MODULUS, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_COEFFICIENT, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PUBLIC_EXPONENT, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBJECT, attr5));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_MODULUS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_COEFFICIENT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PUBLIC_EXPONENT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBJECT));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_COEFFICIENT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PUBLIC_EXPONENT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS).getByteStringValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_COEFFICIENT).getByteStringValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PUBLIC_EXPONENT).getByteStringValue() == value4);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).getByteStringValue() == value5);
+
+ ByteString value6 = "909090908080808080807070707070FF";
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ISSUER, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ISSUER).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getByteStringValue(CKA_ISSUER) == value6);
+ }
+}
+
+void ObjectFileTests::testMechTypeSetAttr()
+{
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ std::set<CK_MECHANISM_TYPE> set;
+ set.insert(CKM_SHA256);
+ set.insert(CKM_SHA512);
+ OSAttribute attr(set);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ std::set<CK_MECHANISM_TYPE> retrieved =
+ testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue();
+
+ CPPUNIT_ASSERT(retrieved.size() == 2);
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA256) != retrieved.end());
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA384) == retrieved.end());
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA512) != retrieved.end());
+ }
+}
+
+void ObjectFileTests::testAttrMapAttr()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> mattr;
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_TOKEN, attr1));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_PRIME_BITS, attr2));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_VALUE, attr3));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_ALLOWED_MECHANISMS, attr4));
+ OSAttribute attra(mattr);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_WRAP_TEMPLATE, attra));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_WRAP_TEMPLATE));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_UNWRAP_TEMPLATE));
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> mattrb =
+ testObject.getAttribute(CKA_WRAP_TEMPLATE).getAttributeMapValue();
+ CPPUNIT_ASSERT(mattrb.size() == 4);
+ CPPUNIT_ASSERT(mattrb.find(CKA_TOKEN) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(mattrb.find(CKA_PRIME_BITS) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(mattrb.find(CKA_VALUE) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(mattrb.find(CKA_ALLOWED_MECHANISMS) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+ }
+}
+
+void ObjectFileTests::testMixedAttr()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+ }
+}
+
+void ObjectFileTests::testDoubleAttr()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ ByteString value3a = "466487346943785684957634";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+ std::set<CK_MECHANISM_TYPE> value4a;
+ value4a.insert(CKM_SHA384);
+ value4a.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+
+ bool value1 = false;
+ unsigned long value2 = 0x76767676;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3a);
+ OSAttribute attr4(value4a);
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+
+ // Check the attributes
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+ }
+
+ // Now re-read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ bool value1 = false;
+ unsigned long value2 = 0x76767676;
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+ }
+}
+
+void ObjectFileTests::testRefresh()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ ByteString value3a = "466487346943785684957634";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+ std::set<CK_MECHANISM_TYPE> value4a;
+ value4a.insert(CKM_SHA384);
+ value4a.insert(CKM_SHA512);
+
+ // Create the test object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+ }
+
+ // Now read back the object
+ {
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+
+ bool value1 = false;
+ unsigned long value2 = 0x76767676;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3a);
+ OSAttribute attr4(value4a);
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+
+ // Check the attributes
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Open the object a second time
+#ifndef _WIN32
+ ObjectFile testObject2(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject2(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject2.isValid());
+
+ // Check the attributes on the second instance
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Add an attribute on the second object
+ ByteString id = "0102010201020102010201020102010201020102";
+
+ OSAttribute attr5(id);
+
+ CPPUNIT_ASSERT(testObject2.setAttribute(CKA_ID, attr5));
+
+ // Check the attribute
+ CPPUNIT_ASSERT(testObject2.attributeExists(CKA_ID));
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ID).getByteStringValue() == id);
+
+ // Now check that the first instance also knows about it
+ CPPUNIT_ASSERT(testObject.isValid());
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ID));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ID).getByteStringValue() == id);
+
+ // Now change another attribute
+ unsigned long value2a = 0x89898989;
+
+ OSAttribute attr2a(value2a);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2a));
+
+ // Check the attribute
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+
+ // Now check that the second instance also knows about the change
+ CPPUNIT_ASSERT(testObject2.isValid());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ }
+}
+
+void ObjectFileTests::testCorruptFile()
+{
+#ifndef _WIN32
+ FILE* stream = fopen("testdir/test.object", "w");
+#else
+ FILE* stream = fopen("testdir\\test.object", "wb");
+#endif
+ RNG* rng = CryptoFactory::i()->getRNG();
+ ByteString randomData;
+
+ CPPUNIT_ASSERT(stream != NULL);
+ CPPUNIT_ASSERT(rng->generateRandom(randomData, 312));
+ CPPUNIT_ASSERT(fwrite(randomData.const_byte_str(), 1, randomData.size(), stream) == randomData.size());
+ CPPUNIT_ASSERT(!fclose(stream));
+
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(!testObject.isValid());
+}
+
+void ObjectFileTests::testTransactions()
+{
+ // Create test object instance
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ std::set<CK_MECHANISM_TYPE> value4;
+ value4.insert(CKM_SHA256);
+ value4.insert(CKM_SHA512);
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+
+ // Create secondary instance for the same object
+#ifndef _WIN32
+ ObjectFile testObject2(NULL, "testdir/test.object", "testdir/test.lock");
+#else
+ ObjectFile testObject2(NULL, "testdir\\test.object", "testdir\\test.lock");
+#endif
+
+ CPPUNIT_ASSERT(testObject2.isValid());
+
+ // Check that it has the same attributes
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+
+ // New values
+ bool value1a = false;
+ unsigned long value2a = 0x12345678;
+ ByteString value3a = "ABABABABABABABABABABABABABABAB";
+ std::set<CK_MECHANISM_TYPE> value4a;
+ value4a.insert(CKM_SHA384);
+ value4a.insert(CKM_SHA512);
+
+ OSAttribute attr1a(value1a);
+ OSAttribute attr2a(value2a);
+ OSAttribute attr3a(value3a);
+ OSAttribute attr4a(value4a);
+
+ // Start transaction on object
+ CPPUNIT_ASSERT(testObject.startTransaction(ObjectFile::ReadWrite));
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4a));
+
+ // Verify that the attributes were set
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Verify that they are unchanged on the other instance
+ CPPUNIT_ASSERT(testObject2.isValid());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+
+ // Commit the transaction
+ CPPUNIT_ASSERT(testObject.commitTransaction());
+
+ // Verify that they have now changed on the other instance
+ CPPUNIT_ASSERT(testObject2.isValid());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Start transaction on object
+ CPPUNIT_ASSERT(testObject.startTransaction(ObjectFile::ReadWrite));
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr4));
+
+ // Verify that the attributes were set
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4);
+
+ // Verify that they are unchanged on the other instance
+ CPPUNIT_ASSERT(testObject2.isValid());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ // Abort the transaction
+ CPPUNIT_ASSERT(testObject.abortTransaction());
+
+ // Verify that they are unchanged on both instances
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+
+ CPPUNIT_ASSERT(testObject2.isValid());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_VALUE).getByteStringValue() == value3a);
+ CPPUNIT_ASSERT(testObject2.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue() == value4a);
+}
+
+void ObjectFileTests::testDestroyObjectFails()
+{
+ // Create test object instance
+#ifndef _WIN32
+ ObjectFile testObject(NULL, "testdir/test.object", "testdir/test.lock", true);
+#else
+ ObjectFile testObject(NULL, "testdir\\test.object", "testdir\\test.lock", true);
+#endif
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSObject* testIF = (OSObject*) &testObject;
+
+ CPPUNIT_ASSERT(!testIF->destroyObject());
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/ObjectFileTests.h b/SoftHSMv2/src/lib/object_store/test/ObjectFileTests.h
new file mode 100644
index 0000000..8342a64
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/ObjectFileTests.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectFileTests.h
+
+ Contains test cases to test the object file implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OBJECTFILETESTS_H
+#define _SOFTHSM_V2_OBJECTFILETESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class ObjectFileTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ObjectFileTests);
+ CPPUNIT_TEST(testBoolAttr);
+ CPPUNIT_TEST(testULongAttr);
+ CPPUNIT_TEST(testByteStrAttr);
+ CPPUNIT_TEST(testMechTypeSetAttr);
+ CPPUNIT_TEST(testAttrMapAttr);
+ CPPUNIT_TEST(testMixedAttr);
+ CPPUNIT_TEST(testDoubleAttr);
+ CPPUNIT_TEST(testRefresh);
+ CPPUNIT_TEST(testCorruptFile);
+ CPPUNIT_TEST(testTransactions);
+ CPPUNIT_TEST(testDestroyObjectFails);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testBoolAttr();
+ void testULongAttr();
+ void testByteStrAttr();
+ void testMechTypeSetAttr();
+ void testAttrMapAttr();
+ void testMixedAttr();
+ void testDoubleAttr();
+ void testRefresh();
+ void testCorruptFile();
+ void testTransactions();
+ void testDestroyObjectFails();
+
+ void setUp();
+ void tearDown();
+};
+
+#endif // !_SOFTHSM_V2_OBJECTFILETESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.cpp b/SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.cpp
new file mode 100644
index 0000000..0cad27b
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectStoreTests.cpp
+
+ Contains test cases to test the object store implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "ObjectStoreTests.h"
+#include "ObjectStore.h"
+#include "File.h"
+#include "Directory.h"
+#include "OSAttribute.h"
+#include "OSAttributes.h"
+#include "cryptoki.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ObjectStoreTests);
+
+// FIXME: all pathnames in this file are *NIX/BSD specific
+
+void ObjectStoreTests::setUp()
+{
+ CPPUNIT_ASSERT(!system("mkdir testdir"));
+}
+
+void ObjectStoreTests::tearDown()
+{
+#ifndef _WIN32
+ CPPUNIT_ASSERT(!system("rm -rf testdir"));
+#else
+ CPPUNIT_ASSERT(!system("rmdir /s /q testdir 2> nul"));
+#endif
+}
+
+void ObjectStoreTests::testEmptyStore()
+{
+ // Create the store for an empty dir
+#ifndef _WIN32
+ ObjectStore store("./testdir");
+#else
+ ObjectStore store(".\\testdir");
+#endif
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 0);
+}
+
+void ObjectStoreTests::testNewTokens()
+{
+ ByteString label1 = "DEADC0FFEE";
+ ByteString label2 = "DEADBEEF";
+
+ {
+ // Create an empty store
+#ifndef _WIN32
+ ObjectStore store("./testdir");
+#else
+ ObjectStore store(".\\testdir");
+#endif
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 0);
+
+ // Create a new token
+ ObjectStoreToken* token1 = store.newToken(label1);
+
+ CPPUNIT_ASSERT(token1 != NULL);
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 1);
+
+ // Create another new token
+ ObjectStoreToken* token2 = store.newToken(label2);
+
+ CPPUNIT_ASSERT(token2 != NULL);
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 2);
+ }
+
+ // Now reopen that same store
+#ifndef _WIN32
+ ObjectStore store("./testdir");
+#else
+ ObjectStore store(".\\testdir");
+#endif
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 2);
+
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* token1 = store.getToken(0);
+ ObjectStoreToken* token2 = store.getToken(1);
+
+ ByteString retrieveLabel1, retrieveLabel2;
+
+ CPPUNIT_ASSERT(token1->getTokenLabel(retrieveLabel1));
+ CPPUNIT_ASSERT(token2->getTokenLabel(retrieveLabel2));
+
+ CPPUNIT_ASSERT((retrieveLabel1 == label1) || (retrieveLabel2 == label1));
+ CPPUNIT_ASSERT((retrieveLabel2 == label1) || (retrieveLabel2 == label2));
+
+ ByteString retrieveSerial1, retrieveSerial2;
+
+ CPPUNIT_ASSERT(token1->getTokenSerial(retrieveSerial1));
+ CPPUNIT_ASSERT(token2->getTokenSerial(retrieveSerial2));
+
+ CPPUNIT_ASSERT(retrieveSerial1 != retrieveSerial2);
+}
+
+void ObjectStoreTests::testExistingTokens()
+{
+ // Create some tokens
+ ByteString label1 = "DEADC0FFEE";
+ ByteString label2 = "DEADBEEF";
+ ByteString serial1 = "0011001100110011";
+ ByteString serial2 = "2233223322332233";
+
+#ifndef _WIN32
+ ObjectStoreToken* token1 = ObjectStoreToken::createToken("./testdir", "token1", label1, serial1);
+ ObjectStoreToken* token2 = ObjectStoreToken::createToken("./testdir", "token2", label2, serial2);
+#else
+ ObjectStoreToken* token1 = ObjectStoreToken::createToken(".\\testdir", "token1", label1, serial1);
+ ObjectStoreToken* token2 = ObjectStoreToken::createToken(".\\testdir", "token2", label2, serial2);
+#endif
+
+ CPPUNIT_ASSERT((token1 != NULL) && (token2 != NULL));
+
+ delete token1;
+ delete token2;
+
+ // Now associate a store with the test directory
+#ifndef _WIN32
+ ObjectStore store("./testdir");
+#else
+ ObjectStore store(".\\testdir");
+#endif
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 2);
+
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* retrieveToken1 = store.getToken(0);
+ ObjectStoreToken* retrieveToken2 = store.getToken(1);
+
+ ByteString retrieveLabel1, retrieveLabel2, retrieveSerial1, retrieveSerial2;
+
+ CPPUNIT_ASSERT(retrieveToken1 != NULL);
+ CPPUNIT_ASSERT(retrieveToken2 != NULL);
+
+ CPPUNIT_ASSERT(retrieveToken1->getTokenLabel(retrieveLabel1));
+ CPPUNIT_ASSERT(retrieveToken2->getTokenLabel(retrieveLabel2));
+ CPPUNIT_ASSERT(retrieveToken1->getTokenSerial(retrieveSerial1));
+ CPPUNIT_ASSERT(retrieveToken2->getTokenSerial(retrieveSerial2));
+
+ CPPUNIT_ASSERT((retrieveLabel1 == label1) || (retrieveLabel1 == label2));
+ CPPUNIT_ASSERT((retrieveLabel2 == label1) || (retrieveLabel2 == label2));
+ CPPUNIT_ASSERT(retrieveLabel1 != retrieveLabel2);
+ CPPUNIT_ASSERT((retrieveSerial1 == serial1) || (retrieveSerial1 == serial2));
+ CPPUNIT_ASSERT((retrieveSerial2 == serial1) || (retrieveSerial2 == serial2));
+ CPPUNIT_ASSERT(retrieveSerial1 != retrieveSerial2);
+}
+
+void ObjectStoreTests::testDeleteToken()
+{
+ // Create some tokens
+ ByteString label1 = "DEADC0FFEE";
+ ByteString label2 = "DEADBEEF";
+ ByteString serial1 = "0011001100110011";
+ ByteString serial2 = "2233223322332233";
+
+#ifndef _WIN32
+ ObjectStoreToken* token1 = ObjectStoreToken::createToken("./testdir", "token1", label1, serial1);
+ ObjectStoreToken* token2 = ObjectStoreToken::createToken("./testdir", "token2", label2, serial2);
+#else
+ ObjectStoreToken* token1 = ObjectStoreToken::createToken(".\\testdir", "token1", label1, serial1);
+ ObjectStoreToken* token2 = ObjectStoreToken::createToken(".\\testdir", "token2", label2, serial2);
+#endif
+
+ CPPUNIT_ASSERT((token1 != NULL) && (token2 != NULL));
+
+ delete token1;
+ delete token2;
+
+ // Now associate a store with the test directory
+#ifndef _WIN32
+ ObjectStore store("./testdir");
+#else
+ ObjectStore store(".\\testdir");
+#endif
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 2);
+
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* retrieveToken1 = store.getToken(0);
+ ObjectStoreToken* retrieveToken2 = store.getToken(1);
+
+ ByteString retrieveLabel1, retrieveLabel2, retrieveSerial1, retrieveSerial2;
+
+ CPPUNIT_ASSERT(retrieveToken1 != NULL);
+ CPPUNIT_ASSERT(retrieveToken2 != NULL);
+
+ CPPUNIT_ASSERT(retrieveToken1->getTokenLabel(retrieveLabel1));
+ CPPUNIT_ASSERT(retrieveToken2->getTokenLabel(retrieveLabel2));
+ CPPUNIT_ASSERT(retrieveToken1->getTokenSerial(retrieveSerial1));
+ CPPUNIT_ASSERT(retrieveToken2->getTokenSerial(retrieveSerial2));
+
+ CPPUNIT_ASSERT((retrieveLabel1 == label1) || (retrieveLabel1 == label2));
+ CPPUNIT_ASSERT((retrieveLabel2 == label1) || (retrieveLabel2 == label2));
+ CPPUNIT_ASSERT(retrieveLabel1 != retrieveLabel2);
+ CPPUNIT_ASSERT((retrieveSerial1 == serial1) || (retrieveSerial1 == serial2));
+ CPPUNIT_ASSERT((retrieveSerial2 == serial1) || (retrieveSerial2 == serial2));
+ CPPUNIT_ASSERT(retrieveSerial1 != retrieveSerial2);
+
+ // Now, delete token #1
+ CPPUNIT_ASSERT(store.destroyToken(retrieveToken1));
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 1);
+
+ ObjectStoreToken* retrieveToken_ = store.getToken(0);
+
+ ByteString retrieveLabel_,retrieveSerial_;
+
+ CPPUNIT_ASSERT(retrieveToken_->getTokenLabel(retrieveLabel_));
+ CPPUNIT_ASSERT(retrieveToken_->getTokenSerial(retrieveSerial_));
+
+ CPPUNIT_ASSERT(((retrieveLabel_ == label1) && (retrieveSerial_ == serial1)) ||
+ ((retrieveLabel_ == label2) && (retrieveSerial_ == serial2)));
+
+ // Now add a new token
+ ByteString label3 = "DEADC0FFEEBEEF";
+
+ // Create a new token
+ ObjectStoreToken* tokenNew = store.newToken(label3);
+
+ CPPUNIT_ASSERT(tokenNew != NULL);
+
+ CPPUNIT_ASSERT(store.getTokenCount() == 2);
+
+ // Retrieve both tokens and check that both are present
+ ObjectStoreToken* retrieveToken1_ = store.getToken(0);
+ ObjectStoreToken* retrieveToken2_ = store.getToken(1);
+
+ CPPUNIT_ASSERT(retrieveToken1_ != NULL);
+ CPPUNIT_ASSERT(retrieveToken2_ != NULL);
+
+ CPPUNIT_ASSERT(retrieveToken1_->getTokenLabel(retrieveLabel1));
+ CPPUNIT_ASSERT(retrieveToken2_->getTokenLabel(retrieveLabel2));
+ CPPUNIT_ASSERT(retrieveToken1_->getTokenSerial(retrieveSerial1));
+ CPPUNIT_ASSERT(retrieveToken2_->getTokenSerial(retrieveSerial2));
+
+ CPPUNIT_ASSERT((retrieveLabel1 == label3) || (retrieveLabel2 == label3));
+ CPPUNIT_ASSERT(((retrieveLabel1 == label1) && (retrieveLabel2 != label2)) ||
+ ((retrieveLabel1 == label2) && (retrieveLabel2 != label1)));
+ CPPUNIT_ASSERT(retrieveLabel1 != retrieveLabel2);
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.h b/SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.h
new file mode 100644
index 0000000..3f03c5a
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/ObjectStoreTests.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ ObjectStoreTests.h
+
+ Contains test cases to test the object store implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_OBJECTSTORETESTS_H
+#define _SOFTHSM_V2_OBJECTSTORETESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class ObjectStoreTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(ObjectStoreTests);
+ CPPUNIT_TEST(testEmptyStore);
+ CPPUNIT_TEST(testNewTokens);
+ CPPUNIT_TEST(testExistingTokens);
+ CPPUNIT_TEST(testDeleteToken);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testEmptyStore();
+ void testNewTokens();
+ void testExistingTokens();
+ void testDeleteToken();
+
+ void setUp();
+ void tearDown();
+};
+
+#endif // !_SOFTHSM_V2_OBJECTSTORETESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.cpp b/SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.cpp
new file mode 100644
index 0000000..2c41eb0
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.cpp
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObjectStoreTests.cpp
+
+ Contains test cases to test the session object store implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <memory>
+#include "SessionObjectStoreTests.h"
+#include "SessionObjectStore.h"
+#include "SessionObject.h"
+#include "OSAttribute.h"
+#include "OSAttributes.h"
+#include "cryptoki.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SessionObjectStoreTests);
+
+void SessionObjectStoreTests::setUp()
+{
+}
+
+void SessionObjectStoreTests::tearDown()
+{
+}
+
+void SessionObjectStoreTests::testCreateDeleteObjects()
+{
+ // Test IDs
+ ByteString id[5] = { "112233445566", "AABBCCDDEEFF", "ABABABABABAB", "557788991122", "005500550055" };
+ OSAttribute idAtt[5] = { id[0], id[1], id[2], id[3], id[4] };
+ ByteString label = "AABBCCDDEEFF";
+ ByteString serial = "1234567890";
+
+ // Get access to the session object store
+ SessionObjectStore* testStore = new SessionObjectStore();
+
+ // Create 3 objects in the store
+ SessionObject* obj1 = testStore->createObject(1, 1);
+ CPPUNIT_ASSERT(obj1 != NULL);
+ SessionObject* obj2 = testStore->createObject(1, 1);
+ CPPUNIT_ASSERT(obj2 != NULL);
+ SessionObject* obj3 = testStore->createObject(1, 1);
+ CPPUNIT_ASSERT(obj3 != NULL);
+
+ // Now set the IDs of the 3 objects
+ obj1->setAttribute(CKA_ID, idAtt[0]);
+ obj2->setAttribute(CKA_ID, idAtt[1]);
+ obj3->setAttribute(CKA_ID, idAtt[2]);
+
+ // Check that the store contains 3 objects
+ CPPUNIT_ASSERT(testStore->getObjects().size() == 3);
+
+ // Check that all three objects are distinct and present
+ std::set<SessionObject*> objects = testStore->getObjects();
+ bool present1[3] = { false, false, false };
+
+ for (std::set<SessionObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ for (int j = 0; j < 3; j++)
+ {
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[j])
+ {
+ present1[j] = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ CPPUNIT_ASSERT(present1[j] == true);
+ }
+
+ // Now delete the second object
+ for (std::set<SessionObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[1])
+ {
+ CPPUNIT_ASSERT(testStore->deleteObject(*i));
+ break;
+ }
+ }
+
+ // Verify that it was indeed removed
+ CPPUNIT_ASSERT(testStore->getObjects().size() == 2);
+
+ objects = testStore->getObjects();
+ bool present3[2] = { false, false };
+
+ for (std::set<SessionObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present3[0] = true;
+ }
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present3[1] = true;
+ }
+ }
+
+ for (int j = 0; j < 2; j++)
+ {
+ CPPUNIT_ASSERT(present3[j] == true);
+ }
+
+ delete testStore;
+}
+
+void SessionObjectStoreTests::testMultiSession()
+{
+ // Get access to the store
+ SessionObjectStore* store = new SessionObjectStore();
+
+ // Check that the store is empty
+ CPPUNIT_ASSERT(store->getObjects().size() == 0);
+
+ // Test IDs
+ ByteString id[5] = { "112233445566", "AABBCCDDEEFF", "ABABABABABAB", "557788991122", "005500550055" };
+ OSAttribute idAtt[5] = { id[0], id[1], id[2], id[3], id[4] };
+
+ // Create 3 objects in the store for three different sessions
+ SessionObject* obj1 = store->createObject(1, 1);
+ CPPUNIT_ASSERT(obj1 != NULL);
+ SessionObject* obj2 = store->createObject(1, 2);
+ CPPUNIT_ASSERT(obj2 != NULL);
+ SessionObject* obj3 = store->createObject(1, 3);
+ CPPUNIT_ASSERT(obj3 != NULL);
+
+ // Now set the IDs of the 3 objects
+ obj1->setAttribute(CKA_ID, idAtt[0]);
+ obj2->setAttribute(CKA_ID, idAtt[1]);
+ obj3->setAttribute(CKA_ID, idAtt[2]);
+
+ // Check that the store contains 3 objects
+ CPPUNIT_ASSERT(store->getObjects().size() == 3);
+
+ // Check that all three objects are distinct and present
+ std::set<SessionObject*> objects = store->getObjects();
+ bool present1[3] = { false, false, false };
+
+ for (std::set<SessionObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ for (int j = 0; j < 3; j++)
+ {
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[j])
+ {
+ present1[j] = true;
+ }
+ }
+ }
+
+ for (int j = 0; j < 3; j++)
+ {
+ CPPUNIT_ASSERT(present1[j] == true);
+ }
+
+ // Now indicate that the second session has been closed
+ store->sessionClosed(2);
+
+ // Verify that it was indeed removed
+ CPPUNIT_ASSERT(store->getObjects().size() == 2);
+
+ objects = store->getObjects();
+ bool present3[2] = { false, false };
+
+ for (std::set<SessionObject*>::iterator i = objects.begin(); i != objects.end(); i++)
+ {
+ ByteString retrievedId;
+
+ CPPUNIT_ASSERT((*i)->isValid());
+ CPPUNIT_ASSERT((*i)->attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT((*i)->getAttribute(CKA_ID).isByteStringAttribute());
+
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[0])
+ {
+ present3[0] = true;
+ }
+ if ((*i)->getAttribute(CKA_ID).getByteStringValue() == id[2])
+ {
+ present3[1] = true;
+ }
+ }
+
+ for (int j = 0; j < 2; j++)
+ {
+ CPPUNIT_ASSERT(present3[j] == true);
+ }
+
+ // Create two more objects for session 7
+ SessionObject* obj4 = store->createObject(1, 7);
+ CPPUNIT_ASSERT(obj4 != NULL);
+ SessionObject* obj5 = store->createObject(1, 7);
+ CPPUNIT_ASSERT(obj5 != NULL);
+
+ CPPUNIT_ASSERT(store->getObjects().size() == 4);
+
+ // Close session 1
+ store->sessionClosed(1);
+
+ CPPUNIT_ASSERT(store->getObjects().size() == 3);
+
+ objects = store->getObjects();
+
+ CPPUNIT_ASSERT(objects.find(obj1) == objects.end());
+ CPPUNIT_ASSERT(objects.find(obj2) == objects.end());
+ CPPUNIT_ASSERT(objects.find(obj3) != objects.end());
+ CPPUNIT_ASSERT(objects.find(obj4) != objects.end());
+ CPPUNIT_ASSERT(objects.find(obj5) != objects.end());
+
+ CPPUNIT_ASSERT(!obj1->isValid());
+ CPPUNIT_ASSERT(!obj2->isValid());
+ CPPUNIT_ASSERT(obj3->isValid());
+ CPPUNIT_ASSERT(obj4->isValid());
+ CPPUNIT_ASSERT(obj5->isValid());
+
+ // Close session 7
+ store->sessionClosed(7);
+
+ CPPUNIT_ASSERT(store->getObjects().size() == 1);
+
+ objects = store->getObjects();
+
+ CPPUNIT_ASSERT(objects.find(obj1) == objects.end());
+ CPPUNIT_ASSERT(objects.find(obj2) == objects.end());
+ CPPUNIT_ASSERT(objects.find(obj3) != objects.end());
+ CPPUNIT_ASSERT(objects.find(obj4) == objects.end());
+ CPPUNIT_ASSERT(objects.find(obj5) == objects.end());
+
+ CPPUNIT_ASSERT(!obj1->isValid());
+ CPPUNIT_ASSERT(!obj2->isValid());
+ CPPUNIT_ASSERT(obj3->isValid());
+ CPPUNIT_ASSERT(!obj4->isValid());
+ CPPUNIT_ASSERT(!obj5->isValid());
+
+ delete store;
+}
+
+void SessionObjectStoreTests::testWipeStore()
+{
+ // Get access to the store
+ SessionObjectStore* store = new SessionObjectStore();
+
+ // Check that the store is empty
+ CPPUNIT_ASSERT(store->getObjects().size() == 0);
+
+ // Create 3 objects in the store for three different sessions
+ SessionObject* obj1 = store->createObject(1, 1);
+ CPPUNIT_ASSERT(obj1 != NULL);
+ SessionObject* obj2 = store->createObject(1, 2);
+ CPPUNIT_ASSERT(obj2 != NULL);
+ SessionObject* obj3 = store->createObject(1, 3);
+ CPPUNIT_ASSERT(obj3 != NULL);
+
+ // Wipe the store
+ store->clearStore();
+
+ // Check that the store is empty
+ CPPUNIT_ASSERT(store->getObjects().size() == 0);
+
+ delete store;
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.h b/SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.h
new file mode 100644
index 0000000..374eeaa
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/SessionObjectStoreTests.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObjectStoreTests.h
+
+ Contains test cases to test the session object store implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_SESSIONOBJECTSTORETESTS_H
+#define _SOFTHSM_V2_SESSIONOBJECTSTORETESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class SessionObjectStoreTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SessionObjectStoreTests);
+ CPPUNIT_TEST(testCreateDeleteObjects);
+ CPPUNIT_TEST(testMultiSession);
+ CPPUNIT_TEST(testWipeStore);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testCreateDeleteObjects();
+ void testMultiSession();
+ void testWipeStore();
+
+ void setUp();
+ void tearDown();
+};
+
+#endif // !_SOFTHSM_V2_SESSIONOBJECTSTORETESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/SessionObjectTests.cpp b/SoftHSMv2/src/lib/object_store/test/SessionObjectTests.cpp
new file mode 100644
index 0000000..6183ec6
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/SessionObjectTests.cpp
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObjectTests.cpp
+
+ Contains test cases to test the session object implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "SessionObjectTests.h"
+#include "SessionObject.h"
+#include "File.h"
+#include "Directory.h"
+#include "OSAttribute.h"
+#include "cryptoki.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SessionObjectTests);
+
+void SessionObjectTests::setUp()
+{
+}
+
+void SessionObjectTests::tearDown()
+{
+}
+
+void SessionObjectTests::testBoolAttr()
+{
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ bool value2 = false;
+ bool value3 = true;
+ bool value4 = true;
+ bool value5 = false;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SENSITIVE, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_EXTRACTABLE, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_NEVER_EXTRACTABLE, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SIGN, attr5));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SENSITIVE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_EXTRACTABLE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_NEVER_EXTRACTABLE));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SIGN));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SENSITIVE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_EXTRACTABLE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_NEVER_EXTRACTABLE).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).isBooleanAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SENSITIVE).getBooleanValue() == false);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_EXTRACTABLE).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_NEVER_EXTRACTABLE).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SIGN).getBooleanValue() == false);
+
+ bool value6 = true;
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VERIFY, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VERIFY).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VERIFY).getBooleanValue() == value6);
+ CPPUNIT_ASSERT(testObject.getBooleanValue(CKA_VERIFY, false) == value6);
+}
+
+void SessionObjectTests::testULongAttr()
+{
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ unsigned long value1 = 0x12345678;
+ unsigned long value2 = 0x87654321;
+ unsigned long value3 = 0x01010101;
+ unsigned long value4 = 0x10101010;
+ unsigned long value5 = 0xABCDEF;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_MODULUS_BITS, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_AUTH_PIN_FLAGS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBPRIME_BITS, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_KEY_TYPE, attr5));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_MODULUS_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_AUTH_PIN_FLAGS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBPRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_KEY_TYPE));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_AUTH_PIN_FLAGS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBPRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_KEY_TYPE).isUnsignedLongAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS_BITS).getUnsignedLongValue() == 0x12345678);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_AUTH_PIN_FLAGS).getUnsignedLongValue() == 0x01010101);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBPRIME_BITS).getUnsignedLongValue() == 0x10101010);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_KEY_TYPE).getUnsignedLongValue() == 0xABCDEF);
+
+ unsigned long value6 = 0x90909090;
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_CLASS, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_CLASS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_CLASS).getUnsignedLongValue() == value6);
+ CPPUNIT_ASSERT(testObject.getUnsignedLongValue(CKA_CLASS, 0x0) == value6);
+}
+
+void SessionObjectTests::testByteStrAttr()
+{
+ ByteString value1 = "010203040506070809";
+ ByteString value2 = "ABABABABABABABABABABABABABABABABAB";
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ ByteString value4 = "98A7E5D798A7E5D798A7E5D798A7E5D798A7E5D798A7E5D7";
+ ByteString value5 = "ABCDABCDABCDABCDABCDABCDABCDABCD";
+
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+ OSAttribute attr4(value4);
+ OSAttribute attr5(value5);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_MODULUS, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_COEFFICIENT, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PUBLIC_EXPONENT, attr4));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_SUBJECT, attr5));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_MODULUS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_COEFFICIENT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PUBLIC_EXPONENT));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_SUBJECT));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_COEFFICIENT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PUBLIC_EXPONENT).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_MODULUS).getByteStringValue() == value1);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_COEFFICIENT).getByteStringValue() == value2);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getByteStringValue() == value3);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PUBLIC_EXPONENT).getByteStringValue() == value4);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_SUBJECT).getByteStringValue() == value5);
+
+ ByteString value6 = "909090908080808080807070707070FF";
+ OSAttribute attr6(value6);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ISSUER, attr6));
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ISSUER).isByteStringAttribute());
+ CPPUNIT_ASSERT(testObject.getByteStringValue(CKA_ISSUER) == value6);
+}
+
+void SessionObjectTests::testMechTypeSetAttr()
+{
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ std::set<CK_MECHANISM_TYPE> set;
+ set.insert(CKM_SHA256);
+ set.insert(CKM_SHA512);
+
+ OSAttribute attr(set);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_ALLOWED_MECHANISMS, attr));
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_ALLOWED_MECHANISMS));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_ALLOWED_MECHANISMS).isMechanismTypeSetAttribute());
+
+ std::set<CK_MECHANISM_TYPE> retrieved =
+ testObject.getAttribute(CKA_ALLOWED_MECHANISMS).getMechanismTypeSetValue();
+
+ CPPUNIT_ASSERT(retrieved.size() == 2);
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA256) != retrieved.end());
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA384) == retrieved.end());
+ CPPUNIT_ASSERT(retrieved.find(CKM_SHA512) != retrieved.end());
+}
+
+void SessionObjectTests::testAttrMapAttr()
+{
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> mattr;
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_TOKEN, attr1));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_PRIME_BITS, attr2));
+ mattr.insert(std::pair<CK_ATTRIBUTE_TYPE,OSAttribute> (CKA_VALUE_BITS, attr3));
+ OSAttribute attra(mattr);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_WRAP_TEMPLATE, attra));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_WRAP_TEMPLATE));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_UNWRAP_TEMPLATE));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_WRAP_TEMPLATE).isAttributeMapAttribute());
+
+ std::map<CK_ATTRIBUTE_TYPE,OSAttribute> mattrb =
+ testObject.getAttribute(CKA_WRAP_TEMPLATE).getAttributeMapValue();
+ CPPUNIT_ASSERT(mattrb.size() == 3);
+ CPPUNIT_ASSERT(mattrb.find(CKA_TOKEN) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(mattrb.find(CKA_PRIME_BITS) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(mattrb.find(CKA_VALUE_BITS) != mattrb.end());
+ CPPUNIT_ASSERT(mattrb.at(CKA_VALUE_BITS).isByteStringAttribute());
+ CPPUNIT_ASSERT(mattrb.at(CKA_VALUE_BITS).getByteStringValue() == value3);
+
+}
+
+void SessionObjectTests::testMixedAttr()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE_BITS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getByteStringValue() == value3);
+}
+
+void SessionObjectTests::testDoubleAttr()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+ ByteString value3a = "466487346943785684957634";
+
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE_BITS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getByteStringValue() == value3);
+
+ bool value1a = false;
+ unsigned long value2a = 0x76767676;
+
+ OSAttribute attr1a(value1a);
+ OSAttribute attr2a(value2a);
+ OSAttribute attr3a(value3a);
+
+ // Change the attributes
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2a));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3a));
+
+ // Check the attributes
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == value1a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == value2a);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getByteStringValue() == value3a);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+}
+
+void SessionObjectTests::testCloseSession()
+{
+ ByteString value3 = "BDEBDBEDBBDBEBDEBE792759537328";
+
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ bool value1 = true;
+ unsigned long value2 = 0x87654321;
+
+ OSAttribute attr1(value1);
+ OSAttribute attr2(value2);
+ OSAttribute attr3(value3);
+
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_TOKEN, attr1));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_PRIME_BITS, attr2));
+ CPPUNIT_ASSERT(testObject.setAttribute(CKA_VALUE_BITS, attr3));
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(testObject.attributeExists(CKA_VALUE_BITS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_ID));
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).isBooleanAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).isUnsignedLongAttribute());
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).isByteStringAttribute());
+
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_TOKEN).getBooleanValue() == true);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_PRIME_BITS).getUnsignedLongValue() == 0x87654321);
+ CPPUNIT_ASSERT(testObject.getAttribute(CKA_VALUE_BITS).getByteStringValue() == value3);
+
+ // Now close the session
+ testObject.removeOnSessionClose(1);
+
+ CPPUNIT_ASSERT(!testObject.isValid());
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_TOKEN));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_PRIME_BITS));
+ CPPUNIT_ASSERT(!testObject.attributeExists(CKA_VALUE_BITS));
+}
+
+void SessionObjectTests::testDestroyObjectFails()
+{
+ // Create test object instance
+ SessionObject testObject(NULL, 1, 1);
+
+ CPPUNIT_ASSERT(testObject.isValid());
+
+ OSObject* testIF = (OSObject*) &testObject;
+
+ CPPUNIT_ASSERT(!testIF->destroyObject());
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/SessionObjectTests.h b/SoftHSMv2/src/lib/object_store/test/SessionObjectTests.h
new file mode 100644
index 0000000..76fa02e
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/SessionObjectTests.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ SessionObjectTests.h
+
+ Contains test cases to test the session object implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_SESSIONOBJECTTESTS_H
+#define _SOFTHSM_V2_SESSIONOBJECTTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class SessionObjectTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(SessionObjectTests);
+ CPPUNIT_TEST(testBoolAttr);
+ CPPUNIT_TEST(testULongAttr);
+ CPPUNIT_TEST(testByteStrAttr);
+ CPPUNIT_TEST(testMechTypeSetAttr);
+ CPPUNIT_TEST(testAttrMapAttr);
+ CPPUNIT_TEST(testMixedAttr);
+ CPPUNIT_TEST(testDoubleAttr);
+ CPPUNIT_TEST(testCloseSession);
+ CPPUNIT_TEST(testDestroyObjectFails);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testBoolAttr();
+ void testULongAttr();
+ void testByteStrAttr();
+ void testMechTypeSetAttr();
+ void testAttrMapAttr();
+ void testMixedAttr();
+ void testDoubleAttr();
+ void testCloseSession();
+ void testDestroyObjectFails();
+
+ void setUp();
+ void tearDown();
+};
+
+#endif // !_SOFTHSM_V2_SESSIONOBJECTTESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/UUIDTests.cpp b/SoftHSMv2/src/lib/object_store/test/UUIDTests.cpp
new file mode 100644
index 0000000..84a49d2
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/UUIDTests.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ UUIDTests.cpp
+
+ Contains test cases to test the UUID implementation
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include "UUIDTests.h"
+#include "UUID.h"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(UUIDTests);
+
+void UUIDTests::setUp()
+{
+}
+
+void UUIDTests::tearDown()
+{
+}
+
+void UUIDTests::testUUID()
+{
+ std::string uuid1 = UUID::newUUID();
+ std::string uuid2 = UUID::newUUID();
+ std::string uuid3 = UUID::newUUID();
+
+ CPPUNIT_ASSERT((uuid1.size() == 36) && (uuid2.size() == 36) && (uuid3.size() == 36));
+
+ CPPUNIT_ASSERT(uuid1.compare(uuid2));
+ CPPUNIT_ASSERT(uuid1.compare(uuid3));
+ CPPUNIT_ASSERT(uuid2.compare(uuid3));
+}
+
diff --git a/SoftHSMv2/src/lib/object_store/test/UUIDTests.h b/SoftHSMv2/src/lib/object_store/test/UUIDTests.h
new file mode 100644
index 0000000..374d509
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/UUIDTests.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ UUIDTests.h
+
+ Contains test cases to test the UUID implementation
+ *****************************************************************************/
+
+#ifndef _SOFTHSM_V2_UUIDTESTS_H
+#define _SOFTHSM_V2_UUIDTESTS_H
+
+#include <cppunit/extensions/HelperMacros.h>
+
+class UUIDTests : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(UUIDTests);
+ CPPUNIT_TEST(testUUID);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ void testUUID();
+
+ void setUp();
+ void tearDown();
+
+private:
+};
+
+#endif // !_SOFTHSM_V2_UUIDTESTS_H
+
diff --git a/SoftHSMv2/src/lib/object_store/test/objstoretest.cpp b/SoftHSMv2/src/lib/object_store/test/objstoretest.cpp
new file mode 100644
index 0000000..7e4b854
--- /dev/null
+++ b/SoftHSMv2/src/lib/object_store/test/objstoretest.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2010 SURFnet bv
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*****************************************************************************
+ objstoretest.cpp
+
+ The main test executor for tests on the object store in SoftHSM v2
+ *****************************************************************************/
+
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <cppunit/ui/text/TestRunner.h>
+#include <cppunit/TestResult.h>
+#include <cppunit/TestResultCollector.h>
+#include <cppunit/XmlOutputter.h>
+#include <fstream>
+
+#include "config.h"
+#include "MutexFactory.h"
+#include "SecureMemoryRegistry.h"
+
+#if defined(WITH_OPENSSL)
+#include "OSSLCryptoFactory.h"
+#else
+#include "BotanCryptoFactory.h"
+#endif
+
+// Initialise the one-and-only instance
+#ifdef HAVE_CXX11
+
+std::unique_ptr<MutexFactory> MutexFactory::instance(nullptr);
+std::unique_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(nullptr);
+#if defined(WITH_OPENSSL)
+std::unique_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(nullptr);
+#else
+std::unique_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(nullptr);
+#endif
+
+#else
+
+std::auto_ptr<MutexFactory> MutexFactory::instance(NULL);
+std::auto_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(NULL);
+#if defined(WITH_OPENSSL)
+std::auto_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(NULL);
+#else
+std::auto_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(NULL);
+#endif
+
+#endif
+
+int main(int /*argc*/, char** /*argv*/)
+{
+ CppUnit::TestResult controller;
+ CppUnit::TestResultCollector result;
+ CppUnit::TextUi::TestRunner runner;
+ controller.addListener(&result);
+ CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
+
+ runner.addTest(registry.makeTest());
+ runner.run(controller);
+
+ std::ofstream xmlFileOut("test-results.xml");
+ CppUnit::XmlOutputter xmlOut(&result, xmlFileOut);
+ xmlOut.write();
+
+ CryptoFactory::reset();
+
+ return result.wasSuccessful() ? 0 : 1;
+}