diff options
author | 2018-02-08 08:34:03 -0800 | |
---|---|---|
committer | 2018-02-08 09:14:52 -0800 | |
commit | 0c89b3ccba7c9b7332ab67ae1936aff51ca62367 (patch) | |
tree | 70c1b1d160d4c6d0a83395ca9a87c1264d0d3439 /SoftHSMv2/src/lib/object_store | |
parent | 945613b4db4e07f75d2bc7463db580ddfaa700fd (diff) |
Initial sshsm project structure
Issue-ID: AAF-94
Change-Id: I5e82fff418e7567b161acf9b98013a9b85ffc5b4
Signed-off-by: NingSun <ning.sun@intel.com>
Diffstat (limited to 'SoftHSMv2/src/lib/object_store')
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 ®istry = 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; +} |