This library provides a simple and nice C++ wrapper around these libraries, so that programmers can concentrate on functionality. It offers general support for PCSC-lite, OpenSSL, PKCS#11, plus specific functionality for the SuisseID.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
15 KiB
372 lines
15 KiB
/*! @file |
|
|
|
@id $Id$ |
|
*/ |
|
// 1 2 3 4 5 6 7 8 |
|
// 45678901234567890123456789012345678901234567890123456789012345678901234567890 |
|
|
|
#include <cryptoki.hxx> |
|
|
|
#include <sstream> |
|
#include <memory> |
|
#ifndef WIN32 |
|
#include <dlfcn.h> |
|
#define CK_PTR * |
|
typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR; |
|
typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR; |
|
typedef CK_RV (*CK_C_GetFunctionList) |
|
(CK_FUNCTION_LIST_PTR_PTR ppFunctionList); |
|
#else |
|
#include <windows.h> |
|
#undef ERROR |
|
#endif |
|
|
|
namespace cryptoki { |
|
|
|
bool Init::functionList(const std::string& library) { |
|
CRYPTOKI_LOG("try to load: "<<library); |
|
#ifndef WIN32 |
|
void* lib(dlopen(library.c_str(), RTLD_NOW)); |
|
#else |
|
HINSTANCE lib(LoadLibrary(library.c_str())); |
|
#endif |
|
if (!lib) throw exception("open of library failed: "+library); |
|
CRYPTOKI_LOG("loaded: "<<library); |
|
#ifndef WIN32 |
|
CK_C_GetFunctionList fn |
|
((CK_C_GetFunctionList)dlsym(lib, "C_GetFunctionList")); |
|
#else |
|
CK_C_GetFunctionList fn |
|
((CK_C_GetFunctionList)GetProcAddress(lib, "C_GetFunctionList")); |
|
#endif |
|
if (!fn) |
|
throw exception("required library symbol C_GetFunctionList not found in " |
|
+library); |
|
CRYPTOKI_LOG("Got C_GetFunctionList, now call it"); |
|
//! calls @c C_GetFunctionList |
|
return check(fn(&_fn), CRYPTOKI_FN_LOG("C_GetFunctionList")); |
|
} |
|
|
|
bool Init::check(CK_RV result, const std::string& context) { |
|
CRYPTOKI_LOG("log"); |
|
_res = result; |
|
if (_exc && !*this) |
|
if (context.size()) |
|
throw access_error(context+": "+error()); |
|
else |
|
throw access_error(error()); |
|
return _res==CKR_OK; |
|
} |
|
|
|
std::string Init::error(CK_RV res) { |
|
CRYPTOKI_LOG("log"); |
|
switch (res) { |
|
case CKR_OK: return "CKR_OK"; |
|
case CKR_CANCEL: return "CKR_CANCEL"; |
|
case CKR_HOST_MEMORY: return "CKR_HOST_MEMORY"; |
|
case CKR_SLOT_ID_INVALID: return "CKR_SLOT_ID_INVALID"; |
|
case CKR_GENERAL_ERROR: return "CKR_GENERAL_ERROR"; |
|
case CKR_FUNCTION_FAILED: return "CKR_FUNCTION_FAILED"; |
|
case CKR_ARGUMENTS_BAD: return "CKR_ARGUMENTS_BAD"; |
|
case CKR_NO_EVENT: return "CKR_NO_EVENT"; |
|
case CKR_NEED_TO_CREATE_THREADS: return "CKR_NEED_TO_CREATE_THREADS"; |
|
case CKR_CANT_LOCK: return "CKR_CANT_LOCK"; |
|
case CKR_ATTRIBUTE_READ_ONLY: return "CKR_ATTRIBUTE_READ_ONLY"; |
|
case CKR_ATTRIBUTE_SENSITIVE: return "CKR_ATTRIBUTE_SENSITIVE"; |
|
case CKR_ATTRIBUTE_TYPE_INVALID: return "CKR_ATTRIBUTE_TYPE_INVALID"; |
|
case CKR_ATTRIBUTE_VALUE_INVALID: return "CKR_ATTRIBUTE_VALUE_INVALID"; |
|
case CKR_DATA_INVALID: return "CKR_DATA_INVALID"; |
|
case CKR_DATA_LEN_RANGE: return "CKR_DATA_LEN_RANGE"; |
|
case CKR_DEVICE_ERROR: return "CKR_DEVICE_ERROR"; |
|
case CKR_DEVICE_MEMORY: return "CKR_DEVICE_MEMORY"; |
|
case CKR_DEVICE_REMOVED: return "CKR_DEVICE_REMOVED"; |
|
case CKR_ENCRYPTED_DATA_INVALID: return "CKR_ENCRYPTED_DATA_INVALID"; |
|
case CKR_ENCRYPTED_DATA_LEN_RANGE: return "CKR_ENCRYPTED_DATA_LEN_RANGE"; |
|
case CKR_FUNCTION_CANCELED: return "CKR_FUNCTION_CANCELED"; |
|
case CKR_FUNCTION_NOT_PARALLEL: return "CKR_FUNCTION_NOT_PARALLEL"; |
|
case CKR_FUNCTION_NOT_SUPPORTED: return "CKR_FUNCTION_NOT_SUPPORTED"; |
|
case CKR_KEY_HANDLE_INVALID: return "CKR_KEY_HANDLE_INVALID"; |
|
case CKR_KEY_SIZE_RANGE: return "CKR_KEY_SIZE_RANGE"; |
|
case CKR_KEY_TYPE_INCONSISTENT: return "CKR_KEY_TYPE_INCONSISTENT"; |
|
case CKR_KEY_NOT_NEEDED: return "CKR_KEY_NOT_NEEDED"; |
|
case CKR_KEY_CHANGED: return "CKR_KEY_CHANGED"; |
|
case CKR_KEY_NEEDED: return "CKR_KEY_NEEDED"; |
|
case CKR_KEY_INDIGESTIBLE: return "CKR_KEY_INDIGESTIBLE"; |
|
case CKR_KEY_FUNCTION_NOT_PERMITTED: |
|
return "CKR_KEY_FUNCTION_NOT_PERMITTED"; |
|
case CKR_KEY_NOT_WRAPPABLE: return "CKR_KEY_NOT_WRAPPABLE"; |
|
case CKR_KEY_UNEXTRACTABLE: return "CKR_KEY_UNEXTRACTABLE"; |
|
case CKR_MECHANISM_INVALID: return "CKR_MECHANISM_INVALID"; |
|
case CKR_MECHANISM_PARAM_INVALID: return "CKR_MECHANISM_PARAM_INVALID"; |
|
case CKR_OBJECT_HANDLE_INVALID: return "CKR_OBJECT_HANDLE_INVALID"; |
|
case CKR_OPERATION_ACTIVE: return "CKR_OPERATION_ACTIVE"; |
|
case CKR_OPERATION_NOT_INITIALIZED: |
|
return "CKR_OPERATION_NOT_INITIALIZED"; |
|
case CKR_PIN_INCORRECT: return "CKR_PIN_INCORRECT"; |
|
case CKR_PIN_INVALID: return "CKR_PIN_INVALID"; |
|
case CKR_PIN_LEN_RANGE: return "CKR_PIN_LEN_RANGE"; |
|
case CKR_PIN_EXPIRED: return "CKR_PIN_EXPIRED"; |
|
case CKR_PIN_LOCKED: return "CKR_PIN_LOCKED"; |
|
case CKR_SESSION_CLOSED: return "CKR_SESSION_CLOSED"; |
|
case CKR_SESSION_COUNT: return "CKR_SESSION_COUNT"; |
|
case CKR_SESSION_HANDLE_INVALID: return "CKR_SESSION_HANDLE_INVALID"; |
|
case CKR_SESSION_PARALLEL_NOT_SUPPORTED: |
|
return "CKR_SESSION_PARALLEL_NOT_SUPPORTED"; |
|
case CKR_SESSION_READ_ONLY: return "CKR_SESSION_READ_ONLY"; |
|
case CKR_SESSION_EXISTS: return "CKR_SESSION_EXISTS"; |
|
case CKR_SESSION_READ_ONLY_EXISTS: return "CKR_SESSION_READ_ONLY_EXISTS"; |
|
case CKR_SESSION_READ_WRITE_SO_EXISTS: |
|
return "CKR_SESSION_READ_WRITE_SO_EXISTS"; |
|
case CKR_SIGNATURE_INVALID: return "CKR_SIGNATURE_INVALID"; |
|
case CKR_SIGNATURE_LEN_RANGE: return "CKR_SIGNATURE_LEN_RANGE"; |
|
case CKR_TEMPLATE_INCOMPLETE: return "CKR_TEMPLATE_INCOMPLETE"; |
|
case CKR_TEMPLATE_INCONSISTENT: return "CKR_TEMPLATE_INCONSISTENT"; |
|
case CKR_TOKEN_NOT_PRESENT: return "CKR_TOKEN_NOT_PRESENT"; |
|
case CKR_TOKEN_NOT_RECOGNIZED: return "CKR_TOKEN_NOT_RECOGNIZED"; |
|
case CKR_TOKEN_WRITE_PROTECTED: return "CKR_TOKEN_WRITE_PROTECTED"; |
|
case CKR_UNWRAPPING_KEY_HANDLE_INVALID: |
|
return "CKR_UNWRAPPING_KEY_HANDLE_INVALID"; |
|
case CKR_UNWRAPPING_KEY_SIZE_RANGE: |
|
return "CKR_UNWRAPPING_KEY_SIZE_RANGE"; |
|
case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT: |
|
return "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT"; |
|
case CKR_USER_ALREADY_LOGGED_IN: return "CKR_USER_ALREADY_LOGGED_IN"; |
|
case CKR_USER_NOT_LOGGED_IN: return "CKR_USER_NOT_LOGGED_IN"; |
|
case CKR_USER_PIN_NOT_INITIALIZED: return "CKR_USER_PIN_NOT_INITIALIZED"; |
|
case CKR_USER_TYPE_INVALID: return "CKR_USER_TYPE_INVALID"; |
|
case CKR_USER_ANOTHER_ALREADY_LOGGED_IN: |
|
return "CKR_USER_ANOTHER_ALREADY_LOGGED_IN"; |
|
case CKR_USER_TOO_MANY_TYPES: return "CKR_USER_TOO_MANY_TYPES"; |
|
case CKR_WRAPPED_KEY_INVALID: return "CKR_WRAPPED_KEY_INVALID"; |
|
case CKR_WRAPPED_KEY_LEN_RANGE: return "CKR_WRAPPED_KEY_LEN_RANGE"; |
|
case CKR_WRAPPING_KEY_HANDLE_INVALID: |
|
return "CKR_WRAPPING_KEY_HANDLE_INVALID"; |
|
case CKR_WRAPPING_KEY_SIZE_RANGE: return "CKR_WRAPPING_KEY_SIZE_RANGE"; |
|
case CKR_WRAPPING_KEY_TYPE_INCONSISTENT: |
|
return "CKR_WRAPPING_KEY_TYPE_INCONSISTENT"; |
|
case CKR_RANDOM_SEED_NOT_SUPPORTED: |
|
return "CKR_RANDOM_SEED_NOT_SUPPORTED"; |
|
case CKR_RANDOM_NO_RNG: return "CKR_RANDOM_NO_RNG"; |
|
case CKR_DOMAIN_PARAMS_INVALID: return "CKR_DOMAIN_PARAMS_INVALID"; |
|
case CKR_BUFFER_TOO_SMALL: return "CKR_BUFFER_TOO_SMALL"; |
|
case CKR_SAVED_STATE_INVALID: return "CKR_SAVED_STATE_INVALID"; |
|
case CKR_INFORMATION_SENSITIVE: return "CKR_INFORMATION_SENSITIVE"; |
|
case CKR_STATE_UNSAVEABLE: return "CKR_STATE_UNSAVEABLE"; |
|
case CKR_CRYPTOKI_NOT_INITIALIZED: return "CKR_CRYPTOKI_NOT_INITIALIZED"; |
|
case CKR_CRYPTOKI_ALREADY_INITIALIZED: |
|
return "CKR_CRYPTOKI_ALREADY_INITIALIZED"; |
|
case CKR_MUTEX_BAD: return "CKR_MUTEX_BAD"; |
|
case CKR_MUTEX_NOT_LOCKED: return "CKR_MUTEX_NOT_LOCKED"; |
|
case CKR_VENDOR_DEFINED: return "CKR_VENDOR_DEFINED"; |
|
default: { |
|
std::stringstream ss; |
|
ss<<"unknown error code ("<<res<<')'; |
|
return ss.str(); |
|
} |
|
} |
|
} |
|
|
|
Init::Init(const std::string& library, bool exc) try: |
|
_exc(exc), _res(CKR_OK), _fn(0) { |
|
CRYPTOKI_LOG("library: "<<library); |
|
//! calls @c functionList |
|
if (!functionList(library)) return; |
|
CRYPTOKI_LOG("now initialize "<<library); |
|
assert(_fn); |
|
//! calls @c C_Initialize |
|
check(_fn->C_Initialize(0), //! @todo add optional argument |
|
CRYPTOKI_FN_LOG("C_Initialize")); |
|
} catch (...) { |
|
throw access_error(CRYPTOKI_FN_LOG("C_Initialize") |
|
+": Error in initialization of library "+library); |
|
} |
|
|
|
Init::operator bool() { |
|
CRYPTOKI_LOG("log "<<(_res==CKR_OK?"success":"failed")); |
|
return _res==CKR_OK; |
|
} |
|
|
|
std::string Init::error() { |
|
CRYPTOKI_LOG("log"); |
|
return error(_res); |
|
} |
|
|
|
/*! @todo Not implemented: |
|
@code |
|
bool Init::getinfo() { |
|
//! calls @c C_GetInfo |
|
return check(_init._fn->C_GetInfo(CK_INFO_PTR), |
|
CRYPTOKI_FN_LOG("C_GetInfo")); |
|
} |
|
@endcode */ |
|
|
|
SlotList Init::slotList(bool tokenPresent) { |
|
CRYPTOKI_LOG("log"); |
|
SlotList res; |
|
CK_ULONG count(0); |
|
//! calls @c C_GetSlotList |
|
check(_fn->C_GetSlotList(tokenPresent?TRUE:FALSE, 0, &count), |
|
CRYPTOKI_FN_LOG("C_GetSlotList")); |
|
if (!count || !*this) return res; |
|
CK_SLOT_ID* slots = 0; |
|
try { |
|
do { |
|
delete[] slots; |
|
slots = new CK_SLOT_ID[count]; |
|
_res = _fn->C_GetSlotList(tokenPresent?TRUE:FALSE, slots, &count); |
|
} while (_res==CKR_BUFFER_TOO_SMALL); |
|
check(_res, CRYPTOKI_FN_LOG("C_GetSlotList")); |
|
if (!*this) return res; |
|
for (CK_ULONG i(0); i<count; ++i) res.push_back(Slot(*this, slots[i])); |
|
} catch (...) { |
|
delete[] slots; |
|
throw; |
|
} |
|
delete[] slots; |
|
return res; |
|
} |
|
|
|
//============================================================================ |
|
|
|
ObjectList Session::find(const AttributeList& attrs) { |
|
CRYPTOKI_LOG("log"); |
|
ObjectList res; |
|
CK_ATTRIBUTE* a(0); |
|
try { |
|
if (attrs.size()) { |
|
a = new CK_ATTRIBUTE[attrs.size()]; |
|
for (AttributeList::size_type i(0); i<attrs.size(); ++i) |
|
a[i] = attrs[i]; |
|
} |
|
//! calls @c C_FindObjectsInit |
|
if (check(_slot._init->_fn->C_FindObjectsInit |
|
(_session, a, attrs.size()), |
|
CRYPTOKI_FN_LOG("C_FindObjectsInit"))) { |
|
CK_OBJECT_HANDLE obj; |
|
//! calls @c C_FindObjects |
|
for (CK_ULONG objs(0); |
|
check(_slot._init->_fn->C_FindObjects |
|
(_session, &obj, 1, &objs), |
|
CRYPTOKI_FN_LOG("C_FindObjects")) && objs; |
|
res.push_back(Object(*this, obj))); |
|
} |
|
//! calls @c C_FindObjectsFinal |
|
check(_slot._init->_fn->C_FindObjectsFinal(_session), |
|
CRYPTOKI_FN_LOG("C_FindObjectsFinal")); |
|
delete[] a; |
|
return res; |
|
} catch (...) { |
|
delete[] a; |
|
throw; |
|
} |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
ObjectList Session::find(const Attribute& a) { |
|
CRYPTOKI_LOG("log"); |
|
AttributeList al; |
|
al.push_back(a); |
|
return find(al); |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
ObjectList Session::find(const Attribute& a1, const Attribute& a2) { |
|
CRYPTOKI_LOG("log"); |
|
AttributeList al; |
|
al.push_back(a1); |
|
al.push_back(a2); |
|
return find(al); |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
Object Session::create(const std::string& label, const openssl::X509& cert) { |
|
CRYPTOKI_LOG("log"); |
|
AttributeList attrs; |
|
attrs.push_back(Attribute(CKA_CLASS) |
|
.from<CK_OBJECT_CLASS>(CKO_CERTIFICATE)); |
|
attrs.push_back(Attribute(CKA_TOKEN).from<CK_BBOOL>(TRUE)); |
|
attrs.push_back(Attribute(CKA_PRIVATE).from<CK_BBOOL>(FALSE)); |
|
attrs.push_back(Attribute(CKA_MODIFIABLE).from<CK_BBOOL>(TRUE)); |
|
attrs.push_back(Attribute(CKA_LABEL, label)); |
|
attrs.push_back(Attribute(CKA_CERTIFICATE_TYPE) |
|
.from<CK_CERTIFICATE_TYPE>(CKC_X_509)); |
|
attrs.push_back(Attribute(CKA_SUBJECT, cert.subjectDER())); |
|
attrs.push_back(Attribute(CKA_ID, cert.id())); |
|
attrs.push_back(Attribute(CKA_ISSUER, cert.issuerDER())); |
|
attrs.push_back(Attribute(CKA_SERIAL_NUMBER, cert.serial())); |
|
attrs.push_back(Attribute(CKA_VALUE, cert.valueDER())); |
|
CRYPTOKI_LOG("create: serial = "<<crypto::hex(cert.serial())); |
|
return create(attrs); |
|
} |
|
|
|
Object Session::create(const std::string& label, |
|
const openssl::PrivateKey& key, |
|
const openssl::X509& cert) { |
|
CRYPTOKI_LOG("log"); |
|
int usage(cert.keyUsageFlags()); |
|
AttributeList attrs; |
|
attrs.push_back(Attribute(CKA_CLASS) |
|
.from<CK_OBJECT_CLASS>(CKO_PRIVATE_KEY)); |
|
attrs.push_back(Attribute(CKA_TOKEN).from<CK_BBOOL>(TRUE)); |
|
attrs.push_back(Attribute(CKA_PRIVATE).from<CK_BBOOL>(TRUE)); |
|
attrs.push_back(Attribute(CKA_MODIFIABLE).from<CK_BBOOL>(TRUE)); |
|
attrs.push_back(Attribute(CKA_LABEL, label)); |
|
attrs.push_back(Attribute(CKA_KEY_TYPE).from<CK_KEY_TYPE>(CKK_RSA)); |
|
attrs.push_back(Attribute(CKA_ID, cert.id())); |
|
attrs.push_back(Attribute(CKA_DERIVE).from<CK_BBOOL>(FALSE)); |
|
attrs.push_back(Attribute(CKA_SUBJECT, cert.subjectDER())); |
|
attrs.push_back(Attribute(CKA_SENSITIVE).from<CK_BBOOL>(TRUE)); |
|
attrs.push_back(Attribute(CKA_SECONDARY_AUTH).from<CK_BBOOL>(FALSE)); |
|
attrs.push_back(Attribute(CKA_DECRYPT) // Required by Doujak/Inverardi |
|
.from<CK_BBOOL>(usage&(X509v3_KU_DATA_ENCIPHERMENT |
|
|usage&X509v3_KU_KEY_ENCIPHERMENT) |
|
?TRUE:FALSE)); // instead of CKA_UNWRAP |
|
attrs.push_back(Attribute(CKA_SIGN) |
|
.from<CK_BBOOL>(usage&(X509v3_KU_DIGITAL_SIGNATURE |
|
|X509v3_KU_NON_REPUDIATION) |
|
?TRUE:FALSE)); |
|
attrs.push_back(Attribute(CKA_SIGN_RECOVER) // same as CKA_SIGN |
|
.from<CK_BBOOL>(usage&(X509v3_KU_DIGITAL_SIGNATURE |
|
|X509v3_KU_NON_REPUDIATION) |
|
?TRUE:FALSE)); |
|
attrs.push_back(Attribute(CKA_EXTRACTABLE).from<CK_BBOOL>(FALSE)); |
|
attrs.push_back(Attribute(CKA_MODULUS, key.modulus())); |
|
attrs.push_back(Attribute(CKA_PUBLIC_EXPONENT, key.publicExponent())); |
|
attrs.push_back(Attribute(CKA_PRIVATE_EXPONENT, key.privateExponent())); |
|
attrs.push_back(Attribute(CKA_PRIME_1, key.prime1())); |
|
attrs.push_back(Attribute(CKA_PRIME_2, key.prime2())); |
|
attrs.push_back(Attribute(CKA_EXPONENT_1, key.exponent1())); |
|
attrs.push_back(Attribute(CKA_EXPONENT_2, key.exponent2())); |
|
attrs.push_back(Attribute(CKA_COEFFICIENT, key.coefficient())); |
|
return create(attrs); |
|
} |
|
|
|
//---------------------------------------------------------------------------- |
|
Object Session::create(const AttributeList& attrs) { |
|
CRYPTOKI_LOG("log"); |
|
CK_ATTRIBUTE* a(0); |
|
try { |
|
if (attrs.size()) { |
|
a = new CK_ATTRIBUTE[attrs.size()]; |
|
for (AttributeList::size_type i(0); i<attrs.size(); ++i) |
|
a[i] = attrs[i]; |
|
} |
|
for (AttributeList::size_type i(0); i<attrs.size(); ++i) { |
|
std::string value((char*)a[i].pValue, a[i].ulValueLen); |
|
Attribute xxx(a[i].type, value); |
|
CRYPTOKI_LOG("Attribute: "<<xxx.name()<<" = "<<xxx.readableValue()); |
|
}; |
|
CK_OBJECT_HANDLE object; |
|
//! calls @c C_CreateObject |
|
check(_slot._init->_fn->C_CreateObject |
|
(_session, a, attrs.size(), &object), |
|
CRYPTOKI_FN_LOG("C_CreateObject")); |
|
delete[] a; |
|
return Object(*this, object); |
|
} catch (...) { |
|
delete[] a; |
|
throw; |
|
} |
|
} |
|
|
|
}
|
|
|