text: /*
   * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved.
   * Use is subject to license terms specified in the COPYING file
   * distributed with the Net-SNMP package.
   */
  
  /*
   * pkcs.c
   */
  
  #include <net-snmp/net-snmp-config.h>
  #ifdef NETSNMP_USE_PKCS11
  #include <net-snmp/config_api.h>
  #include <net-snmp/library/callback.h>
  #include <net-snmp/library/keytools.h>
  #include <net-snmp/library/scapi.h>
  #include <net-snmp/library/snmp_api.h>
  #include <net-snmp/library/tools.h>
  #include <net-snmp/output_api.h>
  #include <net-snmp/types.h>
  #include <security/cryptoki.h>
  
  typedef struct netsnmp_pkcs_slot_session_s
  {
      CK_SLOT_ID sid;
      CK_SESSION_HANDLE hdl;
  } netsnmp_pkcs_slot_session;
  
  typedef struct netsnmp_pkcs_slot_info_s
  {
      int count;
      netsnmp_pkcs_slot_session* pSession;
  } netsnmp_pkcs_slot_info;
  
  static CK_RV get_session_handle(CK_MECHANISM_TYPE, CK_FLAGS,
                                  CK_SESSION_HANDLE_PTR);
  static CK_RV get_slot_session_handle(netsnmp_pkcs_slot_session*,
                                       CK_SESSION_HANDLE_PTR);
  static char* pkcserr_string(CK_RV);
  static int free_slots(int, int, void*, void*);
  
  static netsnmp_pkcs_slot_info* pSlot = NULL;
  
  /*
   * initialize the Cryptoki library.
   */
  int pkcs_init(void)
  {
      CK_RV rv;
      CK_ULONG slotcount;
      CK_SLOT_ID_PTR pSlotList = NULL;
      netsnmp_pkcs_slot_session* tmp;
      int i, rval = SNMPERR_SUCCESS;
      /* Initialize pkcs */
      if ((rv = C_Initialize(NULL)) != CKR_OK)
      {
          DEBUGMSGTL(
              ("pkcs_init", "C_Initialize failed: %s", pkcserr_string(rv)));
          return SNMPERR_SC_NOT_CONFIGURED;
      }
  
      /* Get slot count */
      rv = C_GetSlotList(1, NULL_PTR, &slotcount);
      if (rv != CKR_OK || slotcount == 0)
      {
          DEBUGMSGTL(
              ("pkcs_init", "C_GetSlotList failed: %s", pkcserr_string(rv)));
          QUITFUN(SNMPERR_GENERR, pkcs_init_quit);
      }
  
      /* Found at least one slot, allocate memory for slot list */
      pSlotList = malloc(slotcount * sizeof(CK_SLOT_ID));
      pSlot = malloc(sizeof(netsnmp_pkcs_slot_info));
      pSlot->pSession = malloc(slotcount * sizeof(netsnmp_pkcs_slot_session));
  
      if (pSlotList == NULL_PTR || pSlot == NULL_PTR ||
          pSlot->pSession == NULL_PTR)
      {
          DEBUGMSGTL(("pkcs_init", "malloc failed."));
          QUITFUN(SNMPERR_GENERR, pkcs_init_quit);
      }
  
      /* Get the list of slots */
      if ((rv = C_GetSlotList(1, pSlotList, &slotcount)) != CKR_OK)
      {
          DEBUGMSGTL(
              ("pkcs_init", "C_GetSlotList failed: %s", pkcserr_string(rv)));
          QUITFUN(SNMPERR_GENERR, pkcs_init_quit);
      }
  
      /* initialize Slots structure */
      pSlot->count = slotcount;
      for (i = 0, tmp = pSlot->pSession; i < slotcount; i++, tmp++)
      {
          tmp->sid = pSlotList[i];
          tmp->hdl = NULL;
      }
  
      snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_SHUTDOWN,
                             free_slots, NULL);
  
  pkcs_init_quit:
      SNMP_FREE(pSlotList);
      return rval;
  }
  
  /*
   * close all the opened sessions when finished with Cryptoki library.
   */
  static int free_slots(int majorID, int minorID, void* serverarg,
                        void* clientarg)
  {
      int slotcount, i;
  
      if (pSlot != NULL)
      {
          slotcount = pSlot->count;
          for (i = 0; i < slotcount; i++)
          {
              if (pSlot->pSession->hdl != NULL)
              {
                  free(pSlot->pSession->hdl);
              }
          }
          free(pSlot);
      }
  
      (void)C_Finalize(NULL);
      return 0;
  }
  
  /*
   * generate random data
   */
  int pkcs_random(u_char* buf, size_t buflen)
  {
      CK_SESSION_HANDLE hSession;
  
      if (pSlot != NULL &&
          get_slot_session_handle(pSlot->pSession, &hSession) == CKR_OK &&
          C_GenerateRandom(hSession, buf, buflen) == CKR_OK)
      {
          return SNMPERR_SUCCESS;
      }
  
      return SNMPERR_GENERR;
  }
  
  /*
   * retrieve the session handle from the first slot that supports the specified
   * mechanism.
   */
  static CK_RV get_session_handle(CK_MECHANISM_TYPE mech_type, CK_FLAGS flag,
                                  CK_SESSION_HANDLE_PTR sess)
  {
      CK_RV rv = CKR_OK;
      CK_MECHANISM_INFO info;
      netsnmp_pkcs_slot_session* p = NULL;
      int i, slotcount = 0;
  
      if (pSlot)
      {
          slotcount = pSlot->count;
          p = pSlot->pSession;
      }
  
      /* Find a slot with matching mechanism */
      for (i = 0; i < slotcount; i++, p++)
      {
          rv = C_GetMechanismInfo(p->sid, mech_type, &info);
  
          if (rv != CKR_OK)
          {
              continue; /* to the next slot */
          }
          else
          {
              if (info.flags & flag)
              {
                  rv = get_slot_session_handle(p, sess);
                  break; /* found */
              }
          }
      }
  
      /* Show error if no matching mechanism found */
      if (i == slotcount)
      {
          DEBUGMSGTL(
              ("pkcs_init", "No cryptographic provider for %s", mech_type));
          return CKR_SESSION_HANDLE_INVALID;
      }
  
      return rv;
  }
  
  /*
   * retrieve the session handle from the specified slot.
   */
  static CK_RV get_slot_session_handle(netsnmp_pkcs_slot_session* p,
                                       CK_SESSION_HANDLE_PTR sess)
  {
      CK_RV rv = CKR_OK;
      if (p == NULL)
      {
          *sess = NULL;
          return CKR_SESSION_HANDLE_INVALID;
      }
  
      if (p->hdl == NULL)
      {
          /* Open a session */
          rv = C_OpenSession(p->sid, CKF_SERIAL_SESSION, NULL_PTR, NULL, &p->hdl);
  
          if (rv != CKR_OK)
          {
              DEBUGMSGTL(("get_slot_session_handle",
                          "can not open PKCS #11 session: %s",
                          pkcserr_string(rv)));
          }
      }
      *sess = p->hdl;
  
      return rv;
  }
  
  /*
   * perform a signature operation to generate MAC.
   */
  int pkcs_sign(CK_MECHANISM_TYPE mech_type, u_char* key, u_int keylen,
                u_char* msg, u_int msglen, u_char* mac, size_t* maclen)
  {
      /*
       * Key template
       */
      CK_OBJECT_CLASS class = CKO_SECRET_KEY;
      CK_KEY_TYPE keytype = CKK_GENERIC_SECRET;
      CK_BBOOL truevalue = TRUE;
      CK_BBOOL falsevalue = FALSE;
      CK_ATTRIBUTE template[] = {{CKA_CLASS, &class, sizeof(class)},
                                 {CKA_KEY_TYPE, &keytype, sizeof(keytype)},
                                 {CKA_SIGN, &truevalue, sizeof(truevalue)},
                                 {CKA_TOKEN, &falsevalue, sizeof(falsevalue)},
                                 {CKA_VALUE, key, keylen}};
      CK_SESSION_HANDLE hSession;
      CK_MECHANISM mech;
      CK_OBJECT_HANDLE hkey = (CK_OBJECT_HANDLE)0;
      int rval = SNMPERR_SUCCESS;
      if (get_session_handle(mech_type, CKF_SIGN, &hSession) != CKR_OK ||
          hSession == NULL)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_sign_quit);
      }
  
      /* create a key object */
      if (C_CreateObject(hSession, template,
                         (sizeof(template) / sizeof(CK_ATTRIBUTE)),
                         &hkey) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_sign_quit);
      }
  
      mech.mechanism = mech_type;
      mech.pParameter = NULL_PTR;
      mech.ulParameterLen = 0;
  
      /* initialize a signature operation */
      if (C_SignInit(hSession, &mech, hkey) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_sign_quit);
      }
      /* continue a multiple-part signature operation */
      if (C_SignUpdate(hSession, msg, msglen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_sign_quit);
      }
      /* finish a multiple-part signature operation */
      if (C_SignFinal(hSession, mac, maclen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_sign_quit);
      }
  
  pkcs_sign_quit:
  
      if (key != (CK_OBJECT_HANDLE)0)
      {
          (void)C_DestroyObject(hSession, hkey);
      }
      return rval;
  }
  
  /*
   * perform a message-digesting operation.
   */
  int pkcs_digest(CK_MECHANISM_TYPE mech_type, u_char* msg, u_int msglen,
                  u_char* digest, size_t* digestlen)
  {
      int rval = SNMPERR_SUCCESS;
      CK_SESSION_HANDLE hSession;
      CK_MECHANISM mech;
      if (get_session_handle(mech_type, CKF_DIGEST, &hSession) != CKR_OK ||
          hSession == NULL)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_digest_quit);
      }
  
      mech.mechanism = mech_type;
      mech.pParameter = NULL_PTR;
      mech.ulParameterLen = 0;
  
      /* initialize a message-digesting operation */
      if (C_DigestInit(hSession, &mech) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_digest_quit);
      }
      /* continue a multiple-part message-digesting operation */
      if (C_DigestUpdate(hSession, msg, msglen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_digest_quit);
      }
      /* finish a multiple-part message-digesting operation */
      if (C_DigestFinal(hSession, digest, digestlen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_digest_quit);
      }
  
  pkcs_digest_quit:
      return rval;
  }
  
  /*
   * encrypt plaintext into ciphertext using key and iv.
   */
  int pkcs_encrpyt(CK_MECHANISM_TYPE mech_type, u_char* key, u_int keylen,
                   u_char* iv, u_int ivlen, u_char* plaintext, u_int ptlen,
                   u_char* ciphertext, size_t* ctlen)
  {
      int rval = SNMPERR_SUCCESS;
      int pad_size, offset;
      /*
       * Key template
       */
      CK_OBJECT_CLASS class = CKO_SECRET_KEY;
      CK_KEY_TYPE keytype = CKK_DES;
      /* CK_KEY_TYPE AESkeytype = CKK_AES; */
      CK_BBOOL truevalue = TRUE;
      CK_BBOOL falsevalue = FALSE;
  
      CK_ATTRIBUTE template[] = {{CKA_CLASS, &class, sizeof(class)},
                                 {CKA_KEY_TYPE, &keytype, sizeof(keytype)},
                                 {CKA_ENCRYPT, &truevalue, sizeof(truevalue)},
                                 {CKA_TOKEN, &falsevalue, sizeof(falsevalue)},
                                 {CKA_VALUE, key, keylen}};
  
      CK_SESSION_HANDLE hSession;
      CK_MECHANISM mech;
      CK_OBJECT_HANDLE hkey = (CK_OBJECT_HANDLE)0;
  
      if (get_session_handle(mech_type, CKF_ENCRYPT, &hSession) != CKR_OK ||
          hSession == NULL)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_encrypt_quit);
      }
  
      if (C_CreateObject(hSession, template,
                         (sizeof(template) / sizeof(CK_ATTRIBUTE)),
                         &hkey) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_encrypt_quit);
      }
  
      mech.mechanism = mech_type;
      mech.pParameter = iv;
      mech.ulParameterLen = ivlen;
  
      /* initialize an encryption operation */
      if (C_EncryptInit(hSession, &mech, hkey) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_encrypt_quit);
      }
  
      /* for DES */
      pad_size = BYTESIZE(SNMP_TRANS_PRIVLEN_1DES);
  
      if (ptlen + pad_size - ptlen % pad_size > *ctlen)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_encrypt_quit);
      }
  
      for (offset = 0; offset < ptlen; offset += pad_size)
      {
          /* continue a multiple-part encryption operation */
          if (C_EncryptUpdate(hSession, plaintext + offset, pad_size,
                              ciphertext + offset, ctlen) != CKR_OK)
          {
              QUITFUN(SNMPERR_GENERR, pkcs_encrypt_quit);
          }
      }
  
      /* finish a multiple-part encryption operation */
      if (C_EncryptFinal(hSession, ciphertext + offset, ctlen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_encrypt_quit);
      }
      *ctlen = offset;
  
  pkcs_encrypt_quit:
      if (key != (CK_OBJECT_HANDLE)0)
      {
          (void)C_DestroyObject(hSession, hkey);
      }
      return rval;
  }
  
  /*
   * decrypt ciphertext into plaintext using key and iv.
   */
  int pkcs_decrpyt(CK_MECHANISM_TYPE mech_type, u_char* key, u_int keylen,
                   u_char* iv, u_int ivlen, u_char* ciphertext, u_int ctlen,
                   u_char* plaintext, size_t* ptlen)
  {
      int rval = SNMPERR_SUCCESS;
      /*
       * Key template
       */
      CK_OBJECT_CLASS class = CKO_SECRET_KEY;
      CK_KEY_TYPE keytype = CKK_DES;
      /* CK_KEY_TYPE AESkeytype = CKK_AES; */
      CK_BBOOL truevalue = TRUE;
      CK_BBOOL falsevalue = FALSE;
      CK_ATTRIBUTE template[] = {{CKA_CLASS, &class, sizeof(class)},
                                 {CKA_KEY_TYPE, &keytype, sizeof(keytype)},
                                 {CKA_DECRYPT, &truevalue, sizeof(truevalue)},
                                 {CKA_TOKEN, &falsevalue, sizeof(falsevalue)},
                                 {CKA_VALUE, key, keylen}};
      CK_SESSION_HANDLE hSession;
      CK_MECHANISM mech;
      CK_OBJECT_HANDLE hkey = (CK_OBJECT_HANDLE)0;
  
      if (get_session_handle(mech_type, CKF_DECRYPT, &hSession) != CKR_OK ||
          hSession == NULL)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_decrypt_quit);
      }
  
      if (C_CreateObject(hSession, template,
                         (sizeof(template) / sizeof(CK_ATTRIBUTE)),
                         &hkey) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_decrypt_quit);
      }
  
      mech.mechanism = mech_type;
      mech.pParameter = iv;
      mech.ulParameterLen = ivlen;
  
      /* initialize a decryption operation */
      if (C_DecryptInit(hSession, &mech, hkey) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_decrypt_quit);
      }
      /* continue a multiple-part decryption operation */
      if (C_DecryptUpdate(hSession, ciphertext, ctlen, plaintext, ptlen) !=
          CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_decrypt_quit);
      }
      /* finish a multiple-part decryption operation */
      if (C_DecryptFinal(hSession, plaintext, ptlen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_decrypt_quit);
      }
  
  pkcs_decrypt_quit:
      if (key != (CK_OBJECT_HANDLE)0)
      {
          (void)C_DestroyObject(hSession, hkey);
      }
      return rval;
  }
  
  /*
   * Convert a passphrase into a master user key, Ku, according to the
   * algorithm given in RFC 2274 concerning the SNMPv3 User Security Model (USM)
   */
  int pkcs_generate_Ku(CK_MECHANISM_TYPE mech_type, u_char* passphrase,
                       u_int pplen, u_char* Ku, size_t* kulen)
  {
      int rval = SNMPERR_SUCCESS, nbytes = USM_LENGTH_EXPANDED_PASSPHRASE;
      CK_SESSION_HANDLE hSession;
      CK_MECHANISM mech;
      u_int i, pindex = 0;
      u_char buf[USM_LENGTH_KU_HASHBLOCK], *bufp;
  
      if (get_session_handle(mech_type, CKF_DIGEST, &hSession) != CKR_OK ||
          hSession == NULL)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_generate_Ku_quit);
      }
  
      mech.mechanism = mech_type;
      mech.pParameter = NULL_PTR;
      mech.ulParameterLen = 0;
  
      /* initialize a message-digesting operation */
      if (C_DigestInit(hSession, &mech) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_generate_Ku_quit);
      }
  
      while (nbytes > 0)
      {
          bufp = buf;
          for (i = 0; i < USM_LENGTH_KU_HASHBLOCK; i++)
          {
              /*
               * fill a buffer with the supplied passphrase.  When the end
               * of the passphrase is reachedcycle back to the beginning.
               */
              *bufp++ = passphrase[pindex++ % pplen];
          }
          /* continue a multiple-part message-digesting operation */
          if (C_DigestUpdate(hSession, buf, USM_LENGTH_KU_HASHBLOCK) != CKR_OK)
          {
              QUITFUN(SNMPERR_GENERR, pkcs_generate_Ku_quit);
          }
          nbytes -= USM_LENGTH_KU_HASHBLOCK;
      }
      /* finish a multiple-part message-digesting operation */
      if (C_DigestFinal(hSession, Ku, kulen) != CKR_OK)
      {
          QUITFUN(SNMPERR_GENERR, pkcs_generate_Ku_quit);
      }
  
  pkcs_generate_Ku_quit:
      return rval;
  }
  
  /*
   * pkcserr_stringor: returns a string representation of the given
   * return code.
   */
  static char* pkcserr_string(CK_RV rv)
  {
      static char errstr[128];
      switch (rv)
      {
          case CKR_OK:
              return ("CKR_OK");
              break;
          case CKR_CANCEL:
              return ("CKR_CANCEL");
              break;
          case CKR_HOST_MEMORY:
              return ("CKR_HOST_MEMORY");
              break;
          case CKR_SLOT_ID_INVALID:
              return ("CKR_SLOT_ID_INVALID");
              break;
          case CKR_GENERAL_ERROR:
              return ("CKR_GENERAL_ERROR");
              break;
          case CKR_FUNCTION_FAILED:
              return ("CKR_FUNCTION_FAILED");
              break;
          case CKR_ARGUMENTS_BAD:
              return ("CKR_ARGUMENTS_BAD");
              break;
          case CKR_NO_EVENT:
              return ("CKR_NO_EVENT");
              break;
          case CKR_NEED_TO_CREATE_THREADS:
              return ("CKR_NEED_TO_CREATE_THREADS");
              break;
          case CKR_CANT_LOCK:
              return ("CKR_CANT_LOCK");
              break;
          case CKR_ATTRIBUTE_READ_ONLY:
              return ("CKR_ATTRIBUTE_READ_ONLY");
              break;
          case CKR_ATTRIBUTE_SENSITIVE:
              return ("CKR_ATTRIBUTE_SENSITIVE");
              break;
          case CKR_ATTRIBUTE_TYPE_INVALID:
              return ("CKR_ATTRIBUTE_TYPE_INVALID");
              break;
          case CKR_ATTRIBUTE_VALUE_INVALID:
              return ("CKR_ATTRIBUTE_VALUE_INVALID");
              break;
          case CKR_DATA_INVALID:
              return ("CKR_DATA_INVALID");
              break;
          case CKR_DATA_LEN_RANGE:
              return ("CKR_DATA_LEN_RANGE");
              break;
          case CKR_DEVICE_ERROR:
              return ("CKR_DEVICE_ERROR");
              break;
          case CKR_DEVICE_MEMORY:
              return ("CKR_DEVICE_MEMORY");
              break;
          case CKR_DEVICE_REMOVED:
              return ("CKR_DEVICE_REMOVED");
              break;
          case CKR_ENCRYPTED_DATA_INVALID:
              return ("CKR_ENCRYPTED_DATA_INVALID");
              break;
          case CKR_ENCRYPTED_DATA_LEN_RANGE:
              return ("CKR_ENCRYPTED_DATA_LEN_RANGE");
              break;
          case CKR_FUNCTION_CANCELED:
              return ("CKR_FUNCTION_CANCELED");
              break;
          case CKR_FUNCTION_NOT_PARALLEL:
              return ("CKR_FUNCTION_NOT_PARALLEL");
              break;
          case CKR_FUNCTION_NOT_SUPPORTED:
              return ("CKR_FUNCTION_NOT_SUPPORTED");
              break;
          case CKR_KEY_HANDLE_INVALID:
              return ("CKR_KEY_HANDLE_INVALID");
              break;
          case CKR_KEY_SIZE_RANGE:
              return ("CKR_KEY_SIZE_RANGE");
              break;
          case CKR_KEY_TYPE_INCONSISTENT:
              return ("CKR_KEY_TYPE_INCONSISTENT");
              break;
          case CKR_KEY_NOT_NEEDED:
              return ("CKR_KEY_NOT_NEEDED");
              break;
          case CKR_KEY_CHANGED:
              return ("CKR_KEY_CHANGED");
              break;
          case CKR_KEY_NEEDED:
              return ("CKR_KEY_NEEDED");
              break;
          case CKR_KEY_INDIGESTIBLE:
              return ("CKR_KEY_INDIGESTIBLE");
              break;
          case CKR_KEY_FUNCTION_NOT_PERMITTED:
              return ("CKR_KEY_FUNCTION_NOT_PERMITTED");
              break;
          case CKR_KEY_NOT_WRAPPABLE:
              return ("CKR_KEY_NOT_WRAPPABLE");
              break;
          case CKR_KEY_UNEXTRACTABLE:
              return ("CKR_KEY_UNEXTRACTABLE");
              break;
          case CKR_MECHANISM_INVALID:
              return ("CKR_MECHANISM_INVALID");
              break;
          case CKR_MECHANISM_PARAM_INVALID:
              return ("CKR_MECHANISM_PARAM_INVALID");
              break;
          case CKR_OBJECT_HANDLE_INVALID:
              return ("CKR_OBJECT_HANDLE_INVALID");
              break;
          case CKR_OPERATION_ACTIVE:
              return ("CKR_OPERATION_ACTIVE");
              break;
          case CKR_OPERATION_NOT_INITIALIZED:
              return ("CKR_OPERATION_NOT_INITIALIZED");
              break;
          case CKR_PIN_INCORRECT:
              return ("CKR_PIN_INCORRECT");
              break;
          case CKR_PIN_INVALID:
              return ("CKR_PIN_INVALID");
              break;
          case CKR_PIN_LEN_RANGE:
              return ("CKR_PIN_LEN_RANGE");
              break;
          case CKR_PIN_EXPIRED:
              return ("CKR_PIN_EXPIRED");
              break;
          case CKR_PIN_LOCKED:
              return ("CKR_PIN_LOCKED");
              break;
          case CKR_SESSION_CLOSED:
              return ("CKR_SESSION_CLOSED");
              break;
          case CKR_SESSION_COUNT:
              return ("CKR_SESSION_COUNT");
              break;
          case CKR_SESSION_HANDLE_INVALID:
              return ("CKR_SESSION_HANDLE_INVALID");
              break;
          case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
              return ("CKR_SESSION_PARALLEL_NOT_SUPPORTED");
              break;
          case CKR_SESSION_READ_ONLY:
              return ("CKR_SESSION_READ_ONLY");
              break;
          case CKR_SESSION_EXISTS:
              return ("CKR_SESSION_EXISTS");
              break;
          case CKR_SESSION_READ_ONLY_EXISTS:
              return ("CKR_SESSION_READ_ONLY_EXISTS");
              break;
          case CKR_SESSION_READ_WRITE_SO_EXISTS:
              return ("CKR_SESSION_READ_WRITE_SO_EXISTS");
              break;
          case CKR_SIGNATURE_INVALID:
              return ("CKR_SIGNATURE_INVALID");
              break;
          case CKR_SIGNATURE_LEN_RANGE:
              return ("CKR_SIGNATURE_LEN_RANGE");
              break;
          case CKR_TEMPLATE_INCOMPLETE:
              return ("CKR_TEMPLATE_INCOMPLETE");
              break;
          case CKR_TEMPLATE_INCONSISTENT:
              return ("CKR_TEMPLATE_INCONSISTENT");
              break;
          case CKR_TOKEN_NOT_PRESENT:
              return ("CKR_TOKEN_NOT_PRESENT");
              break;
          case CKR_TOKEN_NOT_RECOGNIZED:
              return ("CKR_TOKEN_NOT_RECOGNIZED");
              break;
          case CKR_TOKEN_WRITE_PROTECTED:
              return ("CKR_TOKEN_WRITE_PROTECTED");
              break;
          case CKR_UNWRAPPING_KEY_HANDLE_INVALID:
              return ("CKR_UNWRAPPING_KEY_HANDLE_INVALID");
              break;
          case CKR_UNWRAPPING_KEY_SIZE_RANGE:
              return ("CKR_UNWRAPPING_KEY_SIZE_RANGE");
              break;
          case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT:
              return ("CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT");
              break;
          case CKR_USER_ALREADY_LOGGED_IN:
              return ("CKR_USER_ALREADY_LOGGED_IN");
              break;
          case CKR_USER_NOT_LOGGED_IN:
              return ("CKR_USER_NOT_LOGGED_IN");
              break;
          case CKR_USER_PIN_NOT_INITIALIZED:
              return ("CKR_USER_PIN_NOT_INITIALIZED");
              break;
          case CKR_USER_TYPE_INVALID:
              return ("CKR_USER_TYPE_INVALID");
              break;
          case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
              return ("CKR_USER_ANOTHER_ALREADY_LOGGED_IN");
              break;
          case CKR_USER_TOO_MANY_TYPES:
              return ("CKR_USER_TOO_MANY_TYPES");
              break;
          case CKR_WRAPPED_KEY_INVALID:
              return ("CKR_WRAPPED_KEY_INVALID");
              break;
          case CKR_WRAPPED_KEY_LEN_RANGE:
              return ("CKR_WRAPPED_KEY_LEN_RANGE");
              break;
          case CKR_WRAPPING_KEY_HANDLE_INVALID:
              return ("CKR_WRAPPING_KEY_HANDLE_INVALID");
              break;
          case CKR_WRAPPING_KEY_SIZE_RANGE:
              return ("CKR_WRAPPING_KEY_SIZE_RANGE");
              break;
          case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:
              return ("CKR_WRAPPING_KEY_TYPE_INCONSISTENT");
              break;
          case CKR_RANDOM_SEED_NOT_SUPPORTED:
              return ("CKR_RANDOM_SEED_NOT_SUPPORTED");
              break;
          case CKR_RANDOM_NO_RNG:
              return ("CKR_RANDOM_NO_RNG");
              break;
          case CKR_DOMAIN_PARAMS_INVALID:
              return ("CKR_DOMAIN_PARAMS_INVALID");
              break;
          case CKR_BUFFER_TOO_SMALL:
              return ("CKR_BUFFER_TOO_SMALL");
              break;
          case CKR_SAVED_STATE_INVALID:
              return ("CKR_SAVED_STATE_INVALID");
              break;
          case CKR_INFORMATION_SENSITIVE:
              return ("CKR_INFORMATION_SENSITIVE");
              break;
          case CKR_STATE_UNSAVEABLE:
              return ("CKR_STATE_UNSAVEABLE");
              break;
          case CKR_CRYPTOKI_NOT_INITIALIZED:
              return ("CKR_CRYPTOKI_NOT_INITIALIZED");
              break;
          case CKR_CRYPTOKI_ALREADY_INITIALIZED:
              return ("CKR_CRYPTOKI_ALREADY_INITIALIZED");
              break;
          case CKR_MUTEX_BAD:
              return ("CKR_MUTEX_BAD");
              break;
          case CKR_MUTEX_NOT_LOCKED:
              return ("CKR_MUTEX_NOT_LOCKED");
              break;
          case CKR_VENDOR_DEFINED:
              return ("CKR_VENDOR_DEFINED");
              break;
          default:
              /* rv not found */
              snprintf(errstr, sizeof(errstr), "Unknown return code: 0x%x", rv);
              return (errstr);
              break;
      }
  }
  #else
  int pkcs_unused; /* Suppress "empty translation unit" warning */
  #endif
  