#define VERSION "EG2.3"
#define USING_TPM   // define if using TPM as HSM
/*
 * Modifications and improvements Copyright (C) 2011 Ed Guzovsky
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * non-commercial use with or without fee is hereby granted, provided that
 * the above copyright notice and this permission notice appear in all copies.
 *
 * Copyright (C) 2007 Internet Corporation for Assigned Names 
 *                         and Numbers ("ICANN")
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ICANN DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ICANN BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: RHLamb 2007
 *   pkcs11 HSM key backup utility
 *
 * cc foo.c -o foo -ldl 
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <dlfcn.h>
#include "cryptoki.h"

#include <unistd.h>
#include <syslog.h>
#include <sys/param.h>
#include <stdarg.h>

typedef unsigned char uchar;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long uint32;
typedef int boolean;

#define LEGACY_WRAPPINGKEY /* for 3DES Wrapping keys. keep using su=ince AEP has trouble */

#define MAX_SLOTS 100
#define MAX_KEYS_PER_SLOT 8000
#define min(x,y) ((x)<(y)?(x):(y))

static CK_FUNCTION_LIST_PTR  pfl;
static CK_BBOOL ctrue = CK_TRUE;
static CK_BBOOL cfalse = CK_FALSE;

static time_t tnow;

static char *misc_hsmconfig=NULL;

#define LOGDIR "."

char logfile[MAXPATHLEN];
char sessionlogfile[MAXPATHLEN];

static char *myx_malloc(int n)
{
  char *p;

  if((p=malloc(n)) == NULL) {
    printf("Can not malloc(%d) memory in %s\n",n,__func__);
  }
  return p;
}
static void myx_syslog(int pri,const char *format, ...)
{
  va_list args;
  static FILE *fp;
  static FILE *fses;

  va_start(args,format);
  vfprintf(stdout,format,args);
  va_end(args);

  if(fp == NULL) {
    /* append here as we might have multiple executions of kgen */
    if((fp=fopen(logfile,"a")) == NULL) {
      return;
    }
  }
  if(fses == NULL) {
    /* append here as we might have multiple executions of kgen */
    if((fses=fopen(sessionlogfile,"w")) == NULL) {
      return;
    }
  }
  if(fp) {
    va_start(args,format);
    vfprintf(fp,format,args);
    va_end(args);
  }
  if(fses) {
    va_start(args,format);
    vfprintf(fses,format,args);
    va_end(args);
  }
#ifdef FOOP
  if(pri == LOG_ERR) {
    va_start(args,format);
    vfprintf(stderr,format,args);
    va_end(args);
  }
#endif
#ifdef FOOP
  va_start(args,format);
  vsyslog(pri,format,args);
  va_end(args);
#endif
}

static void sigpipe(int sig) {
  /*
  char buf[MAXPATHLEN];

  time(&tnow);
  strftime(buf,sizeof(buf),"%Y%m%d-%H%M%S",gmtime(&tnow));
  myx_syslog(LOG_INFO,"%s: %s Exited on signal %d\n",buf,"pkcs11-backup",sig);
  (void)signal(SIGINT,SIG_DFL);
  exit(0);
  */
}

static int cleanup(char *io)
{
  char *q,*p;

  if (io == NULL) return -1;

  /* rid trailing space (' ' or tab) and CF/LF */
  for(q = io + strlen(io);q-- != io && isspace(*q) ;) ;

  *(q+1) = '\0';

  /* rid leading space */
  for(q=io;isblank(*q);q++) ;
  for(p=io;*q;) *p++ = *q++;
  *p = '\0';

  return 0;
}
static const char base64[] =
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
#define PEM_LINE_LENGTH 64

/* "outlen" should be at least (4/3)*n */
static int base64encode(char *out,uint8 *in,int n)
{
  int i,len;

  i = 0;
  len = 0;
  while(n > 0) {
    *out++ = base64[ ((in[0]>>2)&0x3f) ];
    if(n > 2) {
      *out++ = base64[ ((in[0]<<4)&0x30) | ((in[1]>>4)&0x0f) ];
      *out++ = base64[ ((in[1]<<2)&0x3c) | ((in[2]>>6)&0x03) ];
      *out++ = base64[ in[2]&0x3f ];
    } else if(n == 2) {
      *out++ = base64[ ((in[0]<<4)&0x30) | ((in[1]>>4)&0x0f) ];
      *out++ = base64[ ((in[1]<<2)&0x3c) ];
      *out++ = '=';
    } else if(n == 1) {
      *out++ = base64[ ((in[0]<<4)&0x30) ];
      *out++ = '=';
      *out++ = '=';
    }
    len += 4;
    n -= 3;
    in += 3;
  }
  *out = '\0';
  return len;
}
static int base64decode(char *in,uint8 *out)
{
  char *c,*p,*p0;
  int i,n,len;

  len = 0;
  n = strlen(in);
  p0 = p = (char *)myx_malloc(n+4);
  strcpy(p0,in);
  for(i=0;i<n;i++) {
    if((c=strchr(base64,*p)) == NULL) { free(p0); return -1; }
    *p++ = c - base64;
  }
  p = p0;
  while(n > 0) {
    int k;
    k = (p[2] == 64)?1:(p[3] == 64)?2:3;
    if(k != 3) {
      if(p[2] == 64) p[2] = 0;
      if(p[3] == 64) p[3] = 0;
    }
    *out++ = (p[0]<<2)|(p[1]>>4);
    *out++ = (p[1]<<4)|(p[2]>>2);
    *out++ = (p[2]<<6)|(p[3]);
    n -= 4;
    p += 4;
    len += k;
  }
  free(p0);
  return len;
}
static int lparse(char *line,char *argv[],int maxargs,char delc)
{
  char *cp;
  int argc,qflag;

  if((cp = strchr(line,'\r')) != (char *)0) *cp = '\0';
  if((cp = strchr(line,'\n')) != (char *)0) *cp = '\0';

  for(argc=0;argc<maxargs;argc++) argv[argc] = (char *)0;

  for(argc=0;argc<maxargs;) {
    qflag = 0;
    while(*line == ' ' || *line == '\t') line++; /* whitespace */
    if(*line == '\0') break; /* done */
    if(*line == '"') { line++; qflag = 1; } /* quote */
    argv[argc++] = line;
    if(qflag) {                         /* quote */
      if((line = strchr(line,'"')) == (char *)0) return -1; /*error*/
      *line++ = '\0';
      if(*line == delc) line++;
    } else {
      for(cp=line;*cp;cp++) {
        if(*cp == delc) break;
      }
      if(*cp) *cp++ = '\0'; /* non-zero */
      line = cp;
    }
  }
  return argc;
}
static int rdump(const uint8 *ptr,int n)
{
  int i,j1,j2; char buf[80]; static char htoas[]="0123456789ABCDEF";
  j1 = j2 = 0; /* gcc -W */
  for(i=0;i<n;i++,j1+=3,j2++) {
    if((i&0xf) == 0) {
      if(i) { buf[j2]='\0';  printf("%s|",buf); }
      j1=0; j2=51; memset(buf,' ',80); buf[50]='|';
    }
    buf[j1] = htoas[(ptr[i]&0xf0) >> 4]; buf[j1+1] = htoas[ptr[i]&0x0f];
    if(ptr[i] >= 0x20 && ptr[i] < 0x80) buf[j2]=ptr[i]; else buf[j2]='.';
  }
  buf[j2]='\0'; printf("%s|",buf);
  return 0;
}
static int hex2i(char c)
{
  if(c >= '0' && c <= '9') return (int)(c - '0');
  if(c >= 'A' && c <= 'F') return (int)((c - 'A') + 10);
  if(c >= 'a' && c <= 'f') return (int)((c - 'a') + 10);
  return -1;
}

#define GETPASS
/*#include <curses.h>*/
static char *fgetsne(char *s,int n, FILE *fp)
{
  char *p;

#ifdef GETPASS
  p = getpass("");
  if(p == NULL) return NULL;
  strncpy(s,p,n);
  return p;
#else
  /*noecho();*/
  p = fgets(s,n,fp);
  /*echo();*/
  return p;
#endif
}

static char *pkcs11_ret_str(CK_RV rv)
{
  switch(rv) {
  case CKR_OK:
    return "CKR_OK";
  case CKR_CANCEL:
    return "CKR_CANCEL";
  case CKR_HOST_MEMORY:
    return "CKR_HOST_MEMORY";
  case CKR_SLOT_ID_INVALID:
    return "CKR_SLOT_ID_INVALID";
  case CKR_GENERAL_ERROR:
    return "CKR_GENERAL_ERROR";
  case CKR_FUNCTION_FAILED:
    return "CKR_FUNCTION_FAILED";
  case CKR_ARGUMENTS_BAD:
    return "CKR_ARGUMENTS_BAD";
  case CKR_NO_EVENT:
    return "CKR_NO_EVENT";
  case CKR_NEED_TO_CREATE_THREADS:
    return "CKR_NEED_TO_CREATE_THREADS";
  case CKR_CANT_LOCK:
    return "CKR_CANT_LOCK";
  case CKR_ATTRIBUTE_READ_ONLY:
    return "CKR_ATTRIBUTE_READ_ONLY";
  case CKR_ATTRIBUTE_SENSITIVE:
    return "CKR_ATTRIBUTE_SENSITIVE";
  case CKR_ATTRIBUTE_TYPE_INVALID:
    return "CKR_ATTRIBUTE_TYPE_INVALID";
  case CKR_ATTRIBUTE_VALUE_INVALID:
    return "CKR_ATTRIBUTE_VALUE_INVALID";
  case CKR_DATA_INVALID:
    return "CKR_DATA_INVALID";
  case CKR_DATA_LEN_RANGE:
    return "CKR_DATA_LEN_RANGE";
  case CKR_DEVICE_ERROR:
    return "CKR_DEVICE_ERROR";
  case CKR_DEVICE_MEMORY:
    return "CKR_DEVICE_MEMORY";
  case CKR_DEVICE_REMOVED:
    return "CKR_DEVICE_REMOVED";
  case CKR_ENCRYPTED_DATA_INVALID:
    return "CKR_ENCRYPTED_DATA_INVALID";
  case CKR_ENCRYPTED_DATA_LEN_RANGE:
    return "CKR_ENCRYPTED_DATA_LEN_RANGE";
  case CKR_FUNCTION_CANCELED:
    return "CKR_FUNCTION_CANCELED";
  case CKR_FUNCTION_NOT_PARALLEL:
    return "CKR_FUNCTION_NOT_PARALLEL";
  case CKR_FUNCTION_NOT_SUPPORTED:
    return "CKR_FUNCTION_NOT_SUPPORTED";
  case CKR_KEY_HANDLE_INVALID:
    return "CKR_KEY_HANDLE_INVALID";
  case CKR_KEY_SIZE_RANGE:
    return "CKR_KEY_SIZE_RANGE";
  case CKR_KEY_TYPE_INCONSISTENT:
    return "CKR_KEY_TYPE_INCONSISTENT";
  case CKR_KEY_NOT_NEEDED:
    return "CKR_KEY_NOT_NEEDED";
  case CKR_KEY_CHANGED:
    return "CKR_KEY_CHANGED";
  case CKR_KEY_NEEDED:
    return "CKR_KEY_NEEDED";
  case CKR_KEY_INDIGESTIBLE:
    return "CKR_KEY_INDIGESTIBLE";
  case CKR_KEY_FUNCTION_NOT_PERMITTED:
    return "CKR_KEY_FUNCTION_NOT_PERMITTED";
  case CKR_KEY_NOT_WRAPPABLE:
    return "CKR_KEY_NOT_WRAPPABLE";
  case CKR_KEY_UNEXTRACTABLE:
    return "CKR_KEY_UNEXTRACTABLE";
  case CKR_MECHANISM_INVALID:
    return "CKR_MECHANISM_INVALID";
  case CKR_MECHANISM_PARAM_INVALID:
    return "CKR_MECHANISM_PARAM_INVALID";
  case CKR_OBJECT_HANDLE_INVALID:
    return "CKR_OBJECT_HANDLE_INVALID";
  case CKR_OPERATION_ACTIVE:
    return "CKR_OPERATION_ACTIVE";
  case CKR_OPERATION_NOT_INITIALIZED:
    return "CKR_OPERATION_NOT_INITIALIZED";
  case CKR_PIN_INCORRECT:
    return "CKR_PIN_INCORRECT";
  case CKR_PIN_INVALID:
    return "CKR_PIN_INVALID";
  case CKR_PIN_LEN_RANGE:
    return "CKR_PIN_LEN_RANGE";
  case CKR_PIN_EXPIRED:
    return "CKR_PIN_EXPIRED";
  case CKR_PIN_LOCKED:
    return "CKR_PIN_LOCKED";
  case CKR_SESSION_CLOSED:
    return "CKR_SESSION_CLOSED";
  case CKR_SESSION_COUNT:
    return "CKR_SESSION_COUNT";
  case CKR_SESSION_HANDLE_INVALID:
    return "CKR_SESSION_HANDLE_INVALID";
  case CKR_SESSION_PARALLEL_NOT_SUPPORTED:
    return "CKR_SESSION_PARALLEL_NOT_SUPPORTED";
  case CKR_SESSION_READ_ONLY:
    return "CKR_SESSION_READ_ONLY";
  case CKR_SESSION_EXISTS:
    return "CKR_SESSION_EXISTS";
  case CKR_SESSION_READ_ONLY_EXISTS:
    return "CKR_SESSION_READ_ONLY_EXISTS";
  case CKR_SESSION_READ_WRITE_SO_EXISTS:
    return "CKR_SESSION_READ_WRITE_SO_EXISTS";
  case CKR_SIGNATURE_INVALID:
    return "CKR_SIGNATURE_INVALID";
  case CKR_SIGNATURE_LEN_RANGE:
    return "CKR_SIGNATURE_LEN_RANGE";
  case CKR_TEMPLATE_INCOMPLETE:
    return "CKR_TEMPLATE_INCOMPLETE";
  case CKR_TEMPLATE_INCONSISTENT:
    return "CKR_TEMPLATE_INCONSISTENT";
  case CKR_TOKEN_NOT_PRESENT:
    return "CKR_TOKEN_NOT_PRESENT";
  case CKR_TOKEN_NOT_RECOGNIZED:
    return "CKR_TOKEN_NOT_RECOGNIZED";
  case CKR_TOKEN_WRITE_PROTECTED:
    return "CKR_TOKEN_WRITE_PROTECTED";
  case CKR_UNWRAPPING_KEY_HANDLE_INVALID:
    return "CKR_UNWRAPPING_KEY_HANDLE_INVALID";
  case CKR_UNWRAPPING_KEY_SIZE_RANGE:
    return "CKR_UNWRAPPING_KEY_SIZE_RANGE";
  case CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT:
    return "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT";
  case CKR_USER_ALREADY_LOGGED_IN:
    return "CKR_USER_ALREADY_LOGGED_IN";
  case CKR_USER_NOT_LOGGED_IN:
    return "CKR_USER_NOT_LOGGED_IN";
  case CKR_USER_PIN_NOT_INITIALIZED:
    return "CKR_USER_PIN_NOT_INITIALIZED";
  case CKR_USER_TYPE_INVALID:
    return "CKR_USER_TYPE_INVALID";
  case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
    return "CKR_USER_ANOTHER_ALREADY_LOGGED_IN";
  case CKR_USER_TOO_MANY_TYPES:
    return "CKR_USER_TOO_MANY_TYPES";
  case CKR_WRAPPED_KEY_INVALID:
    return "CKR_WRAPPED_KEY_INVALID";
  case CKR_WRAPPED_KEY_LEN_RANGE:
    return "CKR_WRAPPED_KEY_LEN_RANGE";
  case CKR_WRAPPING_KEY_HANDLE_INVALID:
    return "CKR_WRAPPING_KEY_HANDLE_INVALID";
  case CKR_WRAPPING_KEY_SIZE_RANGE:
    return "CKR_WRAPPING_KEY_SIZE_RANGE";
  case CKR_WRAPPING_KEY_TYPE_INCONSISTENT:
    return "CKR_WRAPPING_KEY_TYPE_INCONSISTENT";
  case CKR_RANDOM_SEED_NOT_SUPPORTED:
    return "CKR_RANDOM_SEED_NOT_SUPPORTED";
  case CKR_RANDOM_NO_RNG:
    return "CKR_RANDOM_NO_RNG";
  case CKR_DOMAIN_PARAMS_INVALID:
    return "CKR_DOMAIN_PARAMS_INVALID";
  case CKR_BUFFER_TOO_SMALL:
    return "CKR_BUFFER_TOO_SMALL";
  case CKR_SAVED_STATE_INVALID:
    return "CKR_SAVED_STATE_INVALID";
  case CKR_INFORMATION_SENSITIVE:
    return "CKR_INFORMATION_SENSITIVE";
  case CKR_STATE_UNSAVEABLE:
    return "CKR_STATE_UNSAVEABLE";
  case CKR_CRYPTOKI_NOT_INITIALIZED:
    return "CKR_CRYPTOKI_NOT_INITIALIZED";
  case CKR_CRYPTOKI_ALREADY_INITIALIZED:
    return "CKR_CRYPTOKI_ALREADY_INITIALIZED";
  case CKR_MUTEX_BAD:
    return "CKR_MUTEX_BAD";
  case CKR_MUTEX_NOT_LOCKED:
    return "CKR_MUTEX_NOT_LOCKED";
  case CKR_FUNCTION_REJECTED:
    return "CKR_FUNCTION_REJECTED";
  case CKR_VENDOR_DEFINED:
    return "CKR_VENDOR_DEFINED";
  default:
    return "Undefined Return Code";
  }
}
static int delobject(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hObj)
{
  CK_RV rv;
  if((rv=pfl->C_DestroyObject(sh,hObj)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error C_DestroyObject %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  myx_syslog(LOG_ERR,"pkcs11: Deleted object %08x\n",hObj);
  return 0;
}
static int print_pubkeyinfo(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPub,int flags)
{
  CK_RV rv;
  char *p;
  int i,j;
  CK_ULONG tsize;
  CK_ATTRIBUTE getattributes[] = {
    {CKA_MODULUS,NULL_PTR,0},
    {CKA_PUBLIC_EXPONENT,NULL_PTR,0},
    {CKA_ID,NULL_PTR,0},
    {CKA_LABEL,NULL_PTR,0},
    {CKA_CLASS,NULL_PTR,0},
    {CKA_KEY_TYPE,NULL_PTR,0},
    {CKA_MODULUS_BITS,NULL_PTR,0},
  };

  tsize = sizeof(getattributes)/sizeof (CK_ATTRIBUTE);
  if((rv=pfl->C_GetAttributeValue(sh,hPub,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  for(i=0;i<(int)tsize;i++) {
    getattributes[i].pValue = myx_malloc(getattributes[i].ulValueLen); 
  }
  if((rv=pfl->C_GetAttributeValue(sh,hPub,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
    return -1;
  }
  {
    i = getattributes[3].ulValueLen;
    p = (char *)myx_malloc(i+1);
    memcpy(p,getattributes[3].pValue,i);
    p[i] = '\0';
    myx_syslog(LOG_INFO,"label:%s\n",p);
    free(p);
  }
  if(flags == 1) goto endit;
  {
    uint8 *pl;
    pl = (uint8 *)getattributes[2].pValue;
    myx_syslog(LOG_INFO,"id:");
    for(i=0;i<(int)getattributes[2].ulValueLen;i++) myx_syslog(LOG_INFO,"%02x",pl[i]);
    myx_syslog(LOG_INFO,"\n");
  }
  {
    if(getattributes[4].ulValueLen < sizeof(CK_OBJECT_CLASS)) myx_syslog(LOG_INFO,"class:error\n");
    switch(*(CK_OBJECT_CLASS *)getattributes[4].pValue) {
    case CKO_PRIVATE_KEY: p = "private"; break;
    case CKO_PUBLIC_KEY: p = "public"; break;
    case CKO_SECRET_KEY: p = "secret"; break;
    default: p = "unknown"; break;
    }
    myx_syslog(LOG_INFO,"class:%s\n",p);
  }
  {
    if(getattributes[5].ulValueLen < sizeof(CK_KEY_TYPE)) myx_syslog(LOG_INFO,"type:error\n");
    switch(*(CK_KEY_TYPE *)getattributes[5].pValue) {
    case CKK_RSA: p = "rsa"; break;
    case CKK_DSA: p = "dsa"; break;
    case CKK_DES3: p = "des3"; break;
    case CKK_AES: p = "aes"; break;
    default: p = "unknown"; break;
    }
    myx_syslog(LOG_INFO,"type:%s\n",p);
  }

  if(flags == 2) {
    uint8 *pl;
    myx_syslog(LOG_INFO, "modulus bits: %d\n",
	    *((CK_ULONG_PTR)(getattributes[6].pValue)));    
    pl = (uint8 *)getattributes[0].pValue;
    myx_syslog(LOG_INFO,"modulus: ");
    for(i=0;i<(int)getattributes[0].ulValueLen;i++) {
      myx_syslog(LOG_INFO,"%.2x",pl[i]);
    }
    myx_syslog(LOG_INFO,"\n");
    pl = (uint8 *)getattributes[1].pValue;
    myx_syslog(LOG_INFO,"public exponent: ");
    for(i=0;i<(int)getattributes[1].ulValueLen;i++) {
      myx_syslog(LOG_INFO,"%.2x",pl[i]);
    }
    myx_syslog(LOG_INFO,"\n");
    goto endit;
  }

  {
    char *p0;
    i = getattributes[0].ulValueLen;
    p = p0 = (char *)myx_malloc(2*i + 1);
    base64encode(p,getattributes[0].pValue,i);
    myx_syslog(LOG_INFO,"modulus:\n");
    j = strlen(p);
    while(j > 0) {
      for(i=0;i<min(j,PEM_LINE_LENGTH);i++) myx_syslog(LOG_INFO,"%c",*p++);
      myx_syslog(LOG_INFO,"\n");
      j -= PEM_LINE_LENGTH;
    }
    free(p0);
  }

  {
    char *p0;
    i = getattributes[1].ulValueLen;
    p = p0 = (char *)myx_malloc(2*i + 1);
    base64encode(p,getattributes[1].pValue,i);
    myx_syslog(LOG_INFO,"exponent:\n");
    j = strlen(p);
    while(j > 0) {
      for(i=0;i<min(j,PEM_LINE_LENGTH);i++) myx_syslog(LOG_INFO,"%c",*p++);
      myx_syslog(LOG_INFO,"\n");
      j -= PEM_LINE_LENGTH;
    }
    free(p0);
  }
 endit:
  for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
  return 0;
}
static int print_datainfo(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPriv,int flags)
{
  CK_RV rv;
  int i,j,n;
  char *p;
  CK_ULONG tsize;
  CK_ATTRIBUTE getattributes[] = {
    {CKA_LABEL,NULL_PTR,0},
    /*    {CKA_VALUE_LEN,NULL_PTR,0},*/
  };
  tsize = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);
  if((rv=pfl->C_GetAttributeValue(sh,hPriv,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    goto endit;
  }
  for(i=0;i<(int)tsize;i++) {
    getattributes[i].pValue = myx_malloc(getattributes[i].ulValueLen *sizeof(CK_VOID_PTR));
  }
  if((rv=pfl->C_GetAttributeValue(sh,hPriv,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
    goto endit;
  }
  {
    n = getattributes[0].ulValueLen;
    p = (char *)myx_malloc(n+1);
    memcpy(p,getattributes[0].pValue,n);
    p[n] = '\0';
    myx_syslog(LOG_INFO,"label:%s\n",p);
    free(p);
  }
#ifdef FOOP
  if(flags == 1) goto endit;
  {
    if(getattributes[1].ulValueLen < sizeof(CK_KEY_TYPE)) 
      myx_syslog(LOG_INFO,"type:error\n");
    rdump(getattributes[1].pValue,getattributes[1].ulValueLen);
  }
#endif
 endit:
  for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
  return 0;
}
static int export_pubkey(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPub)
{
  if(print_pubkeyinfo(sh,hPub,0)) return -1;
  myx_syslog(LOG_INFO,"\n"); /* end of key marker */
  return 0;
}
static int print_privkeyinfo(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPriv,int flags)
{
  CK_RV rv;
  int i,j,n;
  char *p;
  CK_ULONG tsize;
  CK_ATTRIBUTE getattributes[] = {
    {CKA_ID,NULL_PTR,0},
    {CKA_LABEL,NULL_PTR,0},
    {CKA_CLASS,NULL_PTR,0},
    {CKA_KEY_TYPE,NULL_PTR,0},
  };
  tsize = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);
  if((rv=pfl->C_GetAttributeValue(sh,hPriv,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    goto endit;
  }
  for(i=0;i<(int)tsize;i++) {
    getattributes[i].pValue = myx_malloc(getattributes[i].ulValueLen);
    /* *sizeof(CK_VOID_PTR)); */
  }
  if((rv=pfl->C_GetAttributeValue(sh,hPriv,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
    goto endit;
  }
  {
    n = getattributes[1].ulValueLen;
    p = (char *)myx_malloc(n+1);
    memcpy(p,getattributes[1].pValue,n);
    p[n] = '\0';
    myx_syslog(LOG_INFO,"label:%s\n",p);
    free(p);
  }
  if(flags == 1) goto endit;
  {
    uint8 *pu;
    pu = (uint8 *)getattributes[0].pValue;
    myx_syslog(LOG_INFO,"id:");
    for(i=0;i<(int)getattributes[0].ulValueLen;i++) myx_syslog(LOG_INFO,"%02x",pu[i]);
    myx_syslog(LOG_INFO,"\n");
  }
  {
    if(getattributes[2].ulValueLen < sizeof(CK_OBJECT_CLASS)) myx_syslog(LOG_INFO,"class:error\n");
    switch(*(CK_OBJECT_CLASS *)getattributes[2].pValue) {
    case CKO_PRIVATE_KEY: p = "private"; break;
    case CKO_PUBLIC_KEY: p = "public"; break;
    case CKO_SECRET_KEY: p = "secret"; break;
    default: p = "unknown"; break;
    }
    myx_syslog(LOG_INFO,"class:%s\n",p);
  }
  {
    if(getattributes[3].ulValueLen < sizeof(CK_KEY_TYPE)) myx_syslog(LOG_INFO,"type:error\n");
    switch(*(CK_KEY_TYPE *)getattributes[3].pValue) {
    case CKK_RSA: p = "rsa"; break;
    case CKK_DSA: p = "dsa"; break;
    case CKK_DES3: p = "des3"; break;
    case CKK_AES: p = "aes"; break;
    default: p = "unknown"; break;
    }
    myx_syslog(LOG_INFO,"type:%s\n",p);
  }
 endit:
  for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
  return 0;
}
static int print_certinfo(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hct,int flags)
{
  CK_RV rv;
  char *p;
  int i,j;
  CK_ULONG tsize;
  CK_BYTE *subject,_subject[1024];
  CK_BYTE *certvalue,_certvalue[2048];
  CK_CERTIFICATE_TYPE certtype = CKC_X_509;
  CK_ATTRIBUTE getattributes[] = {
    {CKA_CERTIFICATE_TYPE, NULL, 0 },
    {CKA_SUBJECT, NULL, 0},
    {CKA_LABEL, NULL, 0},
    {CKA_ID, NULL, 0},
    {CKA_VALUE, NULL, 0},
  };


  tsize = sizeof(getattributes)/sizeof (CK_ATTRIBUTE);
  if((rv=pfl->C_GetAttributeValue(sh,hct,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  for(i=0;i<(int)tsize;i++) {
    getattributes[i].pValue = myx_malloc(getattributes[i].ulValueLen); 
  }
  if((rv=pfl->C_GetAttributeValue(sh,hct,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
    for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
    return -1;
  }
  {
    i = getattributes[2].ulValueLen;
    p = (char *)myx_malloc(i+1);
    memcpy(p,getattributes[2].pValue,i);
    p[i] = '\0';
    myx_syslog(LOG_INFO,"label:%s\n",p);
    free(p);
  }
  if(flags == 1) goto endit;
  {
    uint8 *pl;
    pl = (uint8 *)getattributes[3].pValue;
    myx_syslog(LOG_INFO,"id:");
    for(i=0;i<(int)getattributes[3].ulValueLen;i++) myx_syslog(LOG_INFO,"%02x",pl[i]);
    myx_syslog(LOG_INFO,"\n");
  }
  if(0) {
    i = getattributes[1].ulValueLen;
    p = (char *)myx_malloc(i+1);
    memcpy(p,getattributes[1].pValue,i);
    p[i] = '\0';
    myx_syslog(LOG_INFO,"subject:%s\n",p);
    free(p);
  }
 endit:
  for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
  return 0;
}
static int display_privkey(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPriv,int flags)
{
  return print_privkeyinfo(sh,hPriv,flags);
}
static int display_pubkey(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPub,int flags)
{
  return print_pubkeyinfo(sh,hPub,flags);
}
static int display_secretkey(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hSkey,int flags)
{
  return display_privkey(sh,hSkey,flags);
}
static int display_certificate(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hSkey,int flags)
{
  return print_certinfo(sh,hSkey,flags);
}
static int display_data(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hSkey,int flags)
{
  return print_datainfo(sh,hSkey,flags);
}
static int display_key_value(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hKey)
{
  char *p;
  int k,n;
  CK_RV rv;
  CK_ULONG tsize;
  CK_ATTRIBUTE getattributes[] = {
    {CKA_LABEL,NULL_PTR,0},
    {CKA_CLASS,NULL_PTR,0},
    {CKA_VALUE,NULL_PTR,0},
  };
  
  tsize = sizeof(getattributes)/sizeof(CK_ATTRIBUTE);
  if((rv=pfl->C_GetAttributeValue(sh,hKey,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: A C_GetAttributeValue: %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  for(k=0;k<(int)tsize;k++) {
    getattributes[k].pValue = myx_malloc(getattributes[k].ulValueLen);
  }
  if((rv=pfl->C_GetAttributeValue(sh,hKey,getattributes,tsize)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: B C_GetAttributeValue: %s\n",pkcs11_ret_str(rv));
    for(n=0;n<(int)tsize;n++) free(getattributes[n].pValue);
    return -1;
  }
  
  k = getattributes[0].ulValueLen;
  p = (char *)myx_malloc(k+1);
  memcpy(p,getattributes[0].pValue,k);
  p[k] = '\0';
  myx_syslog(LOG_INFO,"label:%s\n",p);
  free(p);
  
  p = getattributes[2].pValue;
  for(n=0;n<(int)getattributes[2].ulValueLen;n++) myx_syslog(LOG_INFO,"%02X",(uint8)*p++);
  myx_syslog(LOG_INFO,"\n");

  for(n=0;n<(int)tsize;n++) free(getattributes[n].pValue);
  return 0;
}

static int maketranskey(CK_SESSION_HANDLE sh,char *importfile,CK_UTF8CHAR *label)
{
  int i;
  CK_RV rv;
  CK_ATTRIBUTE template[2];
  CK_OBJECT_HANDLE hSkey;
  CK_OBJECT_HANDLE hKeys[MAX_KEYS_PER_SLOT];
  CK_ULONG ofound;

  if(label == NULL || strlen((char *)label) == 0) {
    label = (CK_UTF8CHAR *)"dnssec transport key";
  }

  template[0].type = CKA_LABEL;
  template[0].pValue = label;
  template[0].ulValueLen = strlen((char *)label);
  if((rv=pfl->C_FindObjectsInit(sh,template,1)) != CKR_OK) { 
    myx_syslog(LOG_ERR,"pkcs11: error: C_FindObjectsInit: %s\n",pkcs11_ret_str(rv)); return -1; 
  }
  if((rv=pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_FindObjects: %s\n",pkcs11_ret_str(rv)); return -1;
  }
  if((rv=pfl->C_FindObjectsFinal(sh)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_FindObjectsFinal: %s\n",pkcs11_ret_str(rv)); return -1;
  }
  if(ofound > 0) {
    myx_syslog(LOG_INFO,"pkcs11: Key labeled %s exists (%d).\n",label,ofound);
    display_key_value(sh,hKeys[0]);
    return 0;
  }

  myx_syslog(LOG_ERR,"pkcs11: warnning: Could not find a transport key...creating one labeled:\"%s\"\n",label);

  if(0) { /* Local CKO_SECRET_KEY */
    /* 2011 05 19 - Athena SCS card creates the key+value but then will not return the value on second read.  returns all zeros. Works on AEP. Not on Feitian which does not support secret key gen. */
    CK_MECHANISM genmechanism = {
      CKM_AES_KEY_GEN /*CKM_GENERIC_SECRET_KEY_GEN /*CKM_DES3_KEY_GEN*/, NULL_PTR, 0
    };
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_AES; /*CKK_GENERIC_SECRET;  /*CKK_DES3;*/
    /* CK_BYTE value[24]; */
    CK_ULONG aeslen=24;
    CK_ATTRIBUTE keytmp[] = {
      {CKA_LABEL,NULL_PTR,0},
      {CKA_CLASS,&keyClass,sizeof(keyClass)},
      {CKA_KEY_TYPE,&keyType,sizeof(keyType)},
      {CKA_TOKEN,&ctrue,sizeof(ctrue)},/**/
      {CKA_ENCRYPT,&ctrue,sizeof(ctrue)},
      {CKA_DECRYPT,&ctrue,sizeof(ctrue)},
      /*{CKA_WRAP,&ctrue,sizeof(ctrue)},/**/
      /*{CKA_UNWRAP,&ctrue,sizeof(ctrue)},/**/
      {CKA_SENSITIVE,&cfalse,sizeof(cfalse)}, /* make CKA_VALUE readable */
      {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},
      {CKA_VALUE_LEN,&aeslen,sizeof(aeslen)},/**/
    };

    keytmp[0].pValue = label;
    keytmp[0].ulValueLen = strlen((char *)label);
    if((rv=pfl->C_GenerateKey(sh,&genmechanism,
			      keytmp,
			      (sizeof(keytmp)/sizeof(CK_ATTRIBUTE)),
			      &hSkey)) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GenerateKey %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }

  if(0) { /* External CKO_SECRET_KEY */
    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_DES3;
    CK_ATTRIBUTE template[] = {
      {CKA_CLASS, &class, sizeof (class) },
      {CKA_KEY_TYPE, &keyType, sizeof (keyType) },
      {CKA_TOKEN,&ctrue,sizeof(ctrue)},
      {CKA_LABEL,NULL,0},
      {CKA_VALUE,NULL,0},
      {CKA_ENCRYPT,&ctrue, sizeof(ctrue)},
      {CKA_DECRYPT,&ctrue, sizeof(ctrue)},

      {CKA_SENSITIVE,&cfalse, sizeof(ctrue)}, /* makes key readable */
      {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},

      /*{CKA_SENSITIVE,&ctrue, sizeof(ctrue)},*/
    };
    template[3].pValue = label;
    template[3].ulValueLen = strlen(label);
    template[4].pValue = "this is the data for the secret key in the HSM";
    template[4].ulValueLen = 24;

    if((rv=pfl->C_CreateObject(sh,template,sizeof(template)/sizeof(CK_ATTRIBUTE),&hSkey)) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: C_CreateObject: %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }

  if(1) { /* CKO_DATA - read from file or gen a random string */
    /* works with Feitian (opensc), athena scs, AEP */
    CK_OBJECT_CLASS class = CKO_DATA;
    CK_CHAR application[] = {"DNSSEC Transport Key"};
    CK_BYTE datavalue[256];
    CK_ATTRIBUTE template[] = {
      {CKA_CLASS,&class,sizeof (class)},
      {CKA_TOKEN,&ctrue,sizeof(ctrue)},
      {CKA_APPLICATION,application,sizeof(application)},
      {CKA_LABEL,NULL,0},
      {CKA_VALUE,NULL,0},
    };

    template[3].pValue = label;
    template[3].ulValueLen = strlen(label);

    if(importfile) {
      FILE *fp;
      if((fp=fopen(importfile,"rb")) == NULL) {
	myx_syslog(LOG_INFO,"pkcs11: error: Cannot open %s\n",importfile);
        return -1;
      }
      template[4].pValue = datavalue;
      template[4].ulValueLen = fread(datavalue,1,sizeof(datavalue),fp);
      fclose(fp);
    } else {
      if((rv=pfl->C_GenerateRandom(sh,datavalue,32)) != CKR_OK) {
	myx_syslog(LOG_INFO,"pkcs11: error: C_GenerateRandom %s\n",pkcs11_ret_str(rv));
	return -1;
      }
      template[4].pValue = datavalue;
      template[4].ulValueLen = 32;
    }

    if((rv=pfl->C_CreateObject(sh,template,sizeof(template)/sizeof(CK_ATTRIBUTE),&hSkey)) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: C_CreateObject: %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }

  myx_syslog(LOG_INFO,"pkcs11: Created new transport key labeled:\"%s\".\n",label);
  myx_syslog(LOG_INFO," You will need to manually copy this to your signer to decrypt keybundles generated during the key ceremonies.\n");
  display_key_value(sh,hSkey);

  return 0;
}
static int getkeyarray(CK_SESSION_HANDLE sh,CK_UTF8CHAR *label,CK_OBJECT_CLASS iclass,CK_OBJECT_HANDLE *hKeyA,int *ofound)
{
  int j;
  CK_ULONG n;
  CK_RV rv;
  CK_OBJECT_CLASS class;
  CK_ATTRIBUTE template[2];

  class = iclass;
  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &class;
  template[j].ulValueLen = sizeof(class);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen((char *)label);
    j++;
  }
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) return -1;
  rv = pfl->C_FindObjects(sh,hKeyA,MAX_KEYS_PER_SLOT,&n);
  if(rv != CKR_OK) return -1;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) return -1;
  if(n > 0) {
    if(label && n > 1) {
      myx_syslog(LOG_ERR,"pkcs11: error: Found more than one key matching label:\"%s\"\n",label);
      return -1;
    }
  } else {
    myx_syslog(LOG_ERR,"pkcs11: error: Found no private keys labeled:\"%s\"\n",label);
    return -1;
  }
  *ofound = n;
  return 0;
}
static int getwrapkey(CK_SESSION_HANDLE sh,CK_UTF8CHAR *label,CK_OBJECT_HANDLE *hWrappingKey,CK_MECHANISM *hmech)
{
  int i;
  CK_RV rv;
  CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY;
  CK_ATTRIBUTE template[2];
  CK_OBJECT_HANDLE hKeys[MAX_KEYS_PER_SLOT];
  CK_ULONG ofound;

  if(label == NULL || strlen((char *)label) == 0) {
    label = (CK_UTF8CHAR *)"dnssec backup key";
  }
  template[0].type = CKA_CLASS;
  template[0].pValue = &secretClass;
  template[0].ulValueLen = sizeof(secretClass);
  template[1].type = CKA_LABEL;
  template[1].pValue = label;
  template[1].ulValueLen = strlen((char *)label);
  if((rv=pfl->C_FindObjectsInit(sh,template,2)) != CKR_OK) { 
    myx_syslog(LOG_ERR,"pkcs11: error: C_FindObjectsInit %s\n",pkcs11_ret_str(rv)); return -1; 
  }
  if((rv=pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_FindObjects %s\n",pkcs11_ret_str(rv)); return -1;
  }
  if((rv=pfl->C_FindObjectsFinal(sh)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_FindObjectsFinal %s\n",pkcs11_ret_str(rv)); return -1;
  }
  if(ofound > 0) {
    *hWrappingKey = hKeys[0];
    if(ofound > 1) {
      myx_syslog(LOG_ERR,"pkcs11: error: Found %d (>1) wrapping keys with label:\"%s\"\n",ofound,label);
      for(i=0;i<(int)ofound;i++) {
	display_secretkey(sh,hKeys[i],0);
      }
      return -1;
    }

    {
      char *p;
      CK_ULONG tsize;
      CK_ATTRIBUTE getattributes[] = {
	{CKA_LABEL,NULL_PTR,0},
	{CKA_KEY_TYPE,NULL_PTR,0},
      };
      tsize = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);
      if((rv=pfl->C_GetAttributeValue(sh,*hWrappingKey,getattributes,tsize)) != CKR_OK) {
	myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
	return -1;
      }
      for(i=0;i<(int)tsize;i++) {
	getattributes[i].pValue = myx_malloc(getattributes[i].ulValueLen);
      }
      if((rv=pfl->C_GetAttributeValue(sh,*hWrappingKey,getattributes,tsize)) != CKR_OK) {
	myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
	for(i=0;i<(int)tsize;i++) free(getattributes[i].pValue);
	return -1;
      }
      
      i = getattributes[0].ulValueLen;
      p = (char *)myx_malloc(i+1);
      memcpy(p,getattributes[0].pValue,i);
      p[i] = '\0';
      
      if(getattributes[1].ulValueLen < sizeof(CK_KEY_TYPE)) myx_syslog(LOG_INFO,"type:error\n");
      switch(*(CK_KEY_TYPE *)getattributes[1].pValue) {
      case CKK_DES3: 
	/*myx_syslog(LOG_INFO,"  Using %s DES3\n",p); /**/
	hmech->mechanism = CKM_DES3_ECB;
	hmech->pParameter = NULL_PTR;
	hmech->ulParameterLen = 0;
	break;
      case CKK_AES: 
	/*myx_syslog(LOG_INFO,"  Using %s AES\n",p); /**/
	hmech->mechanism = CKM_AES_ECB;
	hmech->pParameter = NULL_PTR;
	hmech->ulParameterLen = 0;
	break;
      default: 
	hmech->mechanism = 0;
	hmech->pParameter = NULL_PTR;
	hmech->ulParameterLen = 0;
	myx_syslog(LOG_INFO,"  Using %s Unknown (%d)\n",p,*(CK_KEY_TYPE *)getattributes[1].pValue);/**/
	free(p);
	for(i=0;i<(int)tsize;i++) free(getattributes[i].pValue);
	return -1;
      }
      free(p);
      for(i=0;i<(int)tsize;i++) free(getattributes[i].pValue);
    }
    return 0;
  } else {
    myx_syslog(LOG_ERR,"pkcs11: warnning: Could not find a wrapping key...creating one labeled:\"%s\"\n",label);
  }

  if(0) {
    CK_ULONG dhbits = 512;
    CK_ATTRIBUTE dhdntmp[] = {
      {CKA_PRIME_BITS, &dhbits, sizeof(dhbits)},
    };
    CK_OBJECT_HANDLE hDHDN;
    CK_MECHANISM dhdgenmech = { CKM_DH_PKCS_PARAMETER_GEN, NULL_PTR, 0};
    rv = pfl->C_GenerateKey(sh,&dhdgenmech,dhdntmp,1,&hDHDN);
    if(rv != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GenerateKey %s\n",pkcs11_ret_str(rv));
      /*return -1;*/
    }
  }
  if(0) {
    CK_OBJECT_HANDLE hPublicKey, hPrivateKey;
    CK_BYTE prime[20];
    CK_BYTE base[20];
    CK_MECHANISM keyPairMechanism = { CKM_DH_PKCS_KEY_PAIR_GEN, NULL_PTR, 0};
    CK_ATTRIBUTE publicKeyTemplate[] = {
      {CKA_PRIME, prime, sizeof(prime)},
      {CKA_BASE, base, sizeof(base)}
    };
    CK_ATTRIBUTE privateKeyTemplate[] = {
      {CKA_DERIVE, &ctrue, sizeof(ctrue)}
    };
    rv = pfl->C_GenerateKeyPair(sh,&keyPairMechanism,
			  publicKeyTemplate,
			  (sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE)),
			  privateKeyTemplate, 
			  (sizeof(privateKeyTemplate)/sizeof(CK_ATTRIBUTE)),
			  &hPublicKey,&hPrivateKey);
    if(rv != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GenerateKeyPair %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }
  if(0) {
    CK_OBJECT_HANDLE hPublicKey;
    CK_BYTE publicValue[128];
    CK_ATTRIBUTE pTemplate[] = {
      CKA_VALUE, &publicValue, sizeof(publicValue)
    };
    rv = pfl->C_GetAttributeValue(sh,hPublicKey,pTemplate,1);
    if(rv != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }
  /* Put other guy.s public value in otherPublicValue */
  if(0) { /* derive a key from something we both know to Wrap */
    CK_OBJECT_HANDLE hPrivateKey;
    CK_BYTE otherPublicValue[128];
    CK_MECHANISM mechanism = {
      CKM_DH_PKCS_DERIVE, otherPublicValue, sizeof(otherPublicValue)
    };
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
    CK_KEY_TYPE keyType = CKK_DES;
    CK_ATTRIBUTE template[] = {
      {CKA_CLASS, &keyClass, sizeof(keyClass)},
      {CKA_KEY_TYPE, &keyType, sizeof(keyType)},
      {CKA_ENCRYPT, &ctrue, sizeof(ctrue)},
      {CKA_DECRYPT, &ctrue, sizeof(ctrue)}
    };

    rv = pfl->C_DeriveKey(sh,&mechanism,
				      hPrivateKey,
				      template,
				      4,
				      hWrappingKey);
    if (rv != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_DeriveKey %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }

  if(1) { /* gen a raw key to Wrap */
    CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
#ifdef LEGACY_WRAPPINGKEY
    CK_MECHANISM genmechanism = {
      CKM_DES3_KEY_GEN, NULL_PTR, 0
    };
    CK_KEY_TYPE keyType = CKK_DES3;
    /* CK_BYTE value[24]; */
    CK_ATTRIBUTE wkeytmp[] = {
      {CKA_LABEL,NULL_PTR,0},
      {CKA_CLASS,&keyClass,sizeof(keyClass)},
      {CKA_KEY_TYPE,&keyType,sizeof(keyType)},
      {CKA_TOKEN,&ctrue,sizeof(ctrue)},
      {CKA_ENCRYPT,&ctrue,sizeof(ctrue)},
      {CKA_DECRYPT,&ctrue,sizeof(ctrue)},
      {CKA_WRAP,&ctrue,sizeof(ctrue)},
      {CKA_UNWRAP,&ctrue,sizeof(ctrue)},
      {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},
      {CKA_SENSITIVE,&ctrue, sizeof(ctrue)}, /* makes CKA_VALUE fail */
      /*{CKA_VALUE, value, sizeof(value)}, */
    };
    hmech->mechanism = CKM_DES3_ECB;
    hmech->pParameter = NULL_PTR;
    hmech->ulParameterLen = 0;
#else
    CK_MECHANISM genmechanism = {
      CKM_AES_KEY_GEN, NULL_PTR, 0
    };
    CK_KEY_TYPE keyType = CKK_AES;
    CK_ULONG aeslen=32;  /* 256-bit AES */
    CK_ATTRIBUTE wkeytmp[] = {
      {CKA_LABEL,NULL_PTR,0},
      {CKA_CLASS,&keyClass,sizeof(keyClass)},
      {CKA_KEY_TYPE,&keyType,sizeof(keyType)},
      {CKA_TOKEN,&ctrue,sizeof(ctrue)},
      {CKA_ENCRYPT,&ctrue,sizeof(ctrue)},
      {CKA_DECRYPT,&ctrue,sizeof(ctrue)},
      {CKA_WRAP,&ctrue,sizeof(ctrue)},
      {CKA_UNWRAP,&ctrue,sizeof(ctrue)},
      {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},
      {CKA_SENSITIVE,&ctrue, sizeof(ctrue)}, /* makes CKA_VALUE fail */
      {CKA_VALUE_LEN, &aeslen, sizeof(aeslen)},/**/
    };
    hmech->mechanism = CKM_AES_ECB;
    hmech->pParameter = NULL_PTR;
    hmech->ulParameterLen = 0;
#endif

    wkeytmp[0].pValue = label;
    wkeytmp[0].ulValueLen = strlen((char *)label);
    if((rv=pfl->C_GenerateKey(sh,&genmechanism,
			      wkeytmp,
			      (sizeof(wkeytmp)/sizeof(CK_ATTRIBUTE)),
			      hWrappingKey)) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GenerateKey %s\n",pkcs11_ret_str(rv));
      return -1;
    }
  }
  if(0) {
    CK_BYTE publicValue[24];
    CK_UTF8CHAR label[128];
    CK_ATTRIBUTE pTemplate[] = {
      /*{CKA_VALUE, publicValue, sizeof(publicValue)},*/
      {CKA_LABEL, label, sizeof(label)-1},
    };
    
    memset(label,0,sizeof(label));
    for(i=0;i<24;i++) publicValue[i] = 0;

    rv = pfl->C_GetAttributeValue(sh,*hWrappingKey,
			    pTemplate,
			    (sizeof(pTemplate)/sizeof(CK_ATTRIBUTE)));
    if(rv != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
      return -1;
    }
    myx_syslog(LOG_INFO,"3DES \"%s\" (%x)=",label,*hWrappingKey);
    for(i=0;i<24;i++) myx_syslog(LOG_INFO,"%02x ",publicValue[i]);
    myx_syslog(LOG_INFO,"\n");
  }
  myx_syslog(LOG_INFO,"pkcs11: Created new wrapping key labeled:\"%s\".\n",label);
  myx_syslog(LOG_INFO," You will need to manually export this to other HSMs you plan on\n");
  myx_syslog(LOG_INFO," exchanging keys with using your HSM's specific backup procedures.\n");
  return 0;
}
static int listkeys(CK_SESSION_HANDLE sh,char *label)
{
  CK_RV rv;
  CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY;
  CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
  CK_OBJECT_CLASS secretClass = CKO_SECRET_KEY;
  CK_OBJECT_CLASS dataClass = CKO_DATA;
  CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
  CK_ATTRIBUTE template[2];
  CK_OBJECT_HANDLE hKeys[MAX_KEYS_PER_SLOT];
  CK_ULONG ofound;
  int i,j,flag;


  flag = 1; /* min */
  if(label) flag = 2; /* verbose */

  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &pubClass;
  template[j].ulValueLen = sizeof(pubClass);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen(label);
    j++;
  }    
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) goto endit;
  myx_syslog(LOG_INFO,"%d public keys:\n",ofound);
  for(i=0;i<(int)ofound;i++) {
    display_pubkey(sh,hKeys[i],flag);
  }

  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &privClass;
  template[j].ulValueLen = sizeof(privClass);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen(label);
    j++;
  }
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) goto endit;
  myx_syslog(LOG_INFO,"%d private keys:\n",ofound);
  for(i=0;i<(int)ofound;i++) {
    display_privkey(sh,hKeys[i],flag);
  }

  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &secretClass;
  template[j].ulValueLen = sizeof(secretClass);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen(label);
    j++;
  }
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) goto endit;
  myx_syslog(LOG_INFO,"%d secret keys:\n",ofound);
  for(i=0;i<(int)ofound;i++) {
    display_secretkey(sh,hKeys[i],flag);
  }

  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &certClass;
  template[j].ulValueLen = sizeof(dataClass);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen(label);
    j++;
  }
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) goto endit;
  myx_syslog(LOG_INFO,"%d certificates:\n",ofound);
  for(i=0;i<(int)ofound;i++) {
    display_certificate(sh,hKeys[i],flag);
  }

  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &dataClass;
  template[j].ulValueLen = sizeof(dataClass);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen(label);
    j++;
  }
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) goto endit;
  myx_syslog(LOG_INFO,"%d application objects:\n",ofound);
  for(i=0;i<(int)ofound;i++) {
    display_data(sh,hKeys[i],flag);
  }

  return 0;
 endit:
  return -1;
}
static int deletekey(CK_SESSION_HANDLE sh,CK_UTF8CHAR *label,CK_OBJECT_CLASS class)
{
  CK_RV rv;
  CK_ATTRIBUTE template[2];
  CK_OBJECT_HANDLE hKeys[MAX_KEYS_PER_SLOT];
  CK_OBJECT_CLASS lclass;
  CK_ULONG ofound;
  int nel;

  nel = 1;
  lclass = class;
  template[0].type = CKA_LABEL;
  template[0].pValue = label;
  template[0].ulValueLen = strlen((char *)label);
  if(lclass) {
    template[1].type = CKA_CLASS;
    template[1].pValue = &lclass;
    template[1].ulValueLen = sizeof(lclass);
    nel++;
  }
  rv = pfl->C_FindObjectsInit(sh,template,nel);
  if(rv != CKR_OK) return -1;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) return -1;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) return -1;
  if(ofound > 0) {
    delobject(sh,hKeys[0]);
    if(ofound > 1) {
      myx_syslog(LOG_ERR,"pkcs11: warnning: Found %d(>1) keys labeled:\"%s\"\n",ofound,label);
#ifdef FOOP
      for(i=1;i<ofound;i++) {
	display_pubkey(sh,hKeys[i],1);
	delobject(sh,hKeys[i]);
      }
#endif
    }
    return 0;
  } else {
    char *p;

    switch(lclass) {
    case CKO_PRIVATE_KEY: p = "private"; break;
    case CKO_PUBLIC_KEY: p = "public"; break;
    case CKO_SECRET_KEY: p = "secret"; break;
    case CKO_DATA: p = "data"; break;
    default: p = "unknown"; break;
    }
    myx_syslog(LOG_ERR,"pkcs11: error: No %s key labeled:\"%s\"\n",p,label);/**/
    return -1;
  }
}
static int havekey(CK_SESSION_HANDLE sh,CK_UTF8CHAR *label,CK_OBJECT_CLASS class)
{
  CK_RV rv;
  CK_ATTRIBUTE template[2];
  CK_OBJECT_HANDLE hKeys[MAX_KEYS_PER_SLOT];
  CK_OBJECT_CLASS lclass;
  CK_ULONG ofound;

  lclass = class;
  template[0].type = CKA_CLASS;
  template[0].pValue = &lclass;
  template[0].ulValueLen = sizeof(lclass);
  template[1].type = CKA_LABEL;
  template[1].pValue = label;
  template[1].ulValueLen = strlen((char *)label);
  rv = pfl->C_FindObjectsInit(sh,template,2);
  if(rv != CKR_OK) return -1;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) return -1;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) return -1;
  if(ofound > 0) {
    return 0;
  } else {
    return -1;
  }
}

#define FREE_AND_CLEAR(x) { if(x) { free(x); x = NULL; } }

static int read_keys_into_hsm(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hWrappingkey,FILE *fp,CK_MECHANISM uwmechanism)
{
  char *p64,*p,lbuf[512];
  int n,j;
  char *label;
  uint8 *id;
  int idlen,moduluslen,exponentlen;
  CK_RV rv;
  CK_BYTE *modulus,*exponent;
  CK_OBJECT_CLASS keyclass;
  CK_KEY_TYPE keytype;
  CK_OBJECT_HANDLE htmp;
  CK_BYTE *wrappedkey;
  CK_ULONG wrappedkeylen;

  label = NULL;
  id = NULL;
  modulus = NULL;
  exponent = NULL;
  wrappedkey = NULL;
  p64 = NULL;
  while(fgets(lbuf,sizeof(lbuf),fp)) {
    if(lbuf[0] == '#') continue;
    cleanup(lbuf);
    j = strlen(lbuf);
    p = strchr(lbuf,':');
    if(p64) {
      if(j > 0 && p == NULL) strcat(p64,lbuf);
      if(strchr(lbuf,'=') || j < PEM_LINE_LENGTH || p) {
	char *q,*r;

	/*printf("%s\n",p64);*/

	if((r=strchr(p64,':')) == NULL) {
	  myx_syslog(LOG_ERR,"error: malformed base64 key file format\n");
	  goto err64;
	}
	*r++ = '\0';
	if((q=(char *)myx_malloc(strlen(r)+4)) == NULL) {
	  myx_syslog(LOG_ERR,"error: out of memory in %s\n",__func__);
	  goto err64;
	}
	if((n=base64decode(r,(uint8 *)q)) < 0) {
	  myx_syslog(LOG_ERR,"error: malformed base64 encoding in key file\n");
	  free(q);
	  goto err64;
	}
	if(strcmp(p64,"modulus") == 0) {
	  modulus = (CK_BYTE *)q;
	  moduluslen = n;
	} else if(strcmp(p64,"exponent") == 0) {
	  exponent = (CK_BYTE *)q;
	  exponentlen = n;
	} else if(strcasecmp(p64,"wrappedkey") == 0) {
	  wrappedkey = (CK_BYTE *)q;
	  wrappedkeylen = n;
	} else {
	  myx_syslog(LOG_ERR,"warning: unknown key record |%s|\n",p64);
	  free(q);
	}
      err64:
	free(p64);
	p64 = NULL;
      }
    }
    if(j == 0) { /* try to import the key */
      if(label == NULL) continue; /* superfulous <LF> */
      /* check to see if key with this label is already in HSM */
      if(havekey(sh,(CK_UTF8CHAR *)label,keyclass) == 0) {
#ifdef FOOP
	myx_syslog(LOG_ERR,"Key labeled \"%s\" already in HSM.  Exiting...\n",label);
        goto endit;
#else
	myx_syslog(LOG_ERR,"Key labeled \"%s\" already in HSM.  Skiping...\n",label);
	FREE_AND_CLEAR(label);
	FREE_AND_CLEAR(id);
	FREE_AND_CLEAR(modulus);
	FREE_AND_CLEAR(exponent);
	FREE_AND_CLEAR(wrappedkey);
	continue;
#endif
      }

      myx_syslog(LOG_INFO,"importing %s\n",label);
      if(keyclass == CKO_PUBLIC_KEY) {
	CK_ATTRIBUTE template[] = {
	  {CKA_CLASS,&keyclass,sizeof(keyclass)},
	  {CKA_KEY_TYPE,&keytype,sizeof(keytype)},
	  {CKA_TOKEN,&ctrue,sizeof(ctrue)},
	  {CKA_LABEL,NULL_PTR,0},
	  {CKA_ID,NULL_PTR,0},
	  {CKA_WRAP,&ctrue,sizeof(ctrue)},
	  {CKA_ENCRYPT,&ctrue,sizeof(ctrue)},
	  {CKA_MODULUS,NULL_PTR,0},
	  {CKA_PUBLIC_EXPONENT,NULL_PTR,0},
	  {CKA_VERIFY,&ctrue,sizeof(ctrue)},
	  {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},
	};
	if(label == NULL || modulus == NULL || exponent == NULL) {
	  myx_syslog(LOG_ERR,"pkcs11: error: incomplete info for public key\n");
	  goto endit;
	}

#ifdef FOOP  /* redundant */
	CK_ATTRIBUTE publicKeyTemplate[] = {
	  { CKA_MODULUS_BITS,        &keysize, sizeof(keysize) },
	};
	printf("bits = %d\n",moduluslen*8);
#endif

	template[3].pValue = (CK_UTF8CHAR *)label;
	template[3].ulValueLen = strlen(label);
        template[4].pValue = id;
        template[4].ulValueLen = idlen;
	template[7].pValue = modulus;
	template[7].ulValueLen = moduluslen;
	template[8].pValue = exponent;
	template[8].ulValueLen = exponentlen;
	if((rv=pfl->C_CreateObject(sh,
				   template,
				   sizeof(template)/sizeof(CK_ATTRIBUTE),
				   &htmp)) != CKR_OK) {
	  myx_syslog(LOG_ERR,"pkcs11: error: C_CreateObject %s\n",pkcs11_ret_str(rv));
	  goto endit;
	}
	FREE_AND_CLEAR(label);
	FREE_AND_CLEAR(id);
	FREE_AND_CLEAR(modulus);
	FREE_AND_CLEAR(exponent);
	FREE_AND_CLEAR(wrappedkey);
      } else if(keyclass == CKO_PRIVATE_KEY) {
	CK_ATTRIBUTE template[] = {
	  {CKA_LABEL,NULL_PTR,0},
	  {CKA_ID,NULL_PTR,0},
	  {CKA_CLASS,&keyclass,sizeof(keyclass)},
	  {CKA_KEY_TYPE,&keytype,sizeof(keytype)},
	  {CKA_TOKEN,&ctrue,sizeof(ctrue)},
	  {CKA_PRIVATE,&ctrue,sizeof(ctrue)},
	  {CKA_SENSITIVE,&ctrue,sizeof(ctrue)},
	  {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},
	  {CKA_SIGN,&ctrue,sizeof(ctrue)},
	  {CKA_DECRYPT,&ctrue,sizeof(ctrue)},
	};
	if(label == NULL || wrappedkey == NULL) {
          myx_syslog(LOG_ERR,"pkcs11: error: incomplete info for private key\n");
          goto endit;
        }

        template[0].pValue = (CK_UTF8CHAR *)label;
        template[0].ulValueLen = strlen(label);
        template[1].pValue = id;
        template[1].ulValueLen = idlen;
	if((rv=pfl->C_UnwrapKey(sh,&uwmechanism,
				hWrappingkey,
				wrappedkey,
				wrappedkeylen,
				template,
				(sizeof(template)/sizeof(CK_ATTRIBUTE)),
				&htmp)) != CKR_OK) {
	  myx_syslog(LOG_ERR,"pkcs11: error: C_UnWrapKey %s\n",pkcs11_ret_str(rv));
	  goto endit;
	}
        FREE_AND_CLEAR(label);
        FREE_AND_CLEAR(id);
        FREE_AND_CLEAR(modulus);
        FREE_AND_CLEAR(exponent);
        FREE_AND_CLEAR(wrappedkey);
      } else if(keyclass == CKO_SECRET_KEY) {
        CK_ATTRIBUTE template[] = {
          {CKA_LABEL,NULL_PTR,0},
          {CKA_ID,NULL_PTR,0},
          {CKA_CLASS,&keyclass,sizeof(keyclass)},
          {CKA_KEY_TYPE,&keytype,sizeof(keytype)},
          {CKA_TOKEN,&ctrue,sizeof(ctrue)},
          {CKA_EXTRACTABLE,&ctrue,sizeof(ctrue)},
          {CKA_ENCRYPT,&ctrue,sizeof(ctrue)},
          {CKA_DECRYPT,&ctrue,sizeof(ctrue)},
	  {CKA_WRAP, &ctrue, sizeof(ctrue)},
	  {CKA_UNWRAP, &ctrue, sizeof(ctrue)},
        };
        if(label == NULL || wrappedkey == NULL) {
          myx_syslog(LOG_ERR,"pkcs11: error: incomplete info for secret key\n");
          goto endit;
        }
        template[0].pValue = (CK_UTF8CHAR *)label;
        template[0].ulValueLen = strlen(label);
        template[1].pValue = id;
        template[1].ulValueLen = idlen;
        if((rv=pfl->C_UnwrapKey(sh,&uwmechanism,
                                hWrappingkey,
                                wrappedkey,
                                wrappedkeylen,
                                template,
                                (sizeof(template)/sizeof(CK_ATTRIBUTE)),
                                &htmp)) != CKR_OK) {
          myx_syslog(LOG_ERR,"pkcs11: error: C_UnWrapKey %s\n",pkcs11_ret_str(rv));
          goto endit;
        }
        FREE_AND_CLEAR(label);
        FREE_AND_CLEAR(id);
        FREE_AND_CLEAR(modulus);
        FREE_AND_CLEAR(exponent);
        FREE_AND_CLEAR(wrappedkey);
      } else {
	myx_syslog(LOG_ERR,"pkcs11: error: trying to import unknown key class\n");
	goto endit;
      }
      continue;
    }
    if(p64) continue;
    if(p == NULL) { /* last line of base64 encoding */
      /*fprintf(stderr,"warning: keyfile has malformed line:\n|%s|\n",lbuf);*/
      continue;
    }
    *p++ = '\0';
    if(strcmp(lbuf,"label") == 0) {
      label = strdup(p);
    } else if(strcmp(lbuf,"id") == 0) {
      char *q;
      int k;

      idlen = strlen(p)/2;
      id = (uint8 *)myx_malloc(idlen);
      for(q=p,k=0;k<idlen;k++,q += 2) {
	id[k] = hex2i(*q)<<4 | hex2i(*(q+1));
      }
    } else if(strcmp(lbuf,"type") == 0) {
      if(strcmp(p,"rsa") == 0) {
	keytype = CKK_RSA;
      } else if(strcmp(p,"dsa") == 0) {
	keytype = CKK_DSA;
      } else if(strcmp(p,"des3") == 0) {
	keytype = CKK_DES3;
      } else if(strcmp(p,"aes") == 0) {
        keytype = CKK_AES;
      } else {
	keytype = 0;
	myx_syslog(LOG_ERR,"error: trying to import unknown key type |%s|\n",p);
      }
    } else if(strcmp(lbuf,"class") == 0) {
      if(strcmp(p,"private") == 0) {
        keyclass = CKO_PRIVATE_KEY;
      } else if(strcmp(p,"public") == 0) {
        keyclass = CKO_PUBLIC_KEY;
      } else if(strcmp(p,"secret") == 0) {
        keyclass = CKO_SECRET_KEY;
      } else {
        keyclass = 0;
        myx_syslog(LOG_ERR,"error: trying to import unknown key class |%s|\n",p);
      }
    } else if(strcmp(lbuf,"modulus") == 0
	      || strcmp(lbuf,"exponent") == 0
	      || strcasecmp(lbuf,"wrappedkey") == 0) {
      if((p64=(char *)myx_malloc(2048)) == NULL) {
	myx_syslog(LOG_ERR,"error out of memory in %s\n",__func__);
	continue;
      }
      sprintf(p64,"%s:",lbuf);
    } else {
      myx_syslog(LOG_ERR,"warning: unknown key record |%s|\n",lbuf);
    }
  }
  return 0;
 endit:
  FREE_AND_CLEAR(label);
  FREE_AND_CLEAR(id);
  FREE_AND_CLEAR(modulus);
  FREE_AND_CLEAR(exponent);
  FREE_AND_CLEAR(wrappedkey);
  return -1;
}
static int wrap_and_export_privkey(CK_SESSION_HANDLE sh,CK_OBJECT_HANDLE hPriv,CK_OBJECT_HANDLE hWrappingKey,CK_MECHANISM wmechanism)
{
  CK_RV rv;
  int ret;
  CK_BYTE *wrappedKeyBuf;
  CK_ULONG wkeybuflen;

  ret = -1;
  wkeybuflen = 4096; /* > ((4096bit max keylen) / (8bits/byte)) = 512 x 2 for priv exponent and other RSA key material */
  if((wrappedKeyBuf=(CK_BYTE *)myx_malloc(wkeybuflen)) == NULL) goto endit;

  if((rv=pfl->C_WrapKey(sh,&wmechanism,
			hWrappingKey,
			hPriv,
			wrappedKeyBuf,&wkeybuflen)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_WrapKey %s\n",pkcs11_ret_str(rv));
    goto endit;
  }

  /*rdump(wrappedKeyBuf,wkeybuflen); */
  if(print_privkeyinfo(sh,hPriv,0)) goto endit;
  {
    int i,j;
    char *pl,*pl0;
    /*pl = pl0 = (char *)myx_malloc(((4*(wkeybuflen+1))/3) + 1);*/
    pl = pl0 = (char *)myx_malloc(2*wkeybuflen + 1);

    base64encode(pl,wrappedKeyBuf,wkeybuflen);
    myx_syslog(LOG_INFO,"wrappedkey:\n");
    j = strlen(pl);
    while(j > 0) {
      for(i=0;i<min(j,PEM_LINE_LENGTH);i++) myx_syslog(LOG_INFO,"%c",*pl++);
      myx_syslog(LOG_INFO,"\n");
      j -= PEM_LINE_LENGTH;
    }
    free(pl0);
  }
  myx_syslog(LOG_INFO,"\n"); /* end of key */
  ret = 0;
 endit:
  if(wrappedKeyBuf) free(wrappedKeyBuf);
  return ret;
}

static int dosetenv(char *efile)
{
  FILE *fp;
  char lbuf[MAXPATHLEN];

  if((fp=fopen(efile,"r")) == NULL) {
    myx_syslog(LOG_ERR,"error: Cant open %s\n",efile);
    /*continue;*/
    return -1;
  }
  {
    char *p;
    if((p=strrchr(efile,'/'))) p++; else p = efile;
    misc_hsmconfig = strdup(p);
  }
  /*fprintf(stderr,"Using hsmconfig %s\n",efile);*/
  while(fgets(lbuf,sizeof(lbuf),fp)) {
    int n;
#define NARGS 20
    char *args[NARGS];
    
    if(lbuf[0] == '#') continue;
    n = lparse(lbuf,args,NARGS,'=');
    if(n < 1) continue;
    if(n == 1) unsetenv(args[0]);
    else {
      char obuf[2048],*p,*q,*ev,c,*r;
      q = obuf;
      p = args[1];
      ev = NULL;
      while(*p) {
	if(ev) {
	  if(*p == '|' || *p == '&' || *p == ':' || *p == ';' || *p == ' ' || *p == '\t' || *p == '/' || *p == '\\' || *p == '(' || *p == ')' || *p == '<' || *p == '>' || *(p+1) == '\0') {
	    
	    if(*(p+1) == '\0') p++;
	    
	    c = *p;
	    *p = '\0';
	    if((r=getenv(ev))) {
	      while(*r) *q++ = *r++;
	      *q++ = c;
	    } else {
	      /*fprintf(stderr,"Could not get environment variable \"%s\"\n",ev);  may be NULL */
	    }
	    ev = NULL;
	  } else {
	  }
	} else {
	  if(*p == '$') ev = p+1;
	  else *q++ = *p;
	}
	p++;
      }
      *q++ = '\0';
      /*printf("%s=%s\n",args[0],obuf); */
      setenv(args[0],obuf,1);
    }
  }
  fclose(fp);
  return 0;
}

#include <dirent.h>

static int scanhsms(char *configfile)
{
  char *p,lbuf[MAXPATHLEN];
  char fname[MAXPATHLEN];
  DIR *dirp;
  struct dirent *dp;
  char *scandir;
  int ret;

  if(configfile) {
    dosetenv(configfile);
    if((p=getenv("PKCS11_LIBRARY_PATH")) == NULL) {
      myx_syslog(LOG_ERR,"error: You must set at least PKCS11_LIBRARY_PATH\n");
      return -1;
    }
    return 0;
  }

  scandir = ".";
  ret = -1;

  if((dirp = opendir(scandir)) == (DIR *)0) {
    myx_syslog(LOG_ERR,"Cannot open %s directory\n",scandir);
    return ret;
  }

  while((dp = readdir(dirp))) {

    if(strcmp(dp->d_name,".") == 0 || strcmp(dp->d_name,"..") == 0)
      continue;

    if((p=strrchr(dp->d_name,'.')) == NULL) continue;
    if(strcmp(p,".hsmconfig")) continue;
      
    sprintf(fname,"%s/%s",scandir,dp->d_name);
      
    printf("Use HSM %s (y/N)?: ",fname);
    if(fgets(lbuf,sizeof(lbuf),stdin) == NULL) {
      printf("\n");
      myx_syslog(LOG_INFO,"HSM %s NOT activated.\n",fname);
      continue;
    } else {
      printf("\n");
    }
    if(lbuf[0] != 'y' && lbuf[0] != 'Y') {
      myx_syslog(LOG_INFO,"HSM %s NOT activated.\n",fname);
      continue;
    }

    if(dosetenv(fname)) break;

    if((p=getenv("PKCS11_LIBRARY_PATH")) == NULL) {
      myx_syslog(LOG_ERR,"error: You must set at least PKCS11_LIBRARY_PATH\n");
      /*continue;*/
      break;
    }
    /*fprintf(stderr,"PKCS11_LIBRARY_PATH=%s\n",p);*/
    ret = 0;
    break;
  }
  closedir(dirp);
  return ret;
}
static int genrsakey(CK_SESSION_HANDLE sh,char *label,int bits)
{
  int              rv;
  CK_OBJECT_HANDLE hPub,hPriv;
  CK_OBJECT_CLASS  class_public_key = CKO_PUBLIC_KEY;
  CK_OBJECT_CLASS  class_private_key = CKO_PRIVATE_KEY;
  CK_KEY_TYPE      key_type=CKK_RSA;
  CK_ULONG         modulusBits;
  CK_BYTE          rsa_exponent[] = {0x01,0x00,0x01};
  CK_MECHANISM mechanism_gen = {CKM_RSA_PKCS_KEY_PAIR_GEN,NULL_PTR,0};
  CK_ATTRIBUTE publicKeyTemplate[] = {
    {CKA_LABEL,NULL,0},
    {CKA_ID,NULL,0}, /* arb bytes string */
    {CKA_CLASS,&class_public_key,sizeof(class_public_key)},
    {CKA_KEY_TYPE,&key_type,sizeof(key_type)},
    {CKA_TOKEN,&ctrue,sizeof(CK_BBOOL)}, /* bTrue if put in HSM */
    {CKA_ENCRYPT,&ctrue,sizeof(CK_BBOOL)},
    {CKA_VERIFY,&ctrue,sizeof(CK_BBOOL)},
#ifndef USING_TPM
    {CKA_EXTRACTABLE,&ctrue,sizeof(CK_BBOOL)}, // TPM-Opencryptoki complains if true
#endif
    {CKA_MODULUS_BITS,&modulusBits,sizeof(modulusBits)},
    {CKA_PUBLIC_EXPONENT,rsa_exponent,sizeof(rsa_exponent)},
  };
  CK_ATTRIBUTE privateKeyTemplate[] = {
    {CKA_LABEL,NULL,0},
    {CKA_ID,NULL,0}, /* arb bytes string */
    {CKA_CLASS,&class_private_key,sizeof(class_private_key)},
    {CKA_KEY_TYPE,&key_type,sizeof(key_type)},
    {CKA_TOKEN,&ctrue,sizeof(CK_BBOOL)}, /* bTrue if put in HSM */
    {CKA_DECRYPT,&ctrue,sizeof(CK_BBOOL)},
    {CKA_SIGN,&ctrue,sizeof(CK_BBOOL)},
#ifndef USING_TPM
    {CKA_EXTRACTABLE,&ctrue,sizeof(CK_BBOOL)}, // bTrue if API EXPORT enabled. X-TPM-Opencryptoki.
#endif
    {CKA_DERIVE,&ctrue,sizeof(CK_BBOOL)},
    {CKA_SENSITIVE,&ctrue,sizeof(CK_BBOOL)},
    {CKA_PRIVATE,&ctrue,sizeof(CK_BBOOL)},
  };
  int n;

  if(havekey(sh,(CK_UTF8CHAR *)label,class_public_key) == 0) {
    myx_syslog(LOG_ERR,"Key labeled \"%s\" already in HSM.\n",label);
    return -1;
  }
  modulusBits = bits;
  n = strlen(label);

  publicKeyTemplate[0].pValue = label;
  publicKeyTemplate[0].ulValueLen = n;

  publicKeyTemplate[1].pValue = label;
  publicKeyTemplate[1].ulValueLen = n;

  privateKeyTemplate[0].pValue = label;
  privateKeyTemplate[0].ulValueLen = n;

  privateKeyTemplate[1].pValue = label;
  privateKeyTemplate[1].ulValueLen = n;

  if((rv=pfl->C_GenerateKeyPair(sh,
      &mechanism_gen,
      publicKeyTemplate,
      (sizeof(publicKeyTemplate)/sizeof(CK_ATTRIBUTE)),
      privateKeyTemplate,
      (sizeof(privateKeyTemplate)/sizeof(CK_ATTRIBUTE)),
      &hPub,&hPriv)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GenerateKeyPair %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  return 0;
}

static int getrandom(CK_SESSION_HANDLE sh,FILE *fout,int nbytes)
{
  char lbuf[1024];
  int n,rv,total;
  unsigned long nc;

  /* if nbytes < 0 then loop forever */
  nc = 0;
  total = 0;
  while(nbytes < 0) {
    rv = pfl->C_GenerateRandom(sh,(CK_BYTE_PTR)lbuf,128);
    if(rv != CKR_OK) {
      myx_syslog(LOG_INFO,"pkcs11: error: C_GenerateRandom %s\n",pkcs11_ret_str(rv));
      return 0;
    }
    if((n=fwrite(lbuf,1,128,fout)) != 128) {
      nc += n;
      myx_syslog(LOG_INFO,"Output %u random bytes\n",nc);
      return 0;
    }
    nc += n;
    total += n;
    if(total > 1250) { printf("."); fflush(stdout); total = 0; }
  }
  /* otherwise just nbytes */
  memset(lbuf,0,sizeof(lbuf));
  while(nbytes > 0) {
    n = min(sizeof(lbuf),nbytes);
    rv = pfl->C_GenerateRandom(sh,(CK_BYTE_PTR)lbuf,n);
    if(rv != CKR_OK) {
      myx_syslog(LOG_INFO,"pkcs11: error: C_GenerateRandom %s\n",pkcs11_ret_str(rv));
      return 0;
    }
    fwrite(lbuf,1,n,fout);
    nbytes -= n;
  }
  return 0;
}

#include <openssl/rsa.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/bn.h>
static int pass_cb(char *buf,int size,int rwflag,void *u)
{
  int len;
  char lbuf[256];

  /* We'd probably do something else if 'rwflag' is 1 */
  printf("Enter pass phrase for \"%s\"\n",(char *)u);
  /* get pass phrase, length 'len' into 'tmp' */
  fgets(lbuf,sizeof(lbuf),stdin);
  cleanup(lbuf);
  len = strlen(lbuf);
  if (len <= 0) return 0;
  /* if too long, truncate */
  if (len > size) len = size;
  memcpy(buf,lbuf,len);
  return len;
}
static int import_openssl_private_key(CK_SESSION_HANDLE sh,char *kfname,char *label)
{
  RSA *rsa;
  FILE *ft;
  char *p,lbuf[MAXPATHLEN],*args[NARGS];

  CK_RV                 rv;
  CK_OBJECT_HANDLE      hKey;
  CK_KEY_TYPE keyType = CKK_RSA;
  CK_BYTE id[128];
  CK_OBJECT_CLASS class = CKO_PRIVATE_KEY;
  CK_BYTE modulus[1024];
  CK_BYTE publicExponent[1024];
  CK_BYTE privateExponent[1024];
  CK_BYTE prime1[1024];
  CK_BYTE prime2[1024];
  CK_BYTE exponent1[1024];
  CK_BYTE exponent2[1024];
  CK_BYTE coefficient[1024];
  CK_ATTRIBUTE priv[] = {
    {CKA_MODULUS, modulus,sizeof(modulus)},
    {CKA_PUBLIC_EXPONENT, publicExponent,sizeof(publicExponent)},
    {CKA_PRIVATE_EXPONENT, privateExponent,sizeof(privateExponent)},
    {CKA_PRIME_1, prime1, sizeof(prime1)},
    {CKA_PRIME_2, prime2, sizeof(prime2)},
    {CKA_EXPONENT_1, exponent1, sizeof(exponent1)},
    {CKA_EXPONENT_2, exponent2, sizeof(exponent2)},
    {CKA_COEFFICIENT, coefficient, sizeof(coefficient)},

    {CKA_CLASS, &class, sizeof(class)},
    {CKA_KEY_TYPE, &keyType, sizeof(keyType)},
    {CKA_LABEL, label, sizeof(label)-1},
    {CKA_ID, id, sizeof(id)},

    {CKA_SENSITIVE, &ctrue, sizeof(ctrue)},
    {CKA_DECRYPT, &ctrue, sizeof(ctrue)},
    {CKA_SIGN, &ctrue, sizeof(ctrue)},

    {CKA_TOKEN, &ctrue, sizeof(ctrue)},
  };
  CK_OBJECT_CLASS pubclass = CKO_PUBLIC_KEY;
  CK_ATTRIBUTE pub[] = {
    {CKA_MODULUS, modulus,sizeof(modulus)},
    {CKA_PUBLIC_EXPONENT, publicExponent,sizeof(publicExponent)},

    {CKA_CLASS, &pubclass, sizeof (pubclass) },
    {CKA_KEY_TYPE, &keyType, sizeof (keyType) },
    {CKA_LABEL, label, sizeof(label)-1},
    {CKA_ID, id, sizeof(id)},

    {CKA_TOKEN, &ctrue, sizeof(ctrue)},

    {CKA_VERIFY, &ctrue, sizeof(ctrue) },

    {CKA_WRAP, &ctrue, sizeof(ctrue)},
    {CKA_ENCRYPT, &ctrue, sizeof(ctrue)},
  };

  /* Init OPENSSL */
  SSL_load_error_strings();
  /* SSLeay_add_ssl_algorithms();/**/

  /* Start memory check */
  /*CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);/**/

  /* Init of error messages */
  ERR_load_crypto_strings();

  /*SSLeay_add_all_algorithms();/**/
  OpenSSL_add_all_algorithms();

  if((ft=fopen(kfname,"r")) == NULL) {
    fprintf(stderr,"error: can not open %s\n",kfname);
    return -1;
  }
  rsa = PEM_read_RSAPrivateKey(ft,NULL,pass_cb,kfname);
  fclose(ft);
  if(rsa == NULL) {
    fprintf(stderr,"error: cant read private key from %s.\n",kfname);
    return -1;
  }

  priv[0].ulValueLen =  BN_bn2bin(rsa->n,modulus);
  pub[0].ulValueLen = priv[0].ulValueLen;

  priv[1].ulValueLen =  BN_bn2bin(rsa->e,publicExponent);
  pub[1].ulValueLen = priv[1].ulValueLen;

  priv[2].ulValueLen =  BN_bn2bin(rsa->d,privateExponent);
  priv[3].ulValueLen =  BN_bn2bin(rsa->p,prime1);
  priv[4].ulValueLen =  BN_bn2bin(rsa->q,prime2);
  priv[5].ulValueLen =  BN_bn2bin(rsa->dmp1,exponent1);
  priv[6].ulValueLen =  BN_bn2bin(rsa->dmq1,exponent2);
  priv[7].ulValueLen =  BN_bn2bin(rsa->iqmp,coefficient);
  RSA_free(rsa);

  priv[10].ulValueLen =  strlen(label);
  pub[4].ulValueLen = priv[10].ulValueLen;

  priv[11].ulValueLen =  sprintf(id,"%s",label);
  pub[5].ulValueLen = priv[11].ulValueLen;

  if((rv=pfl->C_CreateObject(sh,priv,sizeof(priv)/sizeof(CK_ATTRIBUTE),&hKey)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: Private C_CreateObject: %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  if((rv=pfl->C_CreateObject(sh,pub,sizeof(pub)/sizeof(CK_ATTRIBUTE),&hKey)) != CKR_OK) {
    /* should delete last one */
    myx_syslog(LOG_ERR,"pkcs11: Public C_CreateObject: %s\n",pkcs11_ret_str(rv));
    return -1;
  }

  return 0;
}
static int import_openssl_cert(CK_SESSION_HANDLE sh,char *kfname,char *label)
{
  FILE *ft;
  char *p,lbuf[MAXPATHLEN],*args[NARGS];

  CK_RV                 rv;
  CK_OBJECT_HANDLE      hKey;
  CK_BYTE id[128];
  X509 *x509;
  CK_BYTE *subject,_subject[1024];
  CK_BYTE *certvalue,_certvalue[2048];
  CK_OBJECT_CLASS  certclass = CKO_CERTIFICATE;
  CK_CERTIFICATE_TYPE certtype = CKC_X_509;
  CK_ATTRIBUTE cert[] = {
    {CKA_VALUE, NULL, 0},
    {CKA_CLASS, &certclass, sizeof (certclass) },
    {CKA_CERTIFICATE_TYPE, &certtype, sizeof(certtype) },
    {CKA_SUBJECT, NULL, 0},
    {CKA_LABEL, label, sizeof(label)-1},
    {CKA_ID, id, sizeof(id)},
    {CKA_TOKEN, &ctrue, sizeof(ctrue)},
  };

  /* Init OPENSSL */
  SSL_load_error_strings();
  /* SSLeay_add_ssl_algorithms();/**/

  /* Start memory check */
  /*CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);/**/

  /* Init of error messages */
  ERR_load_crypto_strings();

  /*SSLeay_add_all_algorithms();/**/
  OpenSSL_add_all_algorithms();

  if((ft=fopen(kfname,"r")) == NULL) {
    fprintf(stderr,"error: can not open %s\n",kfname);
    return -1;
  }
  x509 = PEM_read_X509(ft, NULL, NULL, NULL);
  fclose(ft);

  subject = _subject;
  if((cert[3].ulValueLen = i2d_X509_NAME(x509->cert_info->subject,&subject)) < 0) {
    fprintf(stderr,"Cannot extract subject from cert\n");
    return -1;
  }
  cert[3].pValue = _subject;

  certvalue = _certvalue;
  if((cert[0].ulValueLen = i2d_X509(x509,&certvalue)) < 0) {
    fprintf(stderr,"Cannot extract certificate value\n");
    return -1;
  }
  cert[0].pValue = _certvalue;

  X509_free(x509);

  cert[4].pValue = label;
  cert[4].ulValueLen = strlen(label);

  cert[5].ulValueLen = sprintf(id,"%s",label);

  if((rv=pfl->C_CreateObject(sh,cert,sizeof(cert)/sizeof(CK_ATTRIBUTE),&hKey)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: C_CreateObject: %s\n",pkcs11_ret_str(rv));
    return -1;
  }

  return 0;
}

#ifdef linux
#include <stdbool.h>
#include <poll.h>
#include <signal.h>
#include <fcntl.h>
#include <linux/types.h>
#include <linux/random.h>
#include <sys/ioctl.h>

static int berandom(CK_SESSION_HANDLE sh)
{
  struct pollfd pfds[1] = {
    { .events = POLLOUT }
  };
  int rfd,n,rv,first,total;
  uint8 lbuf[128];
  struct rand_pool_info *rpool;

  total = 0;
  first = 0;
  n = sizeof(lbuf);
  if((rfd=open("/dev/random",O_RDWR)) < 0) {
    myx_syslog(LOG_INFO,"error: cannot open /dev/random\n");
    return -1;
  }
  rpool = alloca(sizeof(struct rand_pool_info) + n);
  pfds[0].fd = rfd;
  while(poll(pfds,1,-1) != -1) {
    if(pfds[0].revents & POLLOUT) {
      pfds[0].events = 0;
      if((rv=pfl->C_GenerateRandom(sh,(CK_BYTE_PTR)lbuf,n))!= CKR_OK) {
        myx_syslog(LOG_INFO,"pkcs11: error: C_GenerateRandom %s\n",
                   pkcs11_ret_str(rv));
        break;
      }
      rpool->buf_size = n;
      rpool->entropy_count = 7*n;  /* 1-7 x n */
      memcpy(rpool->buf,lbuf,n);
      if(ioctl(rfd,RNDADDENTROPY,rpool) == -1) {
        perror("ioctl");
        myx_syslog(LOG_INFO,"error: Cannot access /dev/random pool. Check privledges.\n");
        close(rfd);
        return -1;
      }
      pfds[0].events = POLLOUT;

      total += 128;
      if(total > 1250) { printf("."); fflush(stdout); total = 0; }

      if(first) {
	first = 0;
	if(fork() != 0) { exit(0);  /* terminate parent */ }
	/* child: */
	setsid(); /* become session leader and group process leader
		     with no controlling terminal */
      }

    }
  }
  close(rfd);
  return 0;
}
#else
static int berandom(CK_SESSION_HANDLE sh)
{
  return 0;
}
#endif

/*
 * Write ISC/BIND style K files for keys.
 * The K...private file format corresponds to RPKCS11 patches 
 * applied to lib/dns/opensslrsa_link.c by RH Lamb.
 */
static uint16 dnssec_keytag (const uint8 *key, size_t klen)
{
  uint32 acm;
  int i;

  for(acm=0,i=0;i<(int)klen;++i) acm += (i&1)?key[i]:(key[i]<< 8);
  acm += (acm>>16)&0xFFFF;
  return acm&0xFFFF;
}
static size_t dnssec_dn2wire(const char *dn, uint8 *wire)
{
  size_t n;
  uint8 *p,*q,*q0;

  if(strcmp(dn,".") == 0) {
    wire[0] = '\0';
    return 1;
  }

  q0 = wire;
  q = q0 + 1;
  n = 0;

  for(p=(uint8 *)dn;*p;p++) {
    if(*p == '.') {
      *q0 = (uint8)n;
      q0 += (n+1);
      q = q0 + 1;
      n = 0;
      continue;
    }
    *q++ = *p;
    n++;
  }

  *q0 = (uint8)n;
  q0 += (n+1);
  n = (int)(q0 - wire);

  return n;
}
#include "sha1.h"
/*#include "sha2.h" */
typedef struct {
  char *ctx;
#define HASH_SHA1    1
#define HASH_SHA256  2
#define HASH_SHA512  3
  int type;
} genhashctx;
/*#define MAX_HASH_CTX max(sizeof(SHA1Context),sizeof(SHA256_CTX),sizeof(SHA512_CTX)) */
#define MAX_HASH_CTX 1000 /* FIXME - above works for plain compile but not advanced */
static int hashit(genhashctx *gh,uint8_t *buf,int len)
{
  if(buf == NULL) {
    switch(gh->type) {
    case HASH_SHA1:
    case HASH_SHA256:
    case HASH_SHA512:
      break;
    default:
      fprintf(stderr,"Unsupported hash type %d",gh->type);
      return -1;
    }
    gh->ctx = (char *)malloc(MAX_HASH_CTX);
  }
  if(buf == NULL) {
    switch(gh->type) {
    case HASH_SHA1:
      if(SHA1Reset((SHA1Context *)gh->ctx)) {
        fprintf(stderr,"SHA1Reset() returned error in %s", __func__);
        return -1;
      }
      return 0;
    case HASH_SHA256:
      SHA256_Init((SHA256_CTX *)gh->ctx);
      return 0;
    case HASH_SHA512:
      SHA512_Init((SHA512_CTX *)gh->ctx);
      return 0;
    default:
      fprintf(stderr,"Unsupported hash type %d",gh->type);
      return -1;
    }
  }
  if(len == 0) {
    switch(gh->type) {
    case HASH_SHA1:
      /* sizeof(buf[])>=20 bytes for msgs digest */
      if(SHA1Result((SHA1Context *)gh->ctx,buf)) {
        fprintf(stderr,"SHA1Result() returned error in %s", __func__);
        return -1;
      }
      free(gh->ctx);
      return 20;
    case HASH_SHA256:
      SHA256_Final(buf,(SHA256_CTX *)gh->ctx);
      free(gh->ctx);
      return 32;
    case HASH_SHA512:
      SHA512_Final(buf,(SHA512_CTX *)gh->ctx);
      free(gh->ctx);
      return 64;
    default:
      fprintf(stderr,"Unsupported hash type %d",gh->type);
      return -1;
    }
  }
  switch(gh->type) {
  case HASH_SHA1:
    if(SHA1Input((SHA1Context *)gh->ctx,buf,len)) {
      fprintf(stderr,"SHA1Context() can't hash in %s", __func__);
      return -1;
      return -1;
    }
    return 0;
  case HASH_SHA256:
    SHA256_Update((SHA256_CTX *)gh->ctx,buf,len);
    return 0;
  case HASH_SHA512:
    SHA512_Update((SHA512_CTX *)gh->ctx,buf,len);
    return 0;
  default:
    fprintf(stderr,"Unsupported hash type %d",gh->type);
    return -1;
  }
}
static int writeKfiles(int slot,CK_SESSION_HANDLE sh,char *label,int Algorithm,int Flags,char *domain0)
{
  CK_RV rv;
  CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
  CK_ATTRIBUTE template[2];
  CK_OBJECT_HANDLE hKeys[MAX_KEYS_PER_SLOT];
  CK_ULONG ofound;
  int i,j;
  int Protocol;

  /* Domain Name System Security (DNSSEC) Algorithm Numbers */
#define RRSIG_DSASHA1         3
#define RRSIG_ECC             4
#define RRSIG_RSASHA1         5
#define RRSIG_DSANSEC3SHA1    6
#define RRSIG_RSANSEC3SHA1    7
#define RRSIG_RSASHA256       8
#define RRSIG_RSASHA512       10

  /* Delegation Signer (DS) Resource Record (RR) Type Digest Algorithms */
#define DS_SHA1    1
#define DS_SHA256  2

  /* DNSKEY Flags */
#define DNSKEY_ZONE_FLAG    0x0100
#define DNSKEY_SEP_FLAG     0x0001
#define DNSKEY_REVOKE_FLAG  0x0080

  /* DNSKEY Protocol Identifiers */
#define DNSKEY_PROTOCOL_DNSSEC  3

  if(domain0 == NULL) domain0 = "example.";
  if(Flags == 0) Flags = DNSKEY_ZONE_FLAG|DNSKEY_SEP_FLAG;
  Protocol = DNSKEY_PROTOCOL_DNSSEC;
  if(Algorithm == 0) Algorithm = RRSIG_RSASHA256;

  j = 0;
  template[j].type = CKA_CLASS;
  template[j].pValue = &pubClass;
  template[j].ulValueLen = sizeof(pubClass);
  j++;
  if(label) {
    template[j].type = CKA_LABEL;
    template[j].pValue = label;
    template[j].ulValueLen = strlen(label);
    j++;
  }    
  rv = pfl->C_FindObjectsInit(sh,template,j);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjects(sh,hKeys,MAX_KEYS_PER_SLOT,&ofound);
  if(rv != CKR_OK) goto endit;
  rv = pfl->C_FindObjectsFinal(sh);
  if(rv != CKR_OK) goto endit;
  myx_syslog(LOG_INFO,"%d public keys:\n",ofound);
  for(i=0;i<(int)ofound;i++) {
    uint8 *q,*q0,*q1,lbuf[4096];
    char *p0,buf[MAXPATHLEN];
    FILE *fout;
    CK_OBJECT_HANDLE hPub;
    CK_ULONG tsize;
    int elen,mlen,KeyTag;
    CK_ATTRIBUTE getattributes[] = {
      {CKA_MODULUS,NULL_PTR,0},
      {CKA_PUBLIC_EXPONENT,NULL_PTR,0},
      {CKA_ID,NULL_PTR,0},
      {CKA_LABEL,NULL_PTR,0},
      {CKA_CLASS,NULL_PTR,0},
      {CKA_KEY_TYPE,NULL_PTR,0},
      {CKA_MODULUS_BITS,NULL_PTR,0},
    };

    hPub = hKeys[i];
    tsize = sizeof(getattributes)/sizeof(CK_ATTRIBUTE);
    if((rv=pfl->C_GetAttributeValue(sh,hPub,getattributes,tsize)) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
      continue;
    }
    for(j=0;j<(int)tsize;j++) {
      getattributes[j].pValue = myx_malloc(getattributes[j].ulValueLen); 
    }
    if((rv=pfl->C_GetAttributeValue(sh,hPub,getattributes,tsize)) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: C_GetAttributeValue %s\n",pkcs11_ret_str(rv));
      for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
      continue;
    }
    
    j = getattributes[3].ulValueLen;
    label = (char *)myx_malloc(j+1);
    memcpy(label,getattributes[3].pValue,j);
    label[j] = '\0';
    
    elen = (int)getattributes[1].ulValueLen;
    if(elen > 255) {
      myx_syslog(LOG_ERR,"pkcs11: error: Unsupported public exponent size %d\n",elen);
      goto endit2;
    }
    mlen = (int)getattributes[0].ulValueLen;

    q = lbuf;
    q += dnssec_dn2wire(domain0,lbuf);
    q0 = q;
    *(uint16 *)q = htons(Flags);
    q += 2;
    *(uint8 *)q = (uint8)Protocol;
    q++;
    *(uint8 *)q = (uint8)Algorithm;
    q++;
    q1 = q;
    *q++ = (uint8)elen;
    memcpy(q,getattributes[1].pValue,elen);
    q += elen;
    memcpy(q,getattributes[0].pValue,mlen);
    q += mlen;
    base64encode(buf,q1,(int)(q-q1));
    p0 = strdup(buf);
    KeyTag = dnssec_keytag(q0,(int)(q-q0));

    sprintf(buf,"K%s+%03u+%05u.key",domain0,Algorithm,KeyTag);
    if((fout=fopen(buf,"w")) == NULL) goto endit2;
    fprintf(fout,"%s IN DNSKEY %u %u %u %s ; keytag=%u\n",domain0,Flags,Protocol,Algorithm,p0,KeyTag);
    free(p0);

    if(Flags|DNSKEY_SEP_FLAG) {
      genhashctx gh;
      int i,cX,hashlen;
      uint8 hash[256];
      char hout[256];

      gh.type = HASH_SHA256;
      hashit(&gh,NULL,0);
      hashit(&gh,lbuf,(int)(q-lbuf));
      hashlen = hashit(&gh,hash,0);
      hout[0] = '\0';
      cX = sizeof(hout);
      for(i=0;i<hashlen;i++) cX -= snprintf(&hout[2*i],cX,"%02X",hash[i]);
      fprintf(fout,"; %s IN DS %u %u %d %s\n",domain0,KeyTag,Algorithm,DS_SHA256,hout);
    }

    fclose(fout);

    sprintf(buf,"K%s+%03u+%05u.private",domain0,Algorithm,KeyTag);
    if((fout=fopen(buf,"w")) == NULL) goto endit2;
    fprintf(fout,"Private-key-format: xxx\n");
    fprintf(fout,"slot:%d\n",slot);
    fprintf(fout,"pin:\n");
    fprintf(fout,"label:%s\n",label);
    fclose(fout);

    printf("K%s+%03u+%05u",domain0,Algorithm,KeyTag);

  endit2:
    if(label) free(label);
    for(j=0;j<(int)tsize;j++) free(getattributes[j].pValue);
  }
  return 0;
 endit:
  return -1;
}


int main(int argc,char *argv[])
{
  CK_C_GetFunctionList   pGFL=0;
  CK_RV                  rv;
  CK_ULONG               nslots;
  CK_SLOT_ID             slots[MAX_SLOTS];
  CK_SESSION_HANDLE      sh;
  CK_OBJECT_HANDLE       hKeys[MAX_KEYS_PER_SLOT];
  void                   *hLib;
  int                    i,k,n,bits,ret,cmd,wslot,nbytes;
  char                   *p,lbuf[MAXPATHLEN],*args[NARGS];
  char                   *seckeylabel;
  char                   *wrappingkeylabel;
  CK_OBJECT_HANDLE       hWrappingKey;
  char                   gkeybuf[50];
  char                   *keylabel;
  char                   *userpin;
  char                   *importfile,*import_cka_label,importfilebuf[MAXPATHLEN];
  char                   *hsmconfigfile;
  char                   *outfile,outfilebuf[MAXPATHLEN];

  time(&tnow);

  ret = -1;

  /* set up some logging */
  if((p=strrchr(argv[0],'/'))) {
    *p++ = '\0';
  } else {
    p = argv[0];
  }
  sprintf(sessionlogfile,"%s/%s.log",LOGDIR,p);
  sprintf(logfile,"%s/logfile.txt",LOGDIR);
  myx_syslog(LOG_INFO,"Running \"");
  for(i=0;i<argc;i++) myx_syslog(LOG_INFO,"%s ",argv[i]);
  {
    char lbuf[256];
    strcpy(lbuf,ctime(&tnow));
    cleanup(lbuf);
    myx_syslog(LOG_INFO,"\" at %s %s\n",lbuf,daylight?tzname[0]:tzname[1]);
  }
  // myx_syslog(LOG_INFO,"Version %s. For non-commercial use only.\n",VERSION);

  {
    int ch;
    extern char *optarg;
    extern int optind;
    extern int optopt;
    extern int opterr;
    
    bits = 0;
    seckeylabel = NULL;
    keylabel = NULL;
    wrappingkeylabel = NULL;
    userpin = NULL;
    importfile = NULL;
    import_cka_label = NULL;
    hsmconfigfile = NULL;
    outfile = NULL;
    cmd = 0;
    wslot = -1;
    nbytes = -1;
    /* "g::p::s::l::w:d:D:P:S:T:W::" */
    /* "g::p::s::l::w:d:D:P:S:T:W::i:C:h:r:c::" */
    while((ch=getopt(argc,argv,"g::p::s:l::F::w:d:D:P:S:T::W::i:C:h:r:c")) != -1) {
      switch(ch) {
      case 'g':
        if(cmd != 0) {
          myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
          return -1;
        }
	cmd = ch;
	if(optarg) {
	  /* rsa:bits:label */
	  if(strlen(optarg) > (sizeof(gkeybuf)-3) ) {
	    myx_syslog(LOG_ERR,"error: key parameter \"%s\" too long\n",optarg);
	    return -1;
	  }
	  strcpy(gkeybuf,optarg);
	  n = lparse(gkeybuf,args,NARGS,':');
	  if(n != 3 || strcasecmp(args[0],"rsa")) {
            myx_syslog(LOG_ERR,"error: usage -g rsa:bits:label\n");
            return -1;
          }
	  bits = atoi(args[1]);
	  if(bits < 512 || bits > 4096) {
            myx_syslog(LOG_ERR,"error: 512 =< bits =< 4096\n");
            return -1;
	  }
	  keylabel = args[2];
	}
	break;
      case 'p':
      case 's':
      case 'l':
      case 'F':
      case 'c':
      case 'W':
	if(cmd != 0) {
	  myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
	  return -1;
	}
	if(optarg && strlen(optarg) > 0) keylabel = optarg;
	else keylabel = NULL;
	cmd = ch;
	break;
      case 'i':
        if(cmd != 0) {
          myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
          return -1;
        }
	cmd = ch;
        if(optarg) {
          /* rsa-privkey-file:CKA_LABEL */
          if(strlen(optarg) > (sizeof(importfilebuf) - 3) ) {
            myx_syslog(LOG_ERR,"error: Import parameter \"%s\" too long\n",optarg);
            return -1;
          }
          strcpy(importfilebuf,optarg);
          n = lparse(importfilebuf,args,NARGS,':');
          if(n != 2) {
            myx_syslog(LOG_ERR,"error: usage -i rsa-privkey-file:CKA_LABEL\n");
            return -1;
          }
	  importfile = args[0];
          import_cka_label = args[1];
        }
	break;
      case 'C':
        if(cmd != 0) {
          myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
          return -1;
        }
	cmd = ch;
        if(optarg) {
          /* cert-file:CKA_LABEL */
          if(strlen(optarg) > (sizeof(importfilebuf) - 3) ) {
            myx_syslog(LOG_ERR,"error: Import parameter \"%s\" too long\n",optarg);
            return -1;
          }
          strcpy(importfilebuf,optarg);
          n = lparse(importfilebuf,args,NARGS,':');
          if(n != 2) {
            myx_syslog(LOG_ERR,"error: usage -i x509-cert-file:CKA_LABEL\n");
            return -1;
          }
	  importfile = args[0];
          import_cka_label = args[1];
        }
	break;
      case 'r':
        if(cmd != 0) {
          myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
          return -1;
        }
	cmd = ch;
        if(optarg) {
          /* nbytes:outfile */
          if(strlen(optarg) > (sizeof(outfilebuf)-3) ) {
            myx_syslog(LOG_ERR,"error: RNG parameter \"%s\" too long\n",optarg);
            return -1;
          }
          strcpy(outfilebuf,optarg);
          n = lparse(outfilebuf,args,NARGS,':');
          if(n != 2 || atoi(args[0]) < -2) {
            myx_syslog(LOG_ERR,"error: usage -r nbytes:outfile\n");
            return -1;
          }
          nbytes = atoi(args[0]);
          outfile = args[1];
        }
        break;
      case 'h':
	{
	  FILE *fp;
	  /* find the hsmconfig file */
	  if((fp=fopen(optarg,"r"))) {
	    fclose(fp);
	    hsmconfigfile = optarg;
	    break;
	  }
	  p = strdup(getenv("PATH"));
	  n = lparse(p,args,NARGS,':');
	  for(i=0;i<n;i++) {
	    sprintf(lbuf,"%s/%s",args[i],optarg);
	    if((fp=fopen(lbuf,"r")) == NULL) continue;
	    fclose(fp);
	    hsmconfigfile = strdup(lbuf);
	    break;
	  }
	  if(hsmconfigfile == NULL) {
	    myx_syslog(LOG_ERR,"error: could not find %s\n",optarg);
	    return -1;
	  }
	}
        break;
      case 'w':
	wrappingkeylabel = optarg;
	break;
      case 'S':
	wslot = atoi(optarg);
	break;
      case 'P':
        userpin = optarg;
        break;
      case 'd':
      case 'D':
	if(cmd != 0) {
          myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
          return -1;
	}
	cmd = ch;
	keylabel = optarg;
	break;
      case 'T':
        if(cmd != 0) {
          myx_syslog(LOG_ERR,"error: Can only perform one action at a time.\n");
          return -1;
        }
        cmd = ch;
        if(optarg) {
          /* key-file:CKA_LABEL */
          if(strlen(optarg) > (sizeof(importfilebuf) - 3) ) {
            myx_syslog(LOG_ERR,"error: Import parameter \"%s\" too long\n",optarg);
            return -1;
          }
          strcpy(importfilebuf,optarg);
          n = lparse(importfilebuf,args,NARGS,':');
	  if(n > 1) seckeylabel = args[1];
          if(strlen(args[0]) != 0) importfile = args[0];
        }
        break;
      case '?':
      default:
	printf("Usage:%s options.. [ < keyfile ]\n",argv[0]);
	printf(" -l[label] : lists all keys or more info on \"label\"ed key\n");
	printf(" -g rsa:bits:cka_label : generates optionally \"label\"ed RSA key\n");
	printf(" -d label  : deletes \"label\"ed key\n");
	printf(" -D label  : deletes \"label\"ed Object\n");
	printf(" -p[label] : outputs wrapped base64 signing key for all or \"label\"ed key\n");
	printf(" -s[label] : outputs wrapped base64 secret key for all or \"label\"ed key\n");
	printf(" -W : Only create a wrapping key in the HSM\n");
	printf(" -h hsmconfigfile\n");
	printf(" -r nbytes:fname : generates random nbytes in fname. nbytes = -1 and fname = - for filling /dev/random pool\n");
	printf(" -i fname:cka_label : imports key from openssl private key PEM file\n");
	printf(" -C fname:cka_label : imports cert from openssl X.509 PEM file\n");
	printf(" -T [[fname][:cka_label]] : imports/generates dnssec transport key to/on card\n");
        printf(" -P pin    : pin code\n");
        printf(" -S slot   : HSM slot number (0-n)\n");

	printf(" -F[label]:[alg:flags:domain0] : Write ISC/BIND \"K\" PK files for labeled key. E.g. \"-FKabc:8:257:com.\" \n");

	printf(" With no arguments, reads key info created by \"-p\" to import key(s)\n");
	printf(" A specfic (un)wrapping key can be specified using\n \"-w label\" for \"-p,-s, and < keyfile\" operations. If specified wrapping\n key does not exist, a new one will be created inside the HSM\n");

	return -1;
      }
    }
    argc -= optind;
    argv += optind;
    if(cmd == 0) cmd = 'b';
  }

  /*
   * The dynamic lib will also need to know where libs are so:
   *  export KEYPER_LIBRARY_PATH=$PWD
   *  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$KEYPER_LIBRARY_PATH
   *
   */
  scanhsms(hsmconfigfile);
  if((p=getenv("PKCS11_LIBRARY_PATH")) == NULL) {
    myx_syslog(LOG_ERR,"You must set PKCS11_LIBRARY_PATH, e.g.,\n \"export PKCS11_LIBRARY_PATH=/home/dnssec/AEP/pkcs11.so.3.10\"\n");
    return -1;
  }
  sprintf(lbuf,"%s",p);
  hLib = dlopen(lbuf,RTLD_LAZY);
  if(!hLib) {
    myx_syslog(LOG_ERR,"pkcs11: error: failed to open lib %s\n %s\n",lbuf,dlerror());
    return -1;
  }
  if((pGFL=(CK_C_GetFunctionList)dlsym(hLib,"C_GetFunctionList")) == NULL) {
    myx_syslog(LOG_ERR,"pkcs11: error: Cannot find GetFunctionList()\n");
    dlclose(hLib);
    return -1;
  }
  if((rv=pGFL(&pfl)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_GetFunctionList %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  if((rv = pfl->C_Initialize(NULL)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_Initialize %s\n",pkcs11_ret_str(rv));
    return -1;
  }
  nslots = MAX_SLOTS;
  if((rv=pfl->C_GetSlotList(TRUE,slots,&nslots)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_Getslots %s\n",pkcs11_ret_str(rv));
    /*pfl->C_Finalize(0); */
    return -1;
  }
  /*printf("Got %d Slots\n",nslots); */
  k = 0;
  if(wslot >= 0 && (int)nslots >= (wslot+1)) {
    k = wslot;
  } else
  if(nslots > 1) {
    fprintf(stderr,"Found %d slots. Enter slot number (0-%d) to operate on (0):",(int)nslots,(int)(nslots-1));
    if(fgets(lbuf,sizeof(lbuf),stdin) == NULL) {
      return -1;
    }
    cleanup(lbuf);
    k = atoi(lbuf);
    myx_syslog(LOG_INFO,"%d\n",k);
  }

  rv = pfl->C_OpenSession(slots[k],CKF_RW_SESSION|CKF_SERIAL_SESSION,NULL,NULL,&sh);
  if(rv != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: Could not open slot %d\n C_OpenSession %s\n",k,pkcs11_ret_str(rv));
    return -1;
  }

  {
    CK_TOKEN_INFO token_info;

    if((rv = pfl->C_GetTokenInfo(slots[k],&token_info)) == CKR_OK) {
      token_info.label[31] = '\0';
      token_info.manufacturerID[31] = '\0';
      token_info.model[15] = '\0';
      token_info.serialNumber[15] = '\0';
      myx_syslog(LOG_INFO,"Token Name:%s\n",token_info.label);
      myx_syslog(LOG_INFO,"Mfr:%s\n",token_info.manufacturerID);
      myx_syslog(LOG_INFO,"Model:%s\n",token_info.model);
      myx_syslog(LOG_INFO,"Serial:%s\n",token_info.serialNumber);
      myx_syslog(LOG_INFO,"Slot:%d\n",k);
    }
  }

  if(userpin) {
    strcpy(lbuf,userpin);
  } else {
    char *p;
    
    sprintf(lbuf,"%s.%d.pin",misc_hsmconfig,k);
    for(p=lbuf;*p;p++) if(*p == '.') *p = '_';
    if((p=getenv(lbuf))) {
      userpin = strdup(p);
      strcpy(lbuf,userpin);
    } else if((p=getenv("PKCS11_LIBRARY_PIN"))) {
      userpin = strdup(p);
      strcpy(lbuf,userpin);
    } else {
      fprintf(stderr,"Enter PIN for slot %d: ",k);
      /* replace /w fgetsne() for no echo */
      if(fgets(lbuf,sizeof(lbuf),stdin) == NULL) {
	return -1;
      }
      cleanup(lbuf);
      userpin = strdup(lbuf);
    }
  }

  if((rv=pfl->C_Login(sh,CKU_USER,(CK_BYTE *)lbuf,strlen(lbuf) )) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: Invalid PIN\n C_Login %s\n",pkcs11_ret_str(rv));
    pfl->C_CloseSession(sh); 
    return -1;
  }

  if(wrappingkeylabel == NULL) wrappingkeylabel="dnssec backup key";

  if(cmd == 'b') {
    CK_MECHANISM mech;
    if(getwrapkey(sh,(CK_UTF8CHAR *)wrappingkeylabel,&hWrappingKey,&mech)) {
      goto endit;
    }
    myx_syslog(LOG_INFO,"Unwrapping keys from STDIN into HSM with \"%s\"\n",wrappingkeylabel);
    myx_syslog(LOG_INFO,"\n");
    read_keys_into_hsm(sh,hWrappingKey,stdin,mech);
    ret = 0;
    goto endit;
  } else
  if(cmd == 'i') { /* import */
    myx_syslog(LOG_INFO,"\n");
    import_openssl_private_key(sh,importfile,import_cka_label);
    ret = 0;
    goto endit;
  } else
  if(cmd == 'C') { /* import certificate */
    myx_syslog(LOG_INFO,"\n");
    import_openssl_cert(sh,importfile,import_cka_label);
    ret = 0;
    goto endit;
  } else
  if(cmd == 'l') { /* list */
    myx_syslog(LOG_INFO,"\n");
    listkeys(sh,keylabel);
    ret = 0;
    goto endit;
  } else
  if(cmd == 'F') { /* write BIND/ISC "K" files */
    int alg,flags;
    char *dn0; /* "example." */

    myx_syslog(LOG_INFO,"\n");
    /* label:alg:flags:domain0 */
    alg = 0;
    flags = 0;
    dn0 = NULL;
    n = lparse(keylabel,args,NARGS,':');
    if(n>1) alg = atoi(args[1]);
    if(n>2) flags = atoi(args[2]);
    if(n>3) dn0 = strdup(args[3]);
    writeKfiles(k,sh,keylabel,alg,flags,dn0);

    ret = 0;
    goto endit;
  } else
  if(cmd == 'd') {
    myx_syslog(LOG_INFO,"\n");
    deletekey(sh,(CK_UTF8CHAR *)keylabel,CKO_PRIVATE_KEY);
    deletekey(sh,(CK_UTF8CHAR *)keylabel,CKO_PUBLIC_KEY);
    ret = 0;
    goto endit;
  } else
  if(cmd == 'D') {
    myx_syslog(LOG_INFO,"\n");
    myx_syslog(LOG_INFO,"Are you sure you want to delete object \"%s\"? [N/y]: ",keylabel);
    if(fgets(lbuf,sizeof(lbuf),stdin) == NULL) {
      goto endit;
    }
    cleanup(lbuf);
    if(lbuf[0] == 'y' || lbuf[0] == 'Y') {
      deletekey(sh,(CK_UTF8CHAR *)keylabel,0); /* CKO_SECRET_KEY or anything else */
    } else {
      myx_syslog(LOG_INFO,"Object \"%s\" not deleted\n",keylabel);
    }
    ret = 0;
    goto endit;
  } else
  if(cmd == 'g') {
    if(bits == 0) bits = 1024;
    if(keylabel == NULL) {
      keylabel = "NewKey";
    }
    myx_syslog(LOG_INFO,"\n");
    myx_syslog(LOG_INFO,"Generating %d bit RSA key labeled \"%s\"\n",bits,keylabel);
    myx_syslog(LOG_INFO,"\n");
    if(genrsakey(sh,keylabel,bits)) {
      goto endit;
    }
    ret = 0;
    goto endit;
  } else
  if(cmd == 'p') {
    CK_MECHANISM mech;
    myx_syslog(LOG_INFO,"Keys wrapped with \"%s\"\n",wrappingkeylabel);
    myx_syslog(LOG_INFO,"\n");
    if(getwrapkey(sh,(CK_UTF8CHAR *)wrappingkeylabel,&hWrappingKey,&mech)) {
      goto endit;
    }
    if(getkeyarray(sh,(CK_UTF8CHAR *)keylabel,CKO_PRIVATE_KEY,hKeys,&n)) {
      goto endit;
    }
    for(i=0;i<n;i++) {
      wrap_and_export_privkey(sh,hKeys[i],hWrappingKey,mech);
    }
    if(getkeyarray(sh,(CK_UTF8CHAR *)keylabel,CKO_PUBLIC_KEY,hKeys,&n)) {
      goto endit;
    }
    for(i=0;i<n;i++) {
      export_pubkey(sh,hKeys[i]);
    }
    ret = 0;
    goto endit;
  } else
  if(cmd == 's') {
    CK_MECHANISM mech;
    myx_syslog(LOG_INFO,"Keys wrapped with \"%s\"\n",wrappingkeylabel);
    myx_syslog(LOG_INFO,"\n");
    if(getwrapkey(sh,(CK_UTF8CHAR *)wrappingkeylabel,&hWrappingKey,&mech)) {
      goto endit;
    }
    if(getkeyarray(sh,(CK_UTF8CHAR *)keylabel,CKO_SECRET_KEY,hKeys,&n)) {
      goto endit;
    }
    for(i=0;i<n;i++) {
      wrap_and_export_privkey(sh,hKeys[i],hWrappingKey,mech);
    }
    ret = 0;
    goto endit;
  } else
  if(cmd == 'W') {
    CK_MECHANISM mech;
    if(getwrapkey(sh,(CK_UTF8CHAR *)wrappingkeylabel,&hWrappingKey,&mech)) {
      goto endit;
    }
    ret = 0;
    goto endit;
  } else
  if(cmd == 'T') {
    if(maketranskey(sh,importfile,(CK_UTF8CHAR *)seckeylabel)) {
      goto endit;
    }
    ret = 0;
    goto endit;
  } else
  if(cmd == 'r') { /* RNG */
    FILE *fout;

    myx_syslog(LOG_INFO,"\n");
    if(nbytes < 0) {
      if(strcmp(outfile,"-") == 0) {
	myx_syslog(LOG_INFO,"Generating random numbers into /dev/random\n");
	berandom(sh); /* daemonize and fill /dev/random */
      } else {
        if(signal(SIGPIPE,sigpipe) == SIG_ERR) {
          myx_syslog(LOG_ERR,"error: cannot set signal SIGPIPE function\n");
          goto endit;
        }

	while(1) {
	if((fout=fopen(outfile,"w")) == NULL) { /* outfile = mkfifo pipe */
	  myx_syslog(LOG_ERR,"error: Cannot open %s\n",outfile);
	  goto endit;
	}
	myx_syslog(LOG_INFO,"Generating random numbers into %s\n",outfile);
	getrandom(sh,fout,-1); /* exit on fwrite error */
	fclose(fout);
	}

      }
      ret = 0;
      goto endit;
    }
    if((fout=fopen(outfile,"w+")) == NULL) {
      myx_syslog(LOG_ERR,"error: Cannot open %s\n",outfile);
      goto endit;
    }
    getrandom(sh,fout,nbytes);
    fclose(fout);
    ret = 0;
    goto endit;
  } else 
  if(cmd == 'c') { /* change pin */
    char *npin;
    
    myx_syslog(LOG_INFO,"\n");

    fprintf(stderr,"Enter New PIN for slot %d: ",k);
    if(fgets(lbuf,sizeof(lbuf),stdin) == NULL) {
      goto endit;
    }
    cleanup(lbuf);
    npin = strdup(lbuf);
    fprintf(stderr,"Re-enter New PIN for slot %d: ",k);
    if(fgets(lbuf,sizeof(lbuf),stdin) == NULL) {
      goto endit;
    }
    cleanup(lbuf);
    if(strcmp(npin,lbuf)) {
      myx_syslog(LOG_ERR," new PINs dont match! Try again\n");
      goto endit;
    }
    if((rv=pfl->C_SetPIN(sh,(CK_UTF8CHAR_PTR)userpin,strlen(userpin),(CK_UTF8CHAR_PTR)lbuf,strlen(lbuf))) != CKR_OK) {
      myx_syslog(LOG_ERR,"pkcs11: error: Invalid PIN\n C_SetPin: %s\n",pkcs11_ret_str(rv));
      goto endit;
    }
    myx_syslog(LOG_INFO,"PIN change for slot %d sucessful\n",k);

    ret = 0;
    goto endit;
  } else {
    myx_syslog(LOG_ERR,"Unknown command \"%s\"\n",argv[1]);
  }

 endit:

  if((rv=pfl->C_Logout(sh)) != CKR_OK) {
    /* Entersafe/Feitian card emits error here 
    myx_syslog(LOG_ERR,"pkcs11: error: C_Logout %s\n",pkcs11_ret_str(rv));
    */
  }

  if((rv=pfl->C_CloseSession(sh)) != CKR_OK) {
    myx_syslog(LOG_ERR,"pkcs11: error: C_CloseSession %s\n",pkcs11_ret_str(rv));
  }
  /*pfl->C_Finalize(0);  never */
  return ret;
}


