#ifndef CRYPTOKI_HXX
#define CRYPTOKI_HXX
/*! @file

    @id $Id$
    */
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

// interface
#include <openssl.hxx>
#ifndef WIN32
#include <pkcs11.h>
#else
#include <cryptoki.h>
#endif
#include <string>
#include <vector>
#include <map>
#include <set>
#include <mrw/checkcxx11.hxx>
#include <memory>

// for inline implementations only
#include <cryptaux.hxx>
#include <sstream>
#include <cstdlib> // malloc/free
#include <cstring> // memset
#include <cassert> // assert
#include <iomanip>

/*! @defgroup gcryptoki C++ Wrapper around Cryptoki API
    
    Wrapper to abstract the ugly PKCS#11 C-API with nice C++ features,
    such as exception handling, memory management, etc.

    The cryptoky library loads a dynamic library that is responsible
    for translating the card specific commands. The library is passed
    in the constructor of cryptoki::Library. Then method
    cryptoki::Library::slotList returns all slots found on the
    computer. */
//@{
/*! @defgroup cryptokilib Cryptoki C++ Library */
/*! @defgroup cryptokitypes Cryptoki C++ Types and Auxiliary */
/*! @defgroup cryptokiexceptions Cryptoki Exceptions */
/** @example cryptoki-demo.cxx */
/** @example cryptoki-sign-demo.cxx */
//@}

/// @addtogroup cryptokitypes
//@{
#ifndef CRYPTOKI_FN_LOG
  #include <iostream>
  #if __GNUC__ >= 2
    //! Cryptoki Error Message Formatting
    /*! If you want to change cryptoki error formatting, just
        redefine your own CRYPTOKY_FN_LOG macro before <code>\#include
        &lt;cryptoki.hxx&gt;</code>.
        #return std::String */
    #define CRYPTOKI_FN_LOG(X) (std::string(X " failed in ") \
                                +std::string(__PRETTY_FUNCTION__))
  #else
    #define CRYPTOKI_QUOTE(X) CRYPTOKI_QUOTE2(X)
    #define CRYPTOKI_QUOTE2(X) #X
    //! Cryptoki Error Message Formatting
    /*! If you want to change cryptoki error formatting, just
        redefine your own CRYPTOKY_FN_LOG macro before <code>\#include
        &lt;cryptoki.hxx&gt;</code>.
        @return string */
    #define CRYPTOKI_FN_LOG(X) X " failed in \
                               " __FILE__ ":" CRYPTOKI_QUOTE(__LINE__)
  #endif
#endif
//@}

//! @ref gcryptoki @copydoc gcryptoki
namespace cryptoki {

  //! @addtogroup cryptokitypes
  //@{

  /// Map cryptoki number to a string, care about unavailable and infinite
  /** Cryptoky knows illegal values (unavailable information), which
      is mapped to @c - and infinite values which are mapped to @c ∞
      when converted to string. All other numbers just show the number
      as numeric string. */
  inline std::string string(CK_ULONG num) {
    switch (num) {
      case CK_UNAVAILABLE_INFORMATION: return "-";
      case CK_EFFECTIVELY_INFINITE: return "∞";
      default: {
        std::stringstream ss;
        ss<<num;
        return ss.str();
      }
    }
  }

  /// Convert any kind of array (buffer) to an std::vector.
  template <typename TYPE> std::vector<TYPE> toVector(TYPE in[]) {
    return std::vector<TYPE>(in, in+sizeof(in)/sizeof(in[0]));
  }

  //@}

  //============================================================================
  /*! @addtogroup cryptokiexceptions */
  //@{
  
  //----------------------------------------------------------------------------
  class exception: public std::exception {
    public:
      exception(const std::string& reason) throw():
          _what("cryptoki: "+reason) {
        CRYPTOLOG("ERROR: "<<what());
      }
      ~exception() throw() {}
      const char* what() const throw() {
        return _what.c_str();
      }
    private:
      std::string _what;
  };
  //----------------------------------------------------------------------------
  class not_implemented: public exception {
    public:
      not_implemented(const std::string& reason) throw():
          exception("feature is not implemented: "+reason) {}
  };
  //----------------------------------------------------------------------------
  class access_error: public exception {
    public:
      access_error(const std::string& reason) throw():
          exception("smardcard access error: "+reason) {}
  };
  //----------------------------------------------------------------------------
  class wrong_pin: public access_error {
    public:
      wrong_pin(const std::string& reason) throw():
          access_error("wrong pin: "+reason) {}
  };
  //@}
  
  /*! @addtogroup cryptokitypes */
  //@{

  class Slot;
  typedef std::vector<Slot> SlotList;
  
  class Object;
  typedef std::vector<Object> ObjectList;

  typedef std::vector<CK_ATTRIBUTE_TYPE> AttributeTypeList;

  /// Represents a cryproki attribute and maps to C++ types.
  class Attribute {
    public:
      Attribute(CK_ATTRIBUTE_TYPE t = -1): type(t) {}
      Attribute(CK_ATTRIBUTE_TYPE t, const std::string& v): type(t), value(v) {}
      Attribute(CK_ATTRIBUTE& attr):
          type(attr.type), value((char*)attr.pValue, attr.ulValueLen) {
        free(attr.pValue);
        attr.pValue = 0;
      }
      Attribute& operator=(const std::string& v) {
        value = v;
        return *this;
      }
      bool operator==(const Attribute& o) const {
        return type==o.type && value==o.value;
      }
      bool operator!=(const Attribute& o) const {
        return type!=o.type || value!=o.value;
      }
      //! Convert to a @c CK_ATTRIBUTE.
      /*! @note @c pValue points to the internal buffer of this
          element and must therefore not be changed. Also this object
          must not be destructed before the returned @c
          CK_ATTRIBUTE. */
      operator CK_ATTRIBUTE() const {
        CK_ATTRIBUTE a;
        a.type = type;
        a.pValue = const_cast<char*>(&value[0]);
        a.ulValueLen = value.size();
        return a;
      }
      /// Textual name of the attribute.
      std::string name() const {
        switch (type) {
          case CKA_CLASS: return "CLASS";
          case CKA_TOKEN: return "TOKEN";
          case CKA_PRIVATE: return "PRIVATE";
          case CKA_LABEL: return "LABEL";
          case CKA_APPLICATION: return "APPLICATION";
          case CKA_VALUE: return "VALUE";
          case CKA_OBJECT_ID: return "OBJECT_ID";
          case CKA_CERTIFICATE_TYPE: return "CERTIFICATE_TYPE";
          case CKA_ISSUER: return "ISSUER";
          case CKA_SERIAL_NUMBER: return "SERIAL_NUMBER";
          case CKA_AC_ISSUER: return "AC_ISSUER";
          case CKA_OWNER: return "OWNER";
          case CKA_ATTR_TYPES: return "ATTR_TYPES";
          case CKA_TRUSTED: return "TRUSTED";
          case CKA_KEY_TYPE: return "KEY_TYPE";
          case CKA_SUBJECT: return "SUBJECT";
          case CKA_ID: return "ID";
          case CKA_SENSITIVE: return "SENSITIVE";
          case CKA_ENCRYPT: return "ENCRYPT";
          case CKA_DECRYPT: return "DECRYPT";
          case CKA_WRAP: return "WRAP";
          case CKA_UNWRAP: return "UNWRAP";
          case CKA_SIGN: return "SIGN";
          case CKA_SIGN_RECOVER: return "SIGN_RECOVER";
          case CKA_VERIFY: return "VERIFY";
          case CKA_VERIFY_RECOVER: return "VERIFY_RECOVER";
          case CKA_DERIVE: return "DERIVE";
          case CKA_START_DATE: return "START_DATE";
          case CKA_END_DATE: return "END_DATE";
          case CKA_MODULUS: return "MODULUS";
          case CKA_MODULUS_BITS: return "MODULUS_BITS";
          case CKA_PUBLIC_EXPONENT: return "PUBLIC_EXPONENT";
          case CKA_PRIVATE_EXPONENT: return "PRIVATE_EXPONENT";
          case CKA_PRIME_1: return "PRIME_1";
          case CKA_PRIME_2: return "PRIME_2";
          case CKA_EXPONENT_1: return "EXPONENT_1";
          case CKA_EXPONENT_2: return "EXPONENT_2";
          case CKA_COEFFICIENT: return "COEFFICIENT";
          case CKA_PRIME: return "PRIME";
          case CKA_SUBPRIME: return "SUBPRIME";
          case CKA_BASE: return "BASE";
          case CKA_PRIME_BITS: return "PRIME_BITS";
          //case CKA_SUBPRIME_BITS: return "SUBPRIME_BITS";
          case CKA_VALUE_BITS: return "VALUE_BITS";
          case CKA_VALUE_LEN: return "VALUE_LEN";
          case CKA_EXTRACTABLE: return "EXTRACTABLE";
          case CKA_LOCAL: return "LOCAL";
          case CKA_NEVER_EXTRACTABLE: return "NEVER_EXTRACTABLE";
          case CKA_ALWAYS_SENSITIVE: return "ALWAYS_SENSITIVE";
          case CKA_KEY_GEN_MECHANISM: return "KEY_GEN_MECHANISM";
          case CKA_MODIFIABLE: return "MODIFIABLE";
          //case CKA_ECDSA_PARAMS: return "ECDSA_PARAMS";
          case CKA_EC_PARAMS: return "ECDSA_PARAMS or EC_PARAMS";
          case CKA_EC_POINT: return "EC_POINT";
          case CKA_SECONDARY_AUTH: return "SECONDARY_AUTH";
          case CKA_AUTH_PIN_FLAGS: return "AUTH_PIN_FLAGS";
          case CKA_HW_FEATURE_TYPE: return "HW_FEATURE_TYPE";
          case CKA_RESET_ON_INIT: return "RESET_ON_INIT";
          case CKA_HAS_RESET: return "HAS_RESET";
          case CKA_VENDOR_DEFINED: return "VENDOR_DEFINED";
          //case CKA_IBM_OPAQUE: return "IBM_OPAQUE";
          default: return "UNKNOWN";
        }
      }
      /// Textual representation of the value.
      std::string readableValue(std::string::size_type len=20,
                                std::string::size_type indent=0) const {
        std::string res(indent, ' ');
        switch (type) {
          case CKA_CLASS:
            switch (*((const CK_OBJECT_CLASS*)&value[0])) {
              case CKO_DATA: return res+"DATA";
              case CKO_CERTIFICATE: return res+"CERTIFICATE";
              case CKO_PUBLIC_KEY: return res+"PUBLIC_KEY";
              case CKO_PRIVATE_KEY: return res+"PRIVATE_KEY";
              case CKO_SECRET_KEY: return res+"SECRET_KEY";
              case CKO_HW_FEATURE: return res+"HW_FEATURE";
              case CKO_DOMAIN_PARAMETERS: return res+"DOMAIN_PARAMETERS";
              case CKO_VENDOR_DEFINED: return res+"VENDOR_DEFINED";
              default: return res+"UNKNOWN";
            }
          default: return crypto::readable(value, len, indent);
        }
      }
      /// Initialize from a given type.
      /** To use this method, you must know what type the attribute
          represents. */
      template<typename TYPE> Attribute& from(const TYPE& v) {
        value = std::string((const char*)&v, sizeof(TYPE));
        return *this;
      }
      /// Convert to a given type.
      /** To use this method, you must know what type the attribute
          represents. */
      template<typename TYPE> TYPE to() const {
        assert(sizeof(TYPE)==value.size());
        return *reinterpret_cast<const TYPE*>(&value[0]);
      }
      CK_ATTRIBUTE_TYPE type;
      std::string value;
  };
  typedef std::map<CK_ATTRIBUTE_TYPE, Attribute> AttributeMap;
  typedef std::vector<Attribute> AttributeList;

  /// String with fixed length.
  /** FixString represents a string with a fix with spaces and can be
      converted from and to a std::string by adding and removing the
      fill-spaces. */
  template<std::string::size_type SIZE>
      class FixString: public std::string {
    public:
      FixString() {}
      FixString(const char* const cStr) {
        *this = std::string(cStr, SIZE);
        size_type pos(find_last_not_of(" "));
        if (pos!=npos) resize(pos+1); else resize(0);
      }
      FixString(const unsigned char* const cStr) {
        *this = std::string((const char*)cStr, SIZE);
        size_type pos(find_last_not_of(" "));
        if (pos!=npos) resize(pos+1); else resize(0);
      }
      FixString& operator=(const std::string& other) {
        std::string::operator=(other);
        return *this;
      }
      FixString& operator=(const char* const cStr) {
        *this = std::string(cStr, SIZE);
        size_type pos(find_last_not_of(" "));
        if (pos!=npos) resize(pos+1); else resize(0);
        return *this;
      }
      FixString& operator=(const unsigned char* const cStr) {
        *this = std::string((const char*)cStr, SIZE);
        size_type pos(find_last_not_of(" "));
        if (pos!=npos) resize(pos+1); else resize(0);
        return *this;
      }
      operator unsigned char*() {
        return (unsigned char*)begin().operator->();
      }
      FixString fix() {
        FixString cpy(*this);
        cpy.resize(SIZE, ' ');
        return cpy;
      }
  };

  /// C++ representation of mechanism information.
  struct MechanismInfo {
      CK_MECHANISM_TYPE id;
      std::string name;
      CK_ULONG    minKeySize;
      CK_ULONG    maxKeySize;
      CK_FLAGS    flags;
      /// Construct from type with undefined infos. Use assigment later.
      MechanismInfo(CK_MECHANISM_TYPE type):
          minKeySize(0), maxKeySize(0), flags(0) {
        *this=type;
      }
      /// Fully construct from type and infos.
      MechanismInfo(CK_MECHANISM_TYPE type, const CK_MECHANISM_INFO& info) {
        *this=type;
        *this=info;
      }
      /// Set name and id from CK_MECHANISM_TYPE.
      MechanismInfo& operator=(CK_MECHANISM_TYPE type) {
        id=type;
        switch (id) {
#ifdef CKM_RSA_PKCS_KEY_PAIR_GEN
          case CKM_RSA_PKCS_KEY_PAIR_GEN: name="RSA_PKCS_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_RSA_PKCS
          case CKM_RSA_PKCS: name="RSA_PKCS"; break;
#endif
#ifdef CKM_RSA_9796
          case CKM_RSA_9796: name="RSA_9796"; break;
#endif
#ifdef CKM_RSA_X_509
          case CKM_RSA_X_509: name="RSA_X_509"; break;
#endif
#ifdef CKM_MD2_RSA_PKCS
          case CKM_MD2_RSA_PKCS: name="MD2_RSA_PKCS"; break;
#endif
#ifdef CKM_MD5_RSA_PKCS
          case CKM_MD5_RSA_PKCS: name="MD5_RSA_PKCS"; break;
#endif
#ifdef CKM_SHA1_RSA_PKCS
          case CKM_SHA1_RSA_PKCS: name="SHA1_RSA_PKCS"; break;
#endif
#ifdef CKM_RIPEMD128_RSA_PKCS
          case CKM_RIPEMD128_RSA_PKCS: name="RIPEMD128_RSA_PKCS"; break;
#endif
#ifdef CKM_RIPEMD160_RSA_PKCS
          case CKM_RIPEMD160_RSA_PKCS: name="RIPEMD160_RSA_PKCS"; break;
#endif
#ifdef CKM_RSA_PKCS_OAEP
          case CKM_RSA_PKCS_OAEP: name="RSA_PKCS_OAEP"; break;
#endif
#ifdef CKM_RSA_X9_31_KEY_PAIR_GEN
          case CKM_RSA_X9_31_KEY_PAIR_GEN: name="RSA_X9_31_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_RSA_X9_31
          case CKM_RSA_X9_31: name="RSA_X9_31"; break;
#endif
#ifdef CKM_SHA1_RSA_X9_31
          case CKM_SHA1_RSA_X9_31: name="SHA1_RSA_X9_31"; break;
#endif
#ifdef CKM_RSA_PKCS_PSS
          case CKM_RSA_PKCS_PSS: name="RSA_PKCS_PSS"; break;
#endif
#ifdef CKM_SHA1_RSA_PKCS_PSS
          case CKM_SHA1_RSA_PKCS_PSS: name="SHA1_RSA_PKCS_PSS"; break;
#endif
#ifdef CKM_DSA_KEY_PAIR_GEN
          case CKM_DSA_KEY_PAIR_GEN: name="DSA_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_DSA
          case CKM_DSA: name="DSA"; break;
#endif
#ifdef CKM_DSA_SHA1
          case CKM_DSA_SHA1: name="DSA_SHA1"; break;
#endif
#ifdef CKM_DH_PKCS_KEY_PAIR_GEN
          case CKM_DH_PKCS_KEY_PAIR_GEN: name="DH_PKCS_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_DH_PKCS_DERIVE
          case CKM_DH_PKCS_DERIVE: name="DH_PKCS_DERIVE"; break;
#endif
#ifdef CKM_X9_42_DH_KEY_PAIR_GEN
          case CKM_X9_42_DH_KEY_PAIR_GEN: name="X9_42_DH_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_X9_42_DH_DERIVE
          case CKM_X9_42_DH_DERIVE: name="X9_42_DH_DERIVE"; break;
#endif
#ifdef CKM_X9_42_DH_HYBRID_DERIVE
          case CKM_X9_42_DH_HYBRID_DERIVE: name="X9_42_DH_HYBRID_DERIVE"; break;
#endif
#ifdef CKM_X9_42_MQV_DERIVE
          case CKM_X9_42_MQV_DERIVE: name="X9_42_MQV_DERIVE"; break;
#endif
#ifdef CKM_SHA256_RSA_PKCS
          case CKM_SHA256_RSA_PKCS: name="SHA256_RSA_PKCS"; break;
#endif
#ifdef CKM_RC2_KEY_GEN
          case CKM_RC2_KEY_GEN: name="RC2_KEY_GEN"; break;
#endif
#ifdef CKM_RC2_ECB
          case CKM_RC2_ECB: name="RC2_ECB"; break;
#endif
#ifdef CKM_RC2_CBC
          case CKM_RC2_CBC: name="RC2_CBC"; break;
#endif
#ifdef CKM_RC2_MAC
          case CKM_RC2_MAC: name="RC2_MAC"; break;
#endif
#ifdef CKM_RC2_MAC_GENERAL
          case CKM_RC2_MAC_GENERAL: name="RC2_MAC_GENERAL"; break;
#endif
#ifdef CKM_RC2_CBC_PAD
          case CKM_RC2_CBC_PAD: name="RC2_CBC_PAD"; break;
#endif
#ifdef CKM_RC4_KEY_GEN
          case CKM_RC4_KEY_GEN: name="RC4_KEY_GEN"; break;
#endif
#ifdef CKM_RC4
          case CKM_RC4: name="RC4"; break;
#endif
#ifdef CKM_DES_KEY_GEN
          case CKM_DES_KEY_GEN: name="DES_KEY_GEN"; break;
#endif
#ifdef CKM_DES_ECB
          case CKM_DES_ECB: name="DES_ECB"; break;
#endif
#ifdef CKM_DES_CBC
          case CKM_DES_CBC: name="DES_CBC"; break;
#endif
#ifdef CKM_DES_MAC
          case CKM_DES_MAC: name="DES_MAC"; break;
#endif
#ifdef CKM_DES_MAC_GENERAL
          case CKM_DES_MAC_GENERAL: name="DES_MAC_GENERAL"; break;
#endif
#ifdef CKM_DES_CBC_PAD
          case CKM_DES_CBC_PAD: name="DES_CBC_PAD"; break;
#endif
#ifdef CKM_DES2_KEY_GEN
          case CKM_DES2_KEY_GEN: name="DES2_KEY_GEN"; break;
#endif
#ifdef CKM_DES3_KEY_GEN
          case CKM_DES3_KEY_GEN: name="DES3_KEY_GEN"; break;
#endif
#ifdef CKM_DES3_ECB
          case CKM_DES3_ECB: name="DES3_ECB"; break;
#endif
#ifdef CKM_DES3_CBC
          case CKM_DES3_CBC: name="DES3_CBC"; break;
#endif
#ifdef CKM_DES3_MAC
          case CKM_DES3_MAC: name="DES3_MAC"; break;
#endif
#ifdef CKM_DES3_MAC_GENERAL
          case CKM_DES3_MAC_GENERAL: name="DES3_MAC_GENERAL"; break;
#endif
#ifdef CKM_DES3_CBC_PAD
          case CKM_DES3_CBC_PAD: name="DES3_CBC_PAD"; break;
#endif
#ifdef CKM_CDMF_KEY_GEN
          case CKM_CDMF_KEY_GEN: name="CDMF_KEY_GEN"; break;
#endif
#ifdef CKM_CDMF_ECB
          case CKM_CDMF_ECB: name="CDMF_ECB"; break;
#endif
#ifdef CKM_CDMF_CBC
          case CKM_CDMF_CBC: name="CDMF_CBC"; break;
#endif
#ifdef CKM_CDMF_MAC
          case CKM_CDMF_MAC: name="CDMF_MAC"; break;
#endif
#ifdef CKM_CDMF_MAC_GENERAL
          case CKM_CDMF_MAC_GENERAL: name="CDMF_MAC_GENERAL"; break;
#endif
#ifdef CKM_CDMF_CBC_PAD
          case CKM_CDMF_CBC_PAD: name="CDMF_CBC_PAD"; break;
#endif
#ifdef CKM_MD2
          case CKM_MD2: name="MD2"; break;
#endif
#ifdef CKM_MD2_HMAC
          case CKM_MD2_HMAC: name="MD2_HMAC"; break;
#endif
#ifdef CKM_MD2_HMAC_GENERAL
          case CKM_MD2_HMAC_GENERAL: name="MD2_HMAC_GENERAL"; break;
#endif
#ifdef CKM_MD5
          case CKM_MD5: name="MD5"; break;
#endif
#ifdef CKM_MD5_HMAC
          case CKM_MD5_HMAC: name="MD5_HMAC"; break;
#endif
#ifdef CKM_MD5_HMAC_GENERAL
          case CKM_MD5_HMAC_GENERAL: name="MD5_HMAC_GENERAL"; break;
#endif
#ifdef CKM_SHA_1
          case CKM_SHA_1: name="SHA_1"; break;
#endif
#ifdef CKM_SHA_1_HMAC
          case CKM_SHA_1_HMAC: name="SHA_1_HMAC"; break;
#endif
#ifdef CKM_SHA_1_HMAC_GENERAL
          case CKM_SHA_1_HMAC_GENERAL: name="SHA_1_HMAC_GENERAL"; break;
#endif
#ifdef CKM_RIPEMD128
          case CKM_RIPEMD128: name="RIPEMD128"; break;
#endif
#ifdef CKM_RIPEMD128_HMAC
          case CKM_RIPEMD128_HMAC: name="RIPEMD128_HMAC"; break;
#endif
#ifdef CKM_RIPEMD128_HMAC_GENERAL
          case CKM_RIPEMD128_HMAC_GENERAL: name="RIPEMD128_HMAC_GENERAL"; break;
#endif
#ifdef CKM_RIPEMD160
          case CKM_RIPEMD160: name="RIPEMD160"; break;
#endif
#ifdef CKM_RIPEMD160_HMAC
          case CKM_RIPEMD160_HMAC: name="RIPEMD160_HMAC"; break;
#endif
#ifdef CKM_RIPEMD160_HMAC_GENERAL
          case CKM_RIPEMD160_HMAC_GENERAL: name="RIPEMD160_HMAC_GENERAL"; break;
#endif
#ifdef CKM_SHA256
          case CKM_SHA256: name="SHA256"; break;
#endif
#ifdef CKM_SHA256_HMAC
          case CKM_SHA256_HMAC: name="SHA256_HMAC"; break;
#endif
#ifdef CKM_SHA256_HMAC_GENERAL
          case CKM_SHA256_HMAC_GENERAL: name="SHA256_HMAC_GENERAL"; break;
#endif
#ifdef CKM_SHA384
          case CKM_SHA384: name="SHA384"; break;
#endif
#ifdef CKM_SHA384_HMAC
          case CKM_SHA384_HMAC: name="SHA384_HMAC"; break;
#endif
#ifdef CKM_SHA384_HMAC_GENERAL
          case CKM_SHA384_HMAC_GENERAL: name="SHA384_HMAC_GENERAL"; break;
#endif
#ifdef CKM_SHA512
          case CKM_SHA512: name="SHA512"; break;
#endif
#ifdef CKM_SHA512_HMAC
          case CKM_SHA512_HMAC: name="SHA512_HMAC"; break;
#endif
#ifdef CKM_SHA512_HMAC_GENERAL
          case CKM_SHA512_HMAC_GENERAL: name="SHA512_HMAC_GENERAL"; break;
#endif
#ifdef CKM_CAST_KEY_GEN
          case CKM_CAST_KEY_GEN: name="CAST_KEY_GEN"; break;
#endif
#ifdef CKM_CAST_ECB
          case CKM_CAST_ECB: name="CAST_ECB"; break;
#endif
#ifdef CKM_CAST_CBC
          case CKM_CAST_CBC: name="CAST_CBC"; break;
#endif
#ifdef CKM_CAST_MAC
          case CKM_CAST_MAC: name="CAST_MAC"; break;
#endif
#ifdef CKM_CAST_MAC_GENERAL
          case CKM_CAST_MAC_GENERAL: name="CAST_MAC_GENERAL"; break;
#endif
#ifdef CKM_CAST_CBC_PAD
          case CKM_CAST_CBC_PAD: name="CAST_CBC_PAD"; break;
#endif
#ifdef CKM_CAST3_KEY_GEN
          case CKM_CAST3_KEY_GEN: name="CAST3_KEY_GEN"; break;
#endif
#ifdef CKM_CAST3_ECB
          case CKM_CAST3_ECB: name="CAST3_ECB"; break;
#endif
#ifdef CKM_CAST3_CBC
          case CKM_CAST3_CBC: name="CAST3_CBC"; break;
#endif
#ifdef CKM_CAST3_MAC
          case CKM_CAST3_MAC: name="CAST3_MAC"; break;
#endif
#ifdef CKM_CAST3_MAC_GENERAL
          case CKM_CAST3_MAC_GENERAL: name="CAST3_MAC_GENERAL"; break;
#endif
#ifdef CKM_CAST3_CBC_PAD
          case CKM_CAST3_CBC_PAD: name="CAST3_CBC_PAD"; break;
#endif
#ifdef CKM_CAST5_KEY_GEN
          case CKM_CAST5_KEY_GEN: name="CAST5_KEY_GEN"; break;
#endif
#ifdef CKM_CAST128_KEY_GEN
            //case CKM_CAST128_KEY_GEN: name="CAST5_KEY_GEN or
          //CAST128_KEY_GEN"; break;
#endif
#ifdef CKM_CAST5_ECB
          case CKM_CAST5_ECB: name="CAST5_ECB"; break;
#endif
#ifdef CKM_CAST128_ECB
            //case CKM_CAST128_ECB: name="CAST5_ECB or CAST128_ECB"; break;
#endif
#ifdef CKM_CAST5_CBC
          case CKM_CAST5_CBC: name="CAST5_CBC"; break;
#endif
#ifdef CKM_CAST128_CBC
            //case CKM_CAST128_CBC: name="CAST5_CBC or CAST128_CBC"; break;
#endif
#ifdef CKM_CAST5_MAC
          case CKM_CAST5_MAC: name="CAST5_MAC"; break;
#endif
#ifdef CKM_CAST128_MAC
            //case CKM_CAST128_MAC: name="CAST5_MAC or CAST128_MAC"; break;
#endif
#ifdef CKM_CAST5_MAC_GENERAL
          case CKM_CAST5_MAC_GENERAL: name="CAST5_MAC_GENERAL"; break;
#endif
#ifdef CKM_CAST128_MAC_GENERAL
            //case CKM_CAST128_MAC_GENERAL: name="CAST5_MAC_GENERAL or
          //CAST128_MAC_GENERAL"; break;
#endif
#ifdef CKM_CAST5_CBC_PAD
          case CKM_CAST5_CBC_PAD: name="CAST5_CBC_PAD"; break;
#endif
#ifdef CKM_CAST128_CBC_PAD
            //case CKM_CAST128_CBC_PAD: name="CAST5_CBC_PAD or
          //CAST128_CBC_PAD"; break;
#endif
#ifdef CKM_RC5_KEY_GEN
          case CKM_RC5_KEY_GEN: name="RC5_KEY_GEN"; break;
#endif
#ifdef CKM_RC5_ECB
          case CKM_RC5_ECB: name="RC5_ECB"; break;
#endif
#ifdef CKM_RC5_CBC
          case CKM_RC5_CBC: name="RC5_CBC"; break;
#endif
#ifdef CKM_RC5_MAC
          case CKM_RC5_MAC: name="RC5_MAC"; break;
#endif
#ifdef CKM_RC5_MAC_GENERAL
          case CKM_RC5_MAC_GENERAL: name="RC5_MAC_GENERAL"; break;
#endif
#ifdef CKM_RC5_CBC_PAD
          case CKM_RC5_CBC_PAD: name="RC5_CBC_PAD"; break;
#endif
#ifdef CKM_IDEA_KEY_GEN
          case CKM_IDEA_KEY_GEN: name="IDEA_KEY_GEN"; break;
#endif
#ifdef CKM_IDEA_ECB
          case CKM_IDEA_ECB: name="IDEA_ECB"; break;
#endif
#ifdef CKM_IDEA_CBC
          case CKM_IDEA_CBC: name="IDEA_CBC"; break;
#endif
#ifdef CKM_IDEA_MAC
          case CKM_IDEA_MAC: name="IDEA_MAC"; break;
#endif
#ifdef CKM_IDEA_MAC_GENERAL
          case CKM_IDEA_MAC_GENERAL: name="IDEA_MAC_GENERAL"; break;
#endif
#ifdef CKM_IDEA_CBC_PAD
          case CKM_IDEA_CBC_PAD: name="IDEA_CBC_PAD"; break;
#endif
#ifdef CKM_GENERIC_SECRET_KEY_GEN
          case CKM_GENERIC_SECRET_KEY_GEN: name="GENERIC_SECRET_KEY_GEN"; break;
#endif
#ifdef CKM_CONCATENATE_BASE_AND_KEY
          case CKM_CONCATENATE_BASE_AND_KEY:
            name="CONCATENATE_BASE_AND_KEY"; break;
#endif
#ifdef CKM_CONCATENATE_BASE_AND_DATA
          case CKM_CONCATENATE_BASE_AND_DATA:
            name="CONCATENATE_BASE_AND_DATA"; break;
#endif
#ifdef CKM_CONCATENATE_DATA_AND_BASE
          case CKM_CONCATENATE_DATA_AND_BASE:
            name="CONCATENATE_DATA_AND_BASE"; break;
#endif
#ifdef CKM_XOR_BASE_AND_DATA
          case CKM_XOR_BASE_AND_DATA: name="XOR_BASE_AND_DATA"; break;
#endif
#ifdef CKM_EXTRACT_KEY_FROM_KEY
          case CKM_EXTRACT_KEY_FROM_KEY: name="EXTRACT_KEY_FROM_KEY"; break;
#endif
#ifdef CKM_SSL3_PRE_MASTER_KEY_GEN
          case CKM_SSL3_PRE_MASTER_KEY_GEN:
            name="SSL3_PRE_MASTER_KEY_GEN"; break;
#endif
#ifdef CKM_SSL3_MASTER_KEY_DERIVE
          case CKM_SSL3_MASTER_KEY_DERIVE: name="SSL3_MASTER_KEY_DERIVE"; break;
#endif
#ifdef CKM_SSL3_KEY_AND_MAC_DERIVE
          case CKM_SSL3_KEY_AND_MAC_DERIVE:
            name="SSL3_KEY_AND_MAC_DERIVE"; break;
#endif
#ifdef CKM_SSL3_MASTER_KEY_DERIVE_DH
          case CKM_SSL3_MASTER_KEY_DERIVE_DH:
            name="SSL3_MASTER_KEY_DERIVE_DH"; break;
#endif
#ifdef CKM_TLS_PRE_MASTER_KEY_GEN
          case CKM_TLS_PRE_MASTER_KEY_GEN: name="TLS_PRE_MASTER_KEY_GEN"; break;
#endif
#ifdef CKM_TLS_MASTER_KEY_DERIVE
          case CKM_TLS_MASTER_KEY_DERIVE: name="TLS_MASTER_KEY_DERIVE"; break;
#endif
#ifdef CKM_TLS_KEY_AND_MAC_DERIVE
          case CKM_TLS_KEY_AND_MAC_DERIVE: name="TLS_KEY_AND_MAC_DERIVE"; break;
#endif
#ifdef CKM_TLS_MASTER_KEY_DERIVE_DH
          case CKM_TLS_MASTER_KEY_DERIVE_DH:
            name="TLS_MASTER_KEY_DERIVE_DH"; break;
#endif
#ifdef CKM_SSL3_MD5_MAC
          case CKM_SSL3_MD5_MAC: name="SSL3_MD5_MAC"; break;
#endif
#ifdef CKM_SSL3_SHA1_MAC
          case CKM_SSL3_SHA1_MAC: name="SSL3_SHA1_MAC"; break;
#endif
#ifdef CKM_MD5_KEY_DERIVATION
          case CKM_MD5_KEY_DERIVATION: name="MD5_KEY_DERIVATION"; break;
#endif
#ifdef CKM_MD2_KEY_DERIVATION
          case CKM_MD2_KEY_DERIVATION: name="MD2_KEY_DERIVATION"; break;
#endif
#ifdef CKM_SHA1_KEY_DERIVATION
          case CKM_SHA1_KEY_DERIVATION: name="SHA1_KEY_DERIVATION"; break;
#endif
#ifdef CKM_SHA256_KEY_DERIVATION
          case CKM_SHA256_KEY_DERIVATION: name="SHA256_KEY_DERIVATION"; break;
#endif
#ifdef CKM_PBE_MD2_DES_CBC
          case CKM_PBE_MD2_DES_CBC: name="PBE_MD2_DES_CBC"; break;
#endif
#ifdef CKM_PBE_MD5_DES_CBC
          case CKM_PBE_MD5_DES_CBC: name="PBE_MD5_DES_CBC"; break;
#endif
#ifdef CKM_PBE_MD5_CAST_CBC
          case CKM_PBE_MD5_CAST_CBC: name="PBE_MD5_CAST_CBC"; break;
#endif
#ifdef CKM_PBE_MD5_CAST3_CBC
          case CKM_PBE_MD5_CAST3_CBC: name="PBE_MD5_CAST3_CBC"; break;
#endif
#ifdef CKM_PBE_MD5_CAST5_CBC
          case CKM_PBE_MD5_CAST5_CBC: name="PBE_MD5_CAST5_CBC"; break;
#endif
#ifdef CKM_PBE_MD5_CAST128_CBC
            //case CKM_PBE_MD5_CAST128_CBC: name="PBE_MD5_CAST5_CBC or
          //PBE_MD5_CAST128_CBC"; break;
#endif
#ifdef CKM_PBE_SHA1_CAST5_CBC
          case CKM_PBE_SHA1_CAST5_CBC: name="PBE_SHA1_CAST5_CBC"; break;
#endif
#ifdef CKM_PBE_SHA1_CAST128_CBC
            //case CKM_PBE_SHA1_CAST128_CBC: name="PBE_SHA1_CAST5_CBC or
          //PBE_SHA1_CAST128_CBC"; break;
#endif
#ifdef CKM_PBE_SHA1_RC4_128
          case CKM_PBE_SHA1_RC4_128: name="PBE_SHA1_RC4_128"; break;
#endif
#ifdef CKM_PBE_SHA1_RC4_40
          case CKM_PBE_SHA1_RC4_40: name="PBE_SHA1_RC4_40"; break;
#endif
#ifdef CKM_PBE_SHA1_DES3_EDE_CBC
          case CKM_PBE_SHA1_DES3_EDE_CBC: name="PBE_SHA1_DES3_EDE_CBC"; break;
#endif
#ifdef CKM_PBE_SHA1_DES2_EDE_CBC
          case CKM_PBE_SHA1_DES2_EDE_CBC: name="PBE_SHA1_DES2_EDE_CBC"; break;
#endif
#ifdef CKM_PBE_SHA1_RC2_128_CBC
          case CKM_PBE_SHA1_RC2_128_CBC: name="PBE_SHA1_RC2_128_CBC"; break;
#endif
#ifdef CKM_PBE_SHA1_RC2_40_CBC
          case CKM_PBE_SHA1_RC2_40_CBC: name="PBE_SHA1_RC2_40_CBC"; break;
#endif
#ifdef CKM_PKCS5_PBKD2
          case CKM_PKCS5_PBKD2: name="PKCS5_PBKD2"; break;
#endif
#ifdef CKM_PBA_SHA1_WITH_SHA1_HMAC
          case CKM_PBA_SHA1_WITH_SHA1_HMAC:
            name="PBA_SHA1_WITH_SHA1_HMAC"; break;
#endif
#ifdef CKM_KEY_WRAP_LYNKS
          case CKM_KEY_WRAP_LYNKS: name="KEY_WRAP_LYNKS"; break;
#endif
#ifdef CKM_KEY_WRAP_SET_OAEP
          case CKM_KEY_WRAP_SET_OAEP: name="KEY_WRAP_SET_OAEP"; break;
#endif
#ifdef CKM_SKIPJACK_KEY_GEN
          case CKM_SKIPJACK_KEY_GEN: name="SKIPJACK_KEY_GEN"; break;
#endif
#ifdef CKM_SKIPJACK_ECB64
          case CKM_SKIPJACK_ECB64: name="SKIPJACK_ECB64"; break;
#endif
#ifdef CKM_SKIPJACK_CBC64
          case CKM_SKIPJACK_CBC64: name="SKIPJACK_CBC64"; break;
#endif
#ifdef CKM_SKIPJACK_OFB64
          case CKM_SKIPJACK_OFB64: name="SKIPJACK_OFB64"; break;
#endif
#ifdef CKM_SKIPJACK_CFB64
          case CKM_SKIPJACK_CFB64: name="SKIPJACK_CFB64"; break;
#endif
#ifdef CKM_SKIPJACK_CFB32
          case CKM_SKIPJACK_CFB32: name="SKIPJACK_CFB32"; break;
#endif
#ifdef CKM_SKIPJACK_CFB16
          case CKM_SKIPJACK_CFB16: name="SKIPJACK_CFB16"; break;
#endif
#ifdef CKM_SKIPJACK_CFB8
          case CKM_SKIPJACK_CFB8: name="SKIPJACK_CFB8"; break;
#endif
#ifdef CKM_SKIPJACK_WRAP
          case CKM_SKIPJACK_WRAP: name="SKIPJACK_WRAP"; break;
#endif
#ifdef CKM_SKIPJACK_PRIVATE_WRAP
          case CKM_SKIPJACK_PRIVATE_WRAP: name="SKIPJACK_PRIVATE_WRAP"; break;
#endif
#ifdef CKM_SKIPJACK_RELAYX
          case CKM_SKIPJACK_RELAYX: name="SKIPJACK_RELAYX"; break;
#endif
#ifdef CKM_KEA_KEY_PAIR_GEN
          case CKM_KEA_KEY_PAIR_GEN: name="KEA_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_KEA_KEY_DERIVE
          case CKM_KEA_KEY_DERIVE: name="KEA_KEY_DERIVE"; break;
#endif
#ifdef CKM_FORTEZZA_TIMESTAMP
          case CKM_FORTEZZA_TIMESTAMP: name="FORTEZZA_TIMESTAMP"; break;
#endif
#ifdef CKM_BATON_KEY_GEN
          case CKM_BATON_KEY_GEN: name="BATON_KEY_GEN"; break;
#endif
#ifdef CKM_BATON_ECB128
          case CKM_BATON_ECB128: name="BATON_ECB128"; break;
#endif
#ifdef CKM_BATON_ECB96
          case CKM_BATON_ECB96: name="BATON_ECB96"; break;
#endif
#ifdef CKM_BATON_CBC128
          case CKM_BATON_CBC128: name="BATON_CBC128"; break;
#endif
#ifdef CKM_BATON_COUNTER
          case CKM_BATON_COUNTER: name="BATON_COUNTER"; break;
#endif
#ifdef CKM_BATON_SHUFFLE
          case CKM_BATON_SHUFFLE: name="BATON_SHUFFLE"; break;
#endif
#ifdef CKM_BATON_WRAP
          case CKM_BATON_WRAP: name="BATON_WRAP"; break;
#endif
#ifdef CKM_ECDSA_KEY_PAIR_GEN
          case CKM_ECDSA_KEY_PAIR_GEN: name="ECDSA_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_EC_KEY_PAIR_GEN
            //case CKM_EC_KEY_PAIR_GEN: name="ECDSA_KEY_PAIR_GEN or
          //EC_KEY_PAIR_GEN"; break;
#endif
#ifdef CKM_ECDSA
          case CKM_ECDSA: name="ECDSA"; break;
#endif
#ifdef CKM_ECDSA_SHA1
          case CKM_ECDSA_SHA1: name="ECDSA_SHA1"; break;
#endif
#ifdef CKM_ECDH1_DERIVE
          case CKM_ECDH1_DERIVE: name="ECDH1_DERIVE"; break;
#endif
#ifdef CKM_ECDH1_COFACTOR_DERIVE
          case CKM_ECDH1_COFACTOR_DERIVE: name="ECDH1_COFACTOR_DERIVE"; break;
#endif
#ifdef CKM_ECMQV_DERIVE
          case CKM_ECMQV_DERIVE: name="ECMQV_DERIVE"; break;
#endif
#ifdef CKM_JUNIPER_KEY_GEN
          case CKM_JUNIPER_KEY_GEN: name="JUNIPER_KEY_GEN"; break;
#endif
#ifdef CKM_JUNIPER_ECB128
          case CKM_JUNIPER_ECB128: name="JUNIPER_ECB128"; break;
#endif
#ifdef CKM_JUNIPER_CBC128
          case CKM_JUNIPER_CBC128: name="JUNIPER_CBC128"; break;
#endif
#ifdef CKM_JUNIPER_COUNTER
          case CKM_JUNIPER_COUNTER: name="JUNIPER_COUNTER"; break;
#endif
#ifdef CKM_JUNIPER_SHUFFLE
          case CKM_JUNIPER_SHUFFLE: name="JUNIPER_SHUFFLE"; break;
#endif
#ifdef CKM_JUNIPER_WRAP
          case CKM_JUNIPER_WRAP: name="JUNIPER_WRAP"; break;
#endif
#ifdef CKM_FASTHASH
          case CKM_FASTHASH: name="FASTHASH"; break;
#endif
#ifdef CKM_AES_KEY_GEN
          case CKM_AES_KEY_GEN: name="AES_KEY_GEN"; break;
#endif
#ifdef CKM_AES_ECB
          case CKM_AES_ECB: name="AES_ECB"; break;
#endif
#ifdef CKM_AES_CBC
          case CKM_AES_CBC: name="AES_CBC"; break;
#endif
#ifdef CKM_AES_MAC
          case CKM_AES_MAC: name="AES_MAC"; break;
#endif
#ifdef CKM_AES_MAC_GENERAL
          case CKM_AES_MAC_GENERAL: name="AES_MAC_GENERAL"; break;
#endif
#ifdef CKM_AES_CBC_PAD
          case CKM_AES_CBC_PAD: name="AES_CBC_PAD"; break;
#endif
#ifdef CKM_DSA_PARAMETER_GEN
          case CKM_DSA_PARAMETER_GEN: name="DSA_PARAMETER_GEN"; break;
#endif
#ifdef CKM_DH_PKCS_PARAMETER_GEN
          case CKM_DH_PKCS_PARAMETER_GEN: name="DH_PKCS_PARAMETER_GEN"; break;
#endif
#ifdef CKM_X9_42_DH_PARAMETER_GEN
          case CKM_X9_42_DH_PARAMETER_GEN: name="X9_42_DH_PARAMETER_GEN"; break;
#endif
#ifdef CKM_VENDOR_DEFINED
          case CKM_VENDOR_DEFINED: name="VENDOR_DEFINED"; break;
#endif
          default: {
            std::stringstream ss;
            ss<<id;
            name=ss.str();
          }
        }
        return *this;
      }
      /// Assign information.
      MechanismInfo& operator=(const CK_MECHANISM_INFO& info) {
        minKeySize = info.ulMinKeySize;
        maxKeySize = info.ulMaxKeySize;
        flags = info.flags;
        return *this;
      }
      bool operator<(const MechanismInfo& o) const {
        return id<o.id;
      }
  };
  typedef std::set<MechanismInfo> MechanismList;

  /// Handle slot information.
  struct SlotInfo {
      FixString<64> slotDescription;
      FixString<32> manufacturerID;
      CK_FLAGS flags;
      CK_VERSION hardwareVersion;
      CK_VERSION firmwareVersion;

      SlotInfo() {
      }

      //! Convert C-Structure of Slot Information Into C++
      SlotInfo(const CK_SLOT_INFO& cInfo):
          slotDescription(cInfo.slotDescription),
          manufacturerID(cInfo.manufacturerID),
          flags(cInfo.flags),
          hardwareVersion(cInfo.hardwareVersion),
          firmwareVersion(cInfo.firmwareVersion) {
      }
  };

  struct TokenInfo; // forward declaration
  inline std::ostream& operator<<(std::ostream& out, const TokenInfo& ti);

  /// Handle token information.
  struct TokenInfo {
      FixString<32> label;
      FixString<32> manufacturerID;
      FixString<16> model;
      FixString<16> serialNumber;
      CK_FLAGS flags;
      CK_ULONG maxSessionCount;
      CK_ULONG sessionCount;
      CK_ULONG maxRwSessionCount;
      CK_ULONG rwSessionCount;
      CK_ULONG maxPinLen;
      CK_ULONG minPinLen;
      CK_ULONG totalPublicMemory;
      CK_ULONG freePublicMemory;
      CK_ULONG totalPrivateMemory;
      CK_ULONG freePrivateMemory;
      CK_VERSION hardwareVersion;
      CK_VERSION firmwareVersion;
      FixString<16> utcTime;

      TokenInfo() {
        CRYPTOLOG("log");
      }
      
      //! Convert C-Structure of Token Information Into C++
      TokenInfo(const CK_TOKEN_INFO& cInfo):
          label(cInfo.label),
          manufacturerID(cInfo.manufacturerID),
          model(cInfo.model),
          serialNumber(cInfo.serialNumber),
          flags(cInfo.flags),
          maxSessionCount(cInfo.ulMaxSessionCount),
          sessionCount(cInfo.ulSessionCount),
          maxRwSessionCount(cInfo.ulMaxRwSessionCount),
          rwSessionCount(cInfo.ulRwSessionCount),
          maxPinLen(cInfo.ulMaxPinLen),
          minPinLen(cInfo.ulMinPinLen),
          totalPublicMemory(cInfo.ulTotalPublicMemory),
          freePublicMemory(cInfo.ulFreePublicMemory),
          totalPrivateMemory(cInfo.ulTotalPrivateMemory),
          freePrivateMemory(cInfo.ulFreePrivateMemory),
          hardwareVersion(cInfo.hardwareVersion),
          firmwareVersion(cInfo.firmwareVersion),
          utcTime(cInfo.utcTime) {
        CRYPTOLOG("log *this={"<<*this<<'}');
      }

  };

  /// Textual representation for token information.
  inline std::ostream& operator<<(std::ostream& out, const TokenInfo& ti) {
    return out
      <<"label="<<ti.label<<std::endl
      <<"manufacturerID="<<ti.manufacturerID<<std::endl
      <<"model="<<ti.model<<std::endl
      <<"serialNumber="<<ti.serialNumber<<std::endl
      <<"flags="<<ti.flags<<std::endl
      <<"maxSessionCount="<<ti.maxSessionCount<<std::endl
      <<"sessionCount="<<ti.sessionCount<<std::endl
      <<"maxRwSessionCount="<<ti.maxRwSessionCount<<std::endl
      <<"rwSessionCount="<<ti.rwSessionCount<<std::endl
      <<"maxPinLen="<<ti.maxPinLen<<std::endl
      <<"minPinLen="<<ti.minPinLen<<std::endl
      <<"totalPublicMemory="<<ti.totalPublicMemory<<std::endl
      <<"freePublicMemory="<<ti.freePublicMemory<<std::endl
      <<"totalPrivateMemory="<<ti.totalPrivateMemory<<std::endl
      <<"freePrivateMemory="<<ti.freePrivateMemory<<std::endl
      <<"hardwareVersion="<<ti.hardwareVersion.major<<'.'
      <<ti.hardwareVersion.minor<<std::endl
      <<"firmwareVersion="<<ti.firmwareVersion.major<<'.'
      <<ti.firmwareVersion.minor<<std::endl
      <<"utcTime="<<ti.utcTime;
  }

  /// Handle library information.
  struct Info {
      CK_VERSION cryptokiVersion;
      FixString<32> manufacturerID;
      CK_FLAGS flags;
      FixString<32> libraryDescription;
      CK_VERSION libraryVersion;
  };

  //@}

  /*! @addtogroup cryptokilib */
  //@{

  /// Load Cryptoki Library for use with Smart Card.
  /** This is your starting point when accessing tokens through
      PKCS#11. Get an instance using Library::Library. */
  class Library {

      friend class Slot;

    public:

      //! Initialize for a given library
      /*! @note Do not instanciate more than one Library instance per
          shared library. Normally you need only one instance.
          
          @param library name of the shared library that supports pkcs#11
          @param exceptions whether exceptions should be thrown */
      Library(const std::string& library, bool exceptions=true):
          _init(new Init(library, exceptions)) {
        CRYPTOLOG("log");
      }

    protected:
      
      Library() {
        CRYPTOLOG("log");
      }
      
    private:

      /// Initiatlize the library: load and unload the shared object.
      class Init {

        private:

          friend class Library;
        
          bool _exc;
          CK_RV _res;
          CK_FUNCTION_LIST* _fn;
#ifndef WIN32
          void* _lib;
#else
          HINSTANCE _lib;
#endif
      
          //! Initialize Funcion List for this Instance
          bool functionList(const std::string& library);
          
          bool check(CK_RV result, const std::string& context="");
          
          /*! @return error text of last cryptoki call */
          std::string error(CK_RV res);
          
        public:

          /// Called from Library::Library
          Init(const std::string& library="opensc-pkcs11.so", bool exc=true);
          
          ~Init();
          
          Init& reset() {
            check(_fn->C_Finalize(0), CRYPTOKI_FN_LOG("C_Finalize"));
            check(_fn->C_Initialize(0), CRYPTOKI_FN_LOG("C_Initialize"));
            return *this;
          }

          CK_FUNCTION_LIST* fn() {
            return _fn;
          }
          
          /*! @name                                        C Like Error Handling
              
              You are strongly recommended not to disable exception
              handling. If you disable it, you must check after every
              operation whether it was successful or not. These methods
              provide all you need for that. */
          //@{
          
          /*! @return @c true if last cryptoki on this object call was
              successful */
          operator bool();
          
          /*! @return error text of last cryptoki call */
          std::string error();
          
          //@}
          
      };

    public:

      /// Get pointer to cryptoki API functions.
      /** Used internally, normally you shouldn't use this directly. */
      CK_FUNCTION_LIST* operator->() {
        return _init->fn();
      }
      
      /*! @name                                            C Like Error Handling
              
          You are strongly recommended not to disable exception
          handling. If you disable it, you must check after every
          operation whether it was successful or not. These methods
          provide all you need for that. */
      //@{

      /// @return true if exceptions are thrown in case of error
      bool exc() {
        return _init->_exc;
      }
          
      /*! @return @c true if last cryptoki on this object call was successful */
      operator bool() {
        return *_init;
      }
          
      /*! @return error text of last cryptoki call */
      std::string error() {
        return _init->error();
      }

      /// Convert @c CK_RV return code to a human readable text.
      std::string error(CK_RV res) {
        return _init->error(res);
      }

      //@}

      /// Get cryptoki library informartion.
      Info info() {
        CRYPTOLOG("log");
        Info inf;
        CK_INFO cInf;
        //! calls @c C_GetInfo
        if (!_init->check(_init->fn()
                          ->C_GetInfo(&cInf), CRYPTOKI_FN_LOG("C_GetInfo")))
          return inf;
        inf.cryptokiVersion = cInf.cryptokiVersion;
        inf.manufacturerID = cInf.manufacturerID;
        inf.flags = cInf.flags;
        inf.libraryDescription = cInf.libraryDescription;
        inf.libraryVersion = cInf.libraryVersion;
        return inf;
      }
          
      //! Get a list of available slots
      /*! @param tokenPresent whether a token must be inserted into the
          reader
          @param name if given, only return slots with a given name
          @return list of matching slots */
      SlotList slotList(bool tokenPresent=true,
                        std::string name=std::string());
      
    private:

      std::shared_ptr<Init> _init;
      
  };

  //! Slot and Token Management
  class Slot {
    private:

      friend class Library;
      friend class Session;
      friend class Object;
      
      Library _library;
      CK_SLOT_ID _slot;
      CK_RV _res;

    public:

      Slot(const Slot& o):
          _library(o._library), _slot(o._slot), _res(o._res) {
        CRYPTOLOG("ID="<<_slot);
      }

      /// Slots are created from Library::slotList.
      /** @note Empty constructor needs immediate assignment. This
          constructor is public only to be inserted to STL
          containers. */
      Slot(): _slot(0), _res(-1) {
        CRYPTOLOG("ID="<<_slot);
      }

      Slot& operator=(const Slot& o) {
        _library = o._library;
        _slot = o._slot;
        _res = o._res;
        CRYPTOLOG("ID="<<_slot);
        return *this;
      }

    private:
      
      /// Slots are created from Library::slotList.
      Slot(const Library& lib, CK_SLOT_ID slot):
          _library(lib), _slot(slot), _res(CKR_OK) {
        CRYPTOLOG("ID="<<_slot);
      }

      bool check(CK_RV result, const std::string& context="") {
        _res = result;
        if (_library.exc() && !*this) {
          if (!context.empty()) {
            throw access_error(context+": "+error());
          } else {
            throw access_error(error());
          }
        }
        return _res==CKR_OK;
      }

    public:

      /*! @name                                            C Like Error Handling

          You are strongly recommended not to disable exception
          handling. If you disable it, you must check after every
          operation whether it was successful or not. These methods
          provide all you need for that. */
      //@{
      
      /*! @return @c true if last cryptoki on this object call was successful */
      operator bool() {
        return _res==CKR_OK;
      }

      /*! @return error text of last cryptoki call */
      std::string error() {
        return _library.error(_res);
      }

      //@}

      /// Access to the Library.
      Library& library() {
        return _library;
      }

      /// Get the Slot's MechanismInfo given a @c CK_MECHANISM_TYPE
      /** Used internally by mechanismlist(). */
      MechanismInfo mechanisminfo(CK_MECHANISM_TYPE mechanism) {
        CRYPTOLOG("log");
        CK_MECHANISM_INFO info;
        //! calls @c C_GetMechanismInfo
        check(_library->C_GetMechanismInfo(_slot, mechanism, &info),
              CRYPTOKI_FN_LOG("C_GetMechanismInfo"));
        return MechanismInfo(mechanism, info);
      }

      /// Get a list of the Slot's mechanisms.
      MechanismList mechanismlist() {
        CRYPTOLOG("log");
        MechanismList res;
        CK_ULONG count(0);
        //! calls @c C_GetMechanismList
        if (!check(_library->C_GetMechanismList(_slot, 0, &count),
                   CRYPTOKI_FN_LOG("C_GetMechanismList")) || !count) return res;
        CK_MECHANISM_TYPE* mechanisms = 0;
        try {
          mechanisms = new CK_MECHANISM_TYPE[count];
          if (!check(_library->C_GetMechanismList(_slot, mechanisms, &count),
                     CRYPTOKI_FN_LOG("C_GetMechanismList"))) {
            delete[] mechanisms;
            return res;
          }
          for (CK_ULONG i(0); i<count; ++i)
            res.insert(mechanisminfo(mechanisms[i]));
        } catch (...) {
          delete[] mechanisms;
          throw;
        }
          delete[] mechanisms;
          return res;
      }

      //! Read Slot Information
      SlotInfo slotinfo() {
        CRYPTOLOG("log");
        CK_SLOT_INFO cInfo;
        //! calls @c C_GetSlotInfo
        if (!check(_library->C_GetSlotInfo(_slot, &cInfo),
                   CRYPTOKI_FN_LOG("C_GetSlotInfo")))
          return SlotInfo();
        return SlotInfo(cInfo);
      }

      //! Read Token Information
      TokenInfo tokeninfo() {
        CRYPTOLOG("log");
        //! calls @c C_GetTokenInfo
        CK_TOKEN_INFO cInfo;
        if (!check(_library->C_GetTokenInfo(_slot, &cInfo),
                   CRYPTOKI_FN_LOG("C_GetTokenInfo")))
          return TokenInfo();
        return TokenInfo(cInfo);
      }

      /// Initialize a token.
      /** If the token has not been initialized (i.e. new from the
          factory), then the @c pin parameter becomes the initial value
          of the SO PIN. If the token is being reinitialized, the @c pin
          parameter is checked against the existing SO PIN to
          authorize the initialization operation. In both cases, the
          SO PIN is the value @c pin after the function completes
          successfully. If the SO PIN is lost, then the card must be
          reinitialized using a mechanism outside the scope of this
          standard. The CKF_TOKEN_INITIALIZED flag in the
          CK_TOKEN_INFO structure indicates the action that will
          result from calling C_InitToken. If set, the token will be
          reinitialized, and the client must supply the existing SO
          password in @c pin.

          When a token is initialized, all objects that can be
          destroyed are destroyed (i.e., all except for
          “indestructible” objects such as keys built into the
          token). Also, access by the normal user is disabled until
          the SO sets the normal user’s PIN. Depending on the token,
          some “default” objects may be created, and attributes of
          some objects may be set to default values.

          If the token has a “protected authentication path”, as
          indicated by the CKF_PROTECTED_AUTHENTICATION_PATH flag in
          its CK_TOKEN_INFO being set, then that means that there is
          some way for a user to be authenticated to the token without
          having the application send a PIN through the Cryptoki
          library. One such possibility is that the user enters a PIN
          on a PINpad on the token itself, or on the slot device. To
          initialize a token with such a protected authentication
          path, the @c pin parameter to C_InitToken should be empty.
          During the execution of C_InitToken, the SO’s PIN will be
          entered through the protected authentication path.

          If the token has a protected authentication path other than
          a PINpad, then it is token- dependent whether or not
          C_InitToken can be used to initialize the token.

          A token cannot be initialized if Cryptoki detects that any
          application has an open session with it; when a call to
          C_InitToken is made under such circumstances, the call fails
          with error CKR_SESSION_EXISTS. Unfortunately, it may happen
          when C_InitToken is called that some other application does
          have an open session with the token, but Cryptoki cannot
          detect this, because it cannot detect anything about other
          applications using the token. If this is the case, then the
          consequences of the C_InitToken call are undefined.

          The C_InitToken function may not be sufficient to properly
          initialize complex tokens. In these situations, an
          initialization mechanism outside the scope of Cryptoki must
          be employed. The definition of “complex token” is product
          specific.

          @param pin SO's initial PIN
          @param label label of the token */
      bool inittoken(std::string pin, FixString<32> label) {
        CRYPTOLOG("log");
        //! calls @c C_InitToken
        std::string in(label);
        in.resize(32, ' ');
        if (pin.size())
          return check(_library->C_InitToken
                       (_slot,
                        (unsigned char*)&pin[0], pin.size(),
                        (unsigned char*)&in[0]),
                       CRYPTOKI_FN_LOG("C_InitToken"));
        else
          return check(_library->C_InitToken
                       (_slot,
                        0, 0, // pin from external pin pad
                        (unsigned char*)&in[0]),
                       CRYPTOKI_FN_LOG("C_InitToken"));
      }

      /*! @todo Not implemented:
          @code
      class SlotEventListener {
        public: virtual void slotEvent() = 0;
      }

      bool registerforslotevent(SlotEventListener&) {
          CRYPTOLOG("log");
        //! calls @c C_WaitForSlotEvent
        return check(_library->C_WaitForSlotEvent(CK_FLAGS, &_slot, 0),
                     CRYPTOKI_FN_LOG("C_WaitForSlotEvent"));
      }
          @endcode */
  };

  /// Session Management
  /** @note Not implemented: CK_RV C_CloseAllSessions(CK_SLOT_ID); */
  class Session {
    private:

      friend class Login;
      friend class Object;

      typedef std::multimap<CK_SLOT_ID, CK_SESSION_HANDLE> Slots;

      Slot _slot;
      CK_SESSION_HANDLE _session;
      CK_RV _res;

      static Slots& slots() {
        static Slots _slots;
        return _slots;
      }

      Session(); // forbidden

      bool check(CK_RV result, const std::string& context="") {
        _res = result;
        if (_slot.library().exc() && !*this) {
          if (!context.empty()) {
            throw access_error(context+": "+error());
          } else {
            throw access_error(error());
          }
        }
        return _res==CKR_OK;
      }

      //! calls @c C_OpenSession if it's the first session
      void open(bool rw=false) {
        CRYPTOLOG("references: "<<slots().count(_slot._slot));
        if (slots().count(_slot._slot)==0) {
          check(_slot.library()->C_OpenSession
                (_slot._slot, CKF_SERIAL_SESSION|(rw?CKF_RW_SESSION:0),
                 0, 0, &_session),
                CRYPTOKI_FN_LOG("C_OpenSession"));
        } else {
          _session = slots().find(_slot._slot)->second;
        }
        slots().insert(std::make_pair(_slot._slot, _session));
      }

      //! calls @c C_CloseSession if it's the last session
      void close() {
        CRYPTOLOG("references: "<<slots().count(_slot._slot));
        if (slots().count(_slot._slot)==1) {
          slots().erase(slots().find(_slot._slot));
          check(_slot.library()->C_CloseSession(_session),
                CRYPTOKI_FN_LOG("C_CloseSession"));
        } else {
          slots().erase(slots().find(_slot._slot));
        }
        _session=0;
      }
      
    public:

      //! Opens a new session.
      /*! @param slot slot to open a session on
          @param rw whether session is read/write or read only*/
      Session(const Slot& slot, bool rw=false):
          _slot(slot), _session(0), _res(CKR_OK) {
        CRYPTOLOG("log");
        open(rw);
        //! @todo pass parameter
      }

      //! Copy session.
      Session(const Session& o):
          _slot(o._slot), _session(o._session), _res(CKR_OK) {
        CRYPTOLOG("log");
        slots().insert(std::make_pair(_slot._slot, _session));
        //! @todo pass parameter
      }

      ~Session() try {
        /// ignore failure in disconnect,  possibly smartcard has
        /// been removed
        try {
          logout();
        } catch (const std::exception& x) {
          CRYPTOLOG("caught: "<<x.what());
          close();
        } catch (...) {
          CRYPTOLOG("caught");
          close();
        }
        close();
      } catch (const std::exception& x) {
        CRYPTOLOG("caught: "<<x.what());
      } catch (...) {
        CRYPTOLOG("caught");
      }
      
      /*! @name                                               Comfortable Access

          Use these methods in favour of the Low Level Cryptoki
          Functions. They provide a higher level simpler access. */
      //@{

      //! Get a list of matching objects.
      ObjectList find(const AttributeList& attrs=AttributeList());

      //! Get a list of matching objects.
      ObjectList find(const Attribute& a);
      //! Get a list of matching objects.
      ObjectList find(const Attribute& a1, const Attribute& a2);

      //! Create a new Certificate Object.
      Object create(const std::string& label, const openssl::X509& cert);
      //! Create a new PrivateKey Object.
      Object create(const std::string& label, const openssl::PrivateKey& key,
                    const openssl::X509& cert);
      
      //@}
      
      /*! @name                                            C Like Error Handling

          You are strongly recommended not to disable exception
          handling. If you disable it, you must check after every
          operation whether it was successful or not. These methods
          provide all you need for that. */
      //@{
      
      /*! @return @c true if last cryptoki on this object call was successful */
      operator bool() {
        return _res==CKR_OK;
      }

      /*! @return error text of last cryptoki call */
      std::string error() {
        return _slot.library().error(_res);
      }

      //@}

      /*! @name                                     Low Level Cryptoki Functions

          Direct access to the low level cryptoki API. Better use the
          comfort methods. */
      //@{
      
      bool cancel() {
        CRYPTOLOG("log");
        //! calls @c C_CancelFunction
        return check(_slot.library()->C_CancelFunction(_session),
                     CRYPTOKI_FN_LOG("C_CancelFunction"));
      }

      //! Create a new object.
      Object create(const AttributeList& attrs);

      std::string digest(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_Digest
        check(_slot.library()->C_Digest
              (_session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_Digest"));
        res.resize(size);
        return res;
      }

      std::string digestencryptupdate(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_DigestEncryptUpdate
        check(_slot.library()->C_DigestEncryptUpdate
              (_session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_DigestEncryptUpdate"));
        res.resize(size);
        return res;
      }

      /*! @todo Not implemented:
          @code
      bool digestfinal() {
          CRYPTOLOG("log");
        //! calls @c C_DigestFinal
        return check(_slot.library()->C_DigestFinal(_session, CK_BYTE_PTR, CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_DigestFinal"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool digestinit() {
          CRYPTOLOG("log");
        //! calls @c C_DigestInit
        return check(_slot.library()->C_DigestInit(_session, CK_MECHANISM_PTR),
                     CRYPTOKI_FN_LOG("C_DigestInit"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool digestupdate() {
          CRYPTOLOG("log");
        //! calls @c C_DigestUpdate
        return check(_slot.library()->C_DigestUpdate(_session, CK_BYTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_DigestUpdate"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool findobjectsfinal() {
          CRYPTOLOG("log");
        //! calls @c C_FindObjectsFinal
        return check(_slot.library()->C_FindObjectsFinal(_session),
                     CRYPTOKI_FN_LOG("C_FindObjectsFinal"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool findobjectsinit() {
          CRYPTOLOG("log");
        //! calls @c C_FindObjectsInit
        return check(_slot.library()->C_FindObjectsInit(_session, CK_ATTRIBUTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_FindObjectsInit"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool findobjects() {
          CRYPTOLOG("log");
        //! calls @c C_FindObjects
        return check(_session->_slot.library()->C_FindObjects(_session, CK_OBJECT_HANDLE_PTR, CK_ULONG,
                                   CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_FindObjects"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool generaterandom() {
          CRYPTOLOG("log");
        //! calls @c C_GenerateRandom
        return check(_slot.library()->C_GenerateRandom(_session, CK_BYTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_GenerateRandom"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool getfunctionstatus() {
          CRYPTOLOG("log");
        //! calls @c C_GetFunctionStatus
        return check(_slot.library()->C_GetFunctionStatus(_session),
                     CRYPTOKI_FN_LOG("C_GetFunctionStatus"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool getoperationstate() {
          CRYPTOLOG("log");
        //! calls @c C_GetOperationState
        return check(_slot.library()->C_GetOperationState(_session, CK_BYTE_PTR, CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_GetOperationState"));
      }
          @endcode */

      /** definition of session info:
          
          @code
          struct CK_SESSION_INFO {
            CK_SLOT_ID slotID;
            CK_STATE state;
            CK_FLAGS flags;
            CK_ULONG ulDeviceError;
          };
          @endcode
          
          where:
           - @c slotID ID of the slot that interfaces with the token
           - @c state The state of the session
             - @c 0 @c CKS_RO_PUBLIC_SESSION
             - @c 1 @c CKS_RO_USER_FUNCTIONS
             - @c 2 @c CKS_RW_PUBLIC_SESSION 
             - @c 3 @c CKS_RW_USER_FUNCTIONS
             - @c 4 @c CKS_RW_SO_FUNCTIONS
           - @c flags Bit flags that define the type of session; the
             flags are defined as:
             - @c CKF_RW_SESSION
               - True if the session is read/write.
               - False if the session is read only.
             - @c CKF_SERIAL_SESSION Deprecated, always true.
          - @c ulDeviceError An error code defined by the
            cryptographic device. Used for errors not covered by
          Cryptoki. */
      struct Info: public  CK_SESSION_INFO {
          Info(const CK_SESSION_INFO& si): CK_SESSION_INFO(si) {
          }
          bool readonly() {
            return !readwrite();
          }
          bool readwrite() {
            return flags|CKF_RW_SESSION;
          }
          std::string stateString() {
            switch (state) {
              case 0: return "CKS_RO_PUBLIC_SESSION";
              case 1: return "CKS_RO_USER_FUNCTIONS";
              case 2: return "CKS_RW_PUBLIC_SESSION ";
              case 3: return "CKS_RW_USER_FUNCTIONS";
              case 4: return "CKS_RW_SO_FUNCTIONS";
              default: return "<UNKNOWN>";
            }
          }
      };

          
      /** @return session information */
      Info getsessioninfo() {
        CRYPTOLOG("log");
        CK_SESSION_INFO info;
        //! calls @c C_GetSessionInfo
        check(_slot.library()->C_GetSessionInfo(_session, &info),
              CRYPTOKI_FN_LOG("C_GetSessionInfo"));
        return info;
      }
      
      /*! @todo Not implemented:
          @code
      bool seedrandom() {
          CRYPTOLOG("log");
        //! calls @c C_SeedRandom
        return check(_slot.library()->C_SeedRandom(_session, CK_BYTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_SeedRandom"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool setpin() {
          CRYPTOLOG("log");
        //! calls @c C_SetPIN
        return check(_slot.library()->C_SetPIN(_session, CK_CHAR_PTR, CK_ULONG, CK_CHAR_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_SetPIN"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool initpin() {
          CRYPTOLOG("log");
        //! calls @c C_InitPIN
        return check(_slot.library()->C_InitPIN(_session, CK_CHAR_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_InitPIN"));
      }
          @endcode */

      //@}

      /** @name                                                   login with pin

          Unlock access with pin (login) and unlock after use (logout). */
      //@{
      
    private:
      
      class Login {
          
        public:
          
          Login(Session& session,
                const std::string& pin,
                CK_USER_TYPE userType=CKU_USER): _session(session) {
            CRYPTOLOG("log");
            //! calls @c C_Login
            try {
              _session.check(_session._slot.library()->C_Login
                             (_session._session, userType,
                              const_cast<CK_CHAR*>((const CK_CHAR*)pin.c_str()),
                              (int)pin.size()),
                             CRYPTOKI_FN_LOG("C_Login"));
            } catch (std::exception& x) {
              throw wrong_pin(x.what());
            }
          }

          ~Login() {
            /// ignore failure in disconnect,  possibly smartcard has
            /// been removed
            try {
              //! calls @c C_Logout
              _session.check(_session._slot.library()->C_Logout
                             (_session._session),
                             CRYPTOKI_FN_LOG("C_Logout"));
            } catch (const std::exception& x) {
              CRYPTOLOG("ERROR during error cleanup: "<<x.what());
            } catch (...) {
              CRYPTOLOG("ERROR during error cleanup.");
            }
          }
          
        private:
          
          Session& _session;
          
      };

    public:

      /// Login to card
      /** @param pin to unlock card
          @param userType user type */
      void login(const std::string& pin,
                 CK_USER_TYPE userType=CKU_USER) {
        CRYPTOLOG("log");
        _login = std::shared_ptr<Login>(new Login(*this, pin, userType));
      }

      /// Logout from card
      /** Undo the last login. */
      void logout() {
        CRYPTOLOG("log");
        _login.reset();
       }

       std::shared_ptr<Login> _login;

      //@}

  };

  class Object {

    private:

      friend class Session;

      CK_OBJECT_HANDLE _object;
      Session _session;
      CK_RV _res;

      bool check(CK_RV result, const std::string& context="") {
        _res = result;
        if (_session._slot.library().exc() && !*this) {
          if (!context.empty()) {
            throw access_error(context+": "+error());
          } else {
            throw access_error(error());
          }
        }
        return _res==CKR_OK;
      }

      Object(); // forbidden
      
      Object(const Session& session, CK_OBJECT_HANDLE obj):
          _object(obj), _session(session), _res(CKR_OK) {
        CRYPTOLOG("log");
      }

    public:

      /*! @name                                               Comfortable Access

          Use these methods in favour of the Low Level Cryptoki
          Functions. They provide a higher level simpler access. */
      //@{

      std::string encrypt(const std::string& data, CK_MECHANISM_TYPE type,
                          const std::string& param=std::string()) {
        CRYPTOLOG("log");
        CRYPTOLOG("encryptinit");
        encryptinit(type, param);
        CRYPTOLOG("encrypt");
        return encrypt(data);
        //! @todo don't call encryptfinal()?
      }

      std::string decrypt(const std::string& data, CK_MECHANISM_TYPE type,
                          const std::string& param=std::string()) {
        CRYPTOLOG("log");
        CRYPTOLOG("decryptinit");
        decryptinit(type, param);
        CRYPTOLOG("decrypt");
        return decrypt(data);
        //! @todo don't call decryptfinal()?
      }

      std::string sign(const std::string& data, CK_MECHANISM_TYPE type,
                       const std::string& param=std::string()) {
        CRYPTOLOG("log");
        CRYPTOLOG("signinit");
        signinit(type, param);
        CRYPTOLOG("sign");
        return sign(data);
        //! @todo don't call signfinal()?
      }

      bool verify(const std::string& data, const std::string& signature,
                  CK_MECHANISM_TYPE type,
                  const std::string& param=std::string()) {
        CRYPTOLOG("log");
        CRYPTOLOG("verifyinit");
        verifyinit(type, param);
        CRYPTOLOG("verify");
        return verify(data, signature);
        //! @todo don't call verifyfinal()?
      }

      bool destroy() {
        CRYPTOLOG("log");
        //! calls @c C_DestroyObject
        return check(_session._slot.library()->C_DestroyObject
                     (_session._session, _object),
                     CRYPTOKI_FN_LOG("C_DestroyObject"));
      }
      
      //! Get a Single Attribute
      Attribute operator[](CK_ATTRIBUTE_TYPE a) {
        CRYPTOLOG("log");
        return attribute(a);
      }

      //! Get a Single Attribute
      Attribute attribute(CK_ATTRIBUTE_TYPE a) {
        CRYPTOLOG("log");
        Attribute res;
        CK_ATTRIBUTE attr;
        attr.type = a;
        attr.pValue = 0;
        attr.ulValueLen = 0;
        //! calls @c C_GetAttributeValue
        if (!check(_session._slot.library()->C_GetAttributeValue
                   (_session._session, _object, &attr, 1),
                   CRYPTOKI_FN_LOG("C_GetAttributeValue"))
            || !((long)attr.ulValueLen>0l))
           //! Without exception handling, size and type must be checked too.
          return res;
        try {
          attr.pValue = malloc(attr.ulValueLen);
          attr.pValue = memset(attr.pValue, 0, attr.ulValueLen);
          if (check(_session._slot.library()->C_GetAttributeValue
                    (_session._session, _object, &attr, 1),
                    CRYPTOKI_FN_LOG("C_GetAttributeValue")))
            /*! @todo There's no @c CKA_WRAP_TEMPLATE in Open
                      Cryptoki. From the Specs: «In the special case
                      of an attribute whose value is an array of
                      attributes, for example CKA_WRAP_TEMPLATE, where
                      it is passed in with pValue not NULL, then if
                      the pValue of elements within the array is
                      NULL_PTR then the ulValueLen of elements within
                      the array will be set to the required length. If
                      the pValue of elements within the array is not
                      NULL_PTR, then the ulValueLen element of
                      attributes within the array must reflect the
                      space that the corresponding pValue points to,
                      and pValue is filled in if there is sufficient
                      room. Therefore it is important to initialize
                      the contents of a buffer before calling
                      C_GetAttributeValue to get such an array
                      value. If any ulValueLen within the array isn't
                      large enough, it will be set to -1 and the
                      function will return CKR_BUFFER_TOO_SMALL, as it
                      does if an attribute in the pTemplate argument
                      has ulValueLen too small. Note that any
                      attribute whose value is an array of attributes
                      is identifiable by virtue of the attribute type
                      having the CKF_ARRAY_ATTRIBUTE bit set.» */
            res = Attribute(attr);
          else
            free(attr.pValue);
        } catch (...) {
          free(attr.pValue);
          throw;
        }
        return res;
      }

      //! Get a List of Attributes.
      /*! If @c attrs is empty, all available attributes are
          returned. Attributes that cannot be accessed or that are not
          available in this Object won't be in the result map. There
          is no exception in this case. */
      AttributeMap attributes(AttributeTypeList attrs
                              = AttributeTypeList()) {
        CRYPTOLOG("log");
        AttributeMap res;
         //! Gets all attributes, if @c attrs is empty
        if (attrs.empty()) {
          attrs.push_back(CKA_CLASS);
          attrs.push_back(CKA_TOKEN);
          attrs.push_back(CKA_PRIVATE);
          attrs.push_back(CKA_LABEL);
          attrs.push_back(CKA_APPLICATION);
          attrs.push_back(CKA_VALUE);
          attrs.push_back(CKA_OBJECT_ID);
          attrs.push_back(CKA_CERTIFICATE_TYPE);
          attrs.push_back(CKA_ISSUER);
          attrs.push_back(CKA_SERIAL_NUMBER);
          attrs.push_back(CKA_AC_ISSUER);
          attrs.push_back(CKA_OWNER);
          attrs.push_back(CKA_ATTR_TYPES);
          attrs.push_back(CKA_TRUSTED);
          attrs.push_back(CKA_KEY_TYPE);
          attrs.push_back(CKA_SUBJECT);
          attrs.push_back(CKA_ID);
          attrs.push_back(CKA_SENSITIVE);
          attrs.push_back(CKA_ENCRYPT);
          attrs.push_back(CKA_DECRYPT);
          attrs.push_back(CKA_WRAP);
          attrs.push_back(CKA_UNWRAP);
          attrs.push_back(CKA_SIGN);
          attrs.push_back(CKA_SIGN_RECOVER);
          attrs.push_back(CKA_VERIFY);
          attrs.push_back(CKA_VERIFY_RECOVER);
          attrs.push_back(CKA_DERIVE);
          attrs.push_back(CKA_START_DATE);
          attrs.push_back(CKA_END_DATE);
          attrs.push_back(CKA_MODULUS);
          attrs.push_back(CKA_MODULUS_BITS);
          attrs.push_back(CKA_PUBLIC_EXPONENT);
          attrs.push_back(CKA_PRIVATE_EXPONENT);
          attrs.push_back(CKA_PRIME_1);
          attrs.push_back(CKA_PRIME_2);
          attrs.push_back(CKA_EXPONENT_1);
          attrs.push_back(CKA_EXPONENT_2);
          attrs.push_back(CKA_COEFFICIENT);
          attrs.push_back(CKA_PRIME);
          attrs.push_back(CKA_SUBPRIME);
          attrs.push_back(CKA_BASE);
          attrs.push_back(CKA_PRIME_BITS);
          //attrs.push_back(CKA_SUBPRIME_BITS);
          attrs.push_back(CKA_VALUE_BITS);
          attrs.push_back(CKA_VALUE_LEN);
          attrs.push_back(CKA_EXTRACTABLE);
          attrs.push_back(CKA_LOCAL);
          attrs.push_back(CKA_NEVER_EXTRACTABLE);
          attrs.push_back(CKA_ALWAYS_SENSITIVE);
          attrs.push_back(CKA_KEY_GEN_MECHANISM);
          attrs.push_back(CKA_MODIFIABLE);
          attrs.push_back(CKA_ECDSA_PARAMS);
          attrs.push_back(CKA_EC_PARAMS);
          attrs.push_back(CKA_EC_POINT);
          attrs.push_back(CKA_SECONDARY_AUTH);
          attrs.push_back(CKA_AUTH_PIN_FLAGS);
          attrs.push_back(CKA_HW_FEATURE_TYPE);
          attrs.push_back(CKA_RESET_ON_INIT);
          attrs.push_back(CKA_HAS_RESET);
          attrs.push_back(CKA_VENDOR_DEFINED);
          //attrs.push_back(CKA_IBM_OPAQUE);
        }
        for (AttributeTypeList::const_iterator it(attrs.begin());
             it!=attrs.end(); ++it) {
          CK_ATTRIBUTE attr;
          attr.type = *it;
          attr.pValue = 0;
          attr.ulValueLen = 0;
          try {
            //! calls @c C_GetAttributeValue
            if (_session._slot.library()->C_GetAttributeValue
                (_session._session, _object, &attr, 1)
                == CKR_ATTRIBUTE_TYPE_INVALID
                || _res == CKR_ATTRIBUTE_SENSITIVE) {
              continue; //! Ignores unsupported Attributes.
            } else {
              check(_res, CRYPTOKI_FN_LOG("C_GetAttributeValue"));
              if ((long)attr.ulValueLen>0l) {
                attr.pValue = malloc(attr.ulValueLen);
                attr.pValue = memset(attr.pValue, 0, attr.ulValueLen);
                if (check(_session._slot.library()->C_GetAttributeValue
                          (_session._session, _object, &attr, 1),
                          CRYPTOKI_FN_LOG("C_GetAttributeValue")))
                  /*! @todo There's no @c CKA_WRAP_TEMPLATE in Open
                            Cryptoki. From the Specs: «In the special
                            case of an attribute whose value is an
                            array of attributes, for example
                            CKA_WRAP_TEMPLATE, where it is passed in
                            with pValue not NULL, then if the pValue
                            of elements within the array is NULL_PTR
                            then the ulValueLen of elements within the
                            array will be set to the required
                            length. If the pValue of elements within
                            the array is not NULL_PTR, then the
                            ulValueLen element of attributes within
                            the array must reflect the space that the
                            corresponding pValue points to, and pValue
                            is filled in if there is sufficient
                            room. Therefore it is important to
                            initialize the contents of a buffer before
                            calling C_GetAttributeValue to get such an
                            array value. If any ulValueLen within the
                            array isn't large enough, it will be set
                            to -1 and the function will return
                            CKR_BUFFER_TOO_SMALL, as it does if an
                            attribute in the pTemplate argument has
                            ulValueLen too small. Note that any
                            attribute whose value is an array of
                            attributes is identifiable by virtue of
                            the attribute type having the
                            CKF_ARRAY_ATTRIBUTE bit set.» */
                  res.insert(std::make_pair(attr.type, Attribute(attr)));
                else
                  free(attr.pValue);
              } else if (*it==CKA_MODULUS && attr.ulValueLen==0) {
                /*! @bug This is a bug in opensc-pkcs11.so: If
                         @c CKA_MODULUS has a size of 0 bytes, the
                         following query to @c CKA_MODULUS_BITS ends
                         in a segmentation fault.
                    
                    @note @c CKA_MODULUS @b must immediately be
                          followed by @c CKA_MODULUS_BITS in the
                          attribute list, because if the size of @c
                          CKA_MODULUS is 0 Bytes, the following
                          attribute query is skipped as a work around
                          to this bug. */
                if (++it==attrs.end()) break;
              }
            }
          } catch (...) {
            free(attr.pValue);
            throw;
          }
        }
        return res;
      }

      //@}
      
      /*! @name                                            C Like Error Handling

          You are strongly recommended not to disable exception
          handling. If you disable it, you must check after every
          operation whether it was successful or not. These methods
          provide all you need for that. */
      //@{
      
      /*! @return @c true if last cryptoki on this object call was successful */
      operator bool() {
        return _res==CKR_OK;
      }

      /*! @return error text of last cryptoki call */
      std::string error() {
        return _session._slot.library().error(_res);
      }      

      //@}
      
      /*! @name                                     Low Level Cryptoki Functions

          Direct access to the low level cryptoki API. Better use the
          comfort methods. */
      //@{
      
      /*! @todo Not implemented:
          @code
      bool copyobject() {
          CRYPTOLOG("log");
        //! calls @c C_CopyObject
        return check(_session._slot.library()->C_CopyObject(_session._session, CK_OBJECT_HANDLE,
                                  CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR),
                     CRYPTOKI_FN_LOG("C_CopyObject"));
      }
          @endcode */
      
      bool decryptinit(CK_MECHANISM_TYPE type, std::string param) {
        CRYPTOLOG("log");
        CK_MECHANISM mech = {
          type, param.size()?&param[0]:0, (CK_ULONG)param.size()
        };
        CRYPTOLOG("decryptinit: type="<<type<<"; mech=("<<mech.mechanism
                     <<", "<<mech.pParameter<<", "<<mech.ulParameterLen<<')');
        //! calls @c C_DecryptInit
        return check(_session._slot.library()->C_DecryptInit
                     (_session._session, &mech, _object),
                     CRYPTOKI_FN_LOG("C_DecryptInit"));
      }

      //! requires decryptinit to be called before
      std::string decrypt(const std::string& in) {
        CRYPTOLOG("log");
        std::string res;
        CK_ULONG size(0); // two calls, first to get minimum buffer length
        CRYPTOLOG("get size");
        //! calls @c C_Decrypt
        check(_session._slot.library()->C_Decrypt
              (_session._session,
               const_cast<CK_BYTE_PTR>((const unsigned char*)&in[0]),
               in.size(), 0, &size),
              CRYPTOKI_FN_LOG("C_Decrypt"));
        CRYPTOLOG("maximum size is "<<size<<"Bytes");
        res.resize(size, 0);
        check(_session._slot.library()->C_Decrypt
              (_session._session,
               const_cast<CK_BYTE_PTR>((const unsigned char*)&in[0]),
               in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_Decrypt"));
        CRYPTOLOG("exact size is "<<size<<"Bytes");
        res.resize(size);
        return res;
      }

      std::string decryptdigestupdate(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_DecryptDigestUpdate
        check(_session._slot.library()->C_DecryptDigestUpdate
              (_session._session,
               const_cast<CK_BYTE_PTR>((const unsigned char*)&in[0]), in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_DecryptDigestUpdate"));
        res.resize(size);
        return res;
      }

      bool decryptfinal() {
        CRYPTOLOG("log");
        //! calls @c C_DecryptFinal
        return check(_session._slot.library()->C_DecryptFinal
                     (_session._session, 0, 0),
              CRYPTOKI_FN_LOG("C_DecryptFinal"));
        //! @todo does this work?
      }

      std::string decryptupdate(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_DecryptUpdate
        check(_session._slot.library()->C_DecryptUpdate
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_DecryptUpdate"));
        res.resize(size);
        return res;
      }

      std::string decryptverifyupdate(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_DecryptVerifyUpdate
        check(_session._slot.library()->C_DecryptVerifyUpdate
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_DecryptVerifyUpdate"));
        res.resize(size);
        return res;
      }


      std::string sign(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        CK_ULONG size(0);
        check(_session._slot.library()->C_Sign
              (_session._session,
               (unsigned char*)&in[0], in.size(),0, &size),
              CRYPTOKI_FN_LOG("C_Sign"));
        CRYPTOLOG("maximum size is "<<size<<"Bytes");
        res.resize(size, 0);
        //! calls @c C_Sign
        check(_session._slot.library()->C_Sign
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_Sign"));
        CRYPTOLOG("exact size is "<<size<<"Bytes");
        res.resize(size);
        return res;
      }

      std::string signencryptupdate(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_SignEncryptUpdate
        check(_session._slot.library()->C_SignEncryptUpdate
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_SignEncryptUpdate"));
        res.resize(size);
        return res;
      }

      /*! @todo Not implemented:
          @code
      bool signfinal() {
          CRYPTOLOG("log");
        //! calls @c C_SignFinal
        return check(_slot.library()->C_SignFinal(_session, CK_BYTE_PTR, CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_SignFinal"));
      }
          @endcode */

      std::string signrecover(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_SignRecover
        check(_session._slot.library()->C_SignRecover
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_SignRecover"));
        res.resize(size);
        return res;
      }

      /*! @todo Not implemented:
          @code
      bool signupdate() {
          CRYPTOLOG("log");
        //! calls @c C_SignUpdate
        return check(_session._slot.library()->C_SignUpdate(_session._session, CK_BYTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_SignUpdate"));
      }
          @endcode */

      bool verify(std::string data, std::string signature) {
        CRYPTOLOG("log");
        //! calls @c C_Verify
        return check(_session._slot.library()->C_Verify
                     (_session._session,
                      (unsigned char*)&data[0], data.size(),
                      (unsigned char*)&signature[0], signature.size()),
                     CRYPTOKI_FN_LOG("C_Verify"));
      }

      /*! @todo Not implemented:
          @code
      bool verifyfinal() {
          CRYPTOLOG("log");
        //! calls @c C_VerifyFinal
        return check(_session._slot.library()->C_VerifyFinal(_session._session, CK_BYTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_VerifyFinal"));
      }
          @endcode */

      std::string verifyrecover(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_VerifyRecover
        check(_session._slot.library()->C_VerifyRecover
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_VerifyRecover"));
        res.resize(size);
        return res;
      }

      /*! @todo Not implemented:
          @code
      bool verifyupdate() {
          CRYPTOLOG("log");
        //! calls @c C_VerifyUpdate
        return check(_session._slot.library()->C_VerifyUpdate(_session._session, CK_BYTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_VerifyUpdate"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool derivekey() {
          CRYPTOLOG("log");
        //! calls @c C_DeriveKey
        return check(_session._slot.library()->C_DeriveKey(_session._session, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
                                 CK_ATTRIBUTE_PTR, CK_ULONG, CK_OBJECT_HANDLE_PTR),
                     CRYPTOKI_FN_LOG("C_DeriveKey"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool digestkey() {
          CRYPTOLOG("log");
        //! calls @c C_DigestKey
        return check(_session._slot.library()->C_DigestKey(_session._session, CK_OBJECT_HANDLE),
                     CRYPTOKI_FN_LOG("C_DigestKey"));
      }
          @endcode */

      bool encryptinit(CK_MECHANISM_TYPE type, const std::string& param) {
        CRYPTOLOG("log");
        CK_MECHANISM mech = {
          type, param.size()?const_cast<CK_VOID_PTR>((const void*)&param[0]):0,
          (CK_ULONG)param.size()
        };
        CRYPTOLOG("encryptinit: type="<<type<<"; mech=("<<mech.mechanism
                     <<", "<<mech.pParameter<<", "<<mech.ulParameterLen<<')');
        //! calls @c C_EncryptInit
        return check(_session._slot.library()->C_EncryptInit
                     (_session._session, &mech, _object),
                     CRYPTOKI_FN_LOG("C_EncryptInit"));
      }

      std::string encrypt(const std::string& in) {
        CRYPTOLOG("log");
        std::string res;
        CK_ULONG size(0); // two calls, first to get minimum buffer length
        CRYPTOLOG("get size");
        //! calls @c C_Encrypt
        check(_session._slot.library()->C_Encrypt
              (_session._session,
               const_cast<CK_BYTE_PTR>((const unsigned char*)&in[0]),
               in.size(), 0, &size),
              CRYPTOKI_FN_LOG("C_Decrypt"));
        CRYPTOLOG("maximum size is "<<size<<"Bytes");
        res.resize(size, 0);
        check(_session._slot.library()->C_Encrypt
              (_session._session,
               const_cast<CK_BYTE_PTR>((const unsigned char*)&in[0]), in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_Encrypt"));
        res.resize(size);
        return res;
      }

      /*! @todo Not implemented:
          @code
      bool encryptfinal() {
          CRYPTOLOG("log");
        //! calls @c C_EncryptFinal
        return check(_session._slot.library()->C_EncryptFinal(_session._session, CK_BYTE_PTR, CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_EncryptFinal"));
      }
          @endcode */

      std::string encryptupdate(std::string in) {
        CRYPTOLOG("log");
        std::string res;
        res.resize(in.size());
        CK_ULONG size(res.size()); //! @todo check if size is ok
        //! calls @c C_EncryptUpdate
        check(_session._slot.library()->C_EncryptUpdate
              (_session._session,
               (unsigned char*)&in[0], in.size(),
               (unsigned char*)&res[0], &size),
              CRYPTOKI_FN_LOG("C_EncryptUpdate"));
        res.resize(size);
        return res;
      }

      /*! @todo Not implemented:
          @code
      bool generatekey() {
          CRYPTOLOG("log");
        //! calls @c C_GenerateKey
        return check(_session._slot.library()->C_GenerateKey(_session._session, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR,
                                   CK_ULONG, CK_OBJECT_HANDLE_PTR),
                     CRYPTOKI_FN_LOG("C_GenerateKey"));
      }
          @endcode */


      /*! @todo Not implemented:
          @code
      bool generatekeypair() {
          CRYPTOLOG("log");
        //! calls @c C_GenerateKeyPair
        return check(_session._slot.library()->C_GenerateKeyPair(_session._session, CK_MECHANISM_PTR, CK_ATTRIBUTE_PTR,
                                       CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG,
                                       CK_OBJECT_HANDLE_PTR, CK_OBJECT_HANDLE_PTR),
                     CRYPTOKI_FN_LOG("C_GenerateKeyPair"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool getobjectsize() {
          CRYPTOLOG("log");
        //! calls @c C_GetObjectSize
        return check(_session._slot.library()->C_GetObjectSize(_session._session, CK_OBJECT_HANDLE, CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_GetObjectSize"));
      }
          @endcode */


      /*! @todo Not implemented:
          @code
      bool setattributevalue() {
          CRYPTOLOG("log");
        //! calls @c C_SetAttributeValue
        return check(_session._slot.library()->C_SetAttributeValue(_session._session, CK_OBJECT_HANDLE,
                                         CK_ATTRIBUTE_PTR, CK_ULONG),
                     CRYPTOKI_FN_LOG("C_SetAttributeValue"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool setoperationstate() {
          CRYPTOLOG("log");
        //! calls @c C_SetOperationState
        return check(_session._slot.library()->C_SetOperationState(_session._session, CK_BYTE_PTR, CK_ULONG,
                                         CK_OBJECT_HANDLE, CK_OBJECT_HANDLE),
                     CRYPTOKI_FN_LOG("C_SetOperationState"));
      }
          @endcode */

      bool signinit(CK_MECHANISM_TYPE type, std::string param) {
        CRYPTOLOG("log");
        CK_MECHANISM mech = {
          type, param.size()?&param[0]:0, (CK_ULONG)param.size()
        };
        CRYPTOLOG("signinit: type="<<type<<"; mech=("<<mech.mechanism
                     <<", "<<mech.pParameter<<", "<<mech.ulParameterLen<<')');
        //! calls @c C_SignInit
        return check(_session._slot.library()->C_SignInit
                     (_session._session, &mech, _object),
                     CRYPTOKI_FN_LOG("C_SignInit"));
      }

      /*! @todo Not implemented:
          @code
      bool signrecoverinit() {
          CRYPTOLOG("log");
        //! calls @c C_SignRecoverInit
        return check(_session._slot.library()->C_SignRecoverInit(_session._session, CK_MECHANISM_PTR, CK_OBJECT_HANDLE),
                     CRYPTOKI_FN_LOG("C_SignRecoverInit"));
      }
          @endcode */

      /*! @todo Not implemented:
          @code
      bool unwrapkey() {
          CRYPTOLOG("log");
        //! calls @c C_UnwrapKey
        return check(_session._slot.library()->C_UnwrapKey(_session._session, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
                                 CK_BYTE_PTR, CK_ULONG, CK_ATTRIBUTE_PTR, CK_ULONG,
                                 CK_OBJECT_HANDLE_PTR),
                     CRYPTOKI_FN_LOG("C_UnwrapKey"));
      }
          @endcode */

      bool verifyinit(CK_MECHANISM_TYPE type, std::string param) {
        CRYPTOLOG("log");
        CK_MECHANISM mech = {
          type, param.size()?&param[0]:0, (CK_ULONG)param.size()
        };
        CRYPTOLOG("verifyinit: type="<<type<<"; mech=("<<mech.mechanism
                     <<", "<<mech.pParameter<<", "<<mech.ulParameterLen<<')');
         //! calls @c C_VerifyInit
        return check(_session._slot.library()->C_VerifyInit
                     (_session._session, &mech, _object),
                     CRYPTOKI_FN_LOG("C_VerifyInit"));
      }

      /*! @todo Not implemented:
          @code
      bool verifyrecoverinit() {
          CRYPTOLOG("log");
        //! calls @c C_VerifyRecoverInit
        return check(_session._slot.library()->C_VerifyRecoverInit(_session._session, CK_MECHANISM_PTR, CK_OBJECT_HANDLE),
                     CRYPTOKI_FN_LOG("C_VerifyRecoverInit"));
      }
          @endcode */


      /*! @todo Not implemented:
          @code
      bool wrapkey() {
          CRYPTOLOG("log");
        //! calls @c C_WrapKey
        return check(_session._slot.library()->C_WrapKey(_session._session, CK_MECHANISM_PTR, CK_OBJECT_HANDLE,
                               CK_OBJECT_HANDLE, CK_BYTE_PTR, CK_ULONG_PTR),
                     CRYPTOKI_FN_LOG("C_WrapKey"));
      }
          @endcode */

      //@}
      
  };
  //@}

}

//! @addtogroup cryptokitypes
//@{

/// Append a cryptoki::Attribute to a cryptoki::AttributeList.
inline cryptoki::AttributeList& operator<<(cryptoki::AttributeList& list,
                                           const cryptoki::Attribute& attr) {
  CRYPTOLOG("log");
  list.push_back(attr);
  return list;
}

/// Append a cryptoki::Attribute to a new copy of a cryptoki::AttributeList.
inline cryptoki::AttributeList operator<<(const cryptoki::AttributeList& list,
                                           const cryptoki::Attribute& attr) {
  CRYPTOLOG("log");
  cryptoki::AttributeList res(list);
  res.push_back(attr);
  return res;
}

//@}

#endif
