/***************************************************************************
    begin       : Wed Mar 16 2005
    copyright   : (C) 2005-2010 by Martin Preuss
    email       : martin@libchipcard.de

 ***************************************************************************
 *          Please see toplevel file COPYING for license details           *
 ***************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#define DISABLE_DEBUGLOG


#include "ctfile_p.h"
#include "i18n_l.h"
#include <gwenhywfar/ctf_context_be.h>
#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>
#include <gwenhywfar/padd.h>
#include <gwenhywfar/cryptkeyrsa.h>
#include <gwenhywfar/text.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>



GWEN_INHERIT(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE)





int GWEN_Crypt_TokenFile__OpenFile(GWEN_CRYPT_TOKEN *ct, int wr, uint32_t gid) {
  int fd;
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_FSLOCK_RESULT lres;
  const char *fname;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  fname = GWEN_Crypt_Token_GetTokenName(ct);
  if (!fname) {
    DBG_ERROR(GWEN_LOGDOMAIN, "No name of the crypt token set - maybe you need to set the key file as token name? Cannot lock token.");
    return GWEN_ERROR_IO;
  }

  lct->lock=GWEN_FSLock_new(fname,
                            GWEN_FSLock_TypeFile);
  lres=GWEN_FSLock_Lock(lct->lock, 10000, gid);
  if (lres!=GWEN_FSLock_ResultOk) {
    GWEN_FSLock_free(lct->lock);
    lct->lock=0;
    DBG_ERROR(GWEN_LOGDOMAIN, "Could not lock file");
    if (lres==GWEN_FSLock_ResultUserAbort)
      return GWEN_ERROR_USER_ABORTED;
    else
      return GWEN_ERROR_IO;
  }
  else {
    DBG_INFO(GWEN_LOGDOMAIN,
             "Keyfile [%s] locked.",
             GWEN_Crypt_Token_GetTokenName(ct));
  }

  if (wr) {
    /* write file */
    fd=open(GWEN_Crypt_Token_GetTokenName(ct),
            O_RDWR|O_CREAT
#ifdef OS_WIN32
            | O_BINARY
#endif
            ,
            S_IRUSR|S_IWUSR | lct->keyfile_mode);
  }
  else {
    /* Remember the access permissions when opening the file */
    struct stat statbuffer;
    if (!stat(GWEN_Crypt_Token_GetTokenName(ct), &statbuffer)) {
      /* Save the access mode, but masked by the bit masks for
      user/group/other permissions */
      lct->keyfile_mode =
        statbuffer.st_mode & (S_IRWXU
#ifndef OS_WIN32
                              | S_IRWXG | S_IRWXO
#endif
                             );
    }
    else {
      DBG_ERROR(GWEN_LOGDOMAIN,
                "stat(%s): %s",
                GWEN_Crypt_Token_GetTokenName(ct),
                strerror(errno));

      GWEN_FSLock_Unlock(lct->lock);
      GWEN_FSLock_free(lct->lock);
      lct->lock=0;
      DBG_INFO(GWEN_LOGDOMAIN,
               "Keyfile [%s] unlocked.",
               GWEN_Crypt_Token_GetTokenName(ct));
      return GWEN_ERROR_IO;
    }

    /* and open the file */
    fd=open(GWEN_Crypt_Token_GetTokenName(ct),
            O_RDONLY
#ifdef OS_WIN32
            | O_BINARY
#endif
           );
  }

  if (fd==-1) {
    DBG_ERROR(GWEN_LOGDOMAIN,
              "open(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct),
              strerror(errno));
    GWEN_FSLock_Unlock(lct->lock);
    GWEN_FSLock_free(lct->lock);
    lct->lock=0;
    DBG_INFO(GWEN_LOGDOMAIN,
             "Keyfile [%s] unlocked.",
             GWEN_Crypt_Token_GetTokenName(ct));
    return GWEN_ERROR_IO;
  }

  lct->fd=fd;

  return 0;
}



int GWEN_Crypt_TokenFile__CloseFile(GWEN_CRYPT_TOKEN *ct, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_FSLOCK_RESULT lres;
  struct stat st;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  if (lct->fd==-1) {
    DBG_ERROR(GWEN_LOGDOMAIN, "Keyfile \"%s\"not open",
              GWEN_Crypt_Token_GetTokenName(ct));
    return GWEN_ERROR_INTERNAL;
  }

  if (close(lct->fd)) {
    DBG_ERROR(GWEN_LOGDOMAIN, "close(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct), strerror(errno));
    lct->fd=-1;
    GWEN_FSLock_Unlock(lct->lock);
    GWEN_FSLock_free(lct->lock);
    lct->lock=0;
    DBG_INFO(GWEN_LOGDOMAIN,
             "Keyfile [%s] unlocked.",
             GWEN_Crypt_Token_GetTokenName(ct));
    return GWEN_ERROR_IO;
  }
  lct->fd=-1;

  lres=GWEN_FSLock_Unlock(lct->lock);
  if (lres!=GWEN_FSLock_ResultOk) {
    DBG_WARN(GWEN_LOGDOMAIN, "Error removing lock from \"%s\": %d",
             GWEN_Crypt_Token_GetTokenName(ct), lres);
  }
  GWEN_FSLock_free(lct->lock);
  lct->lock=0;
  DBG_INFO(GWEN_LOGDOMAIN,
           "Keyfile [%s] unlocked.",
           GWEN_Crypt_Token_GetTokenName(ct));

  /* get times */
  if (stat(GWEN_Crypt_Token_GetTokenName(ct), &st)) {
    DBG_ERROR(GWEN_LOGDOMAIN,
              "stat(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct),
              strerror(errno));
    return GWEN_ERROR_IO;
  }

#ifndef OS_WIN32
  if (st.st_mode & 0007) {
    DBG_WARN(GWEN_LOGDOMAIN,
             "WARNING: Your keyfile \"%s\" is accessible by every user on your computer!\n"
             "Nobody but you should have access to the file. You \n"
             "should probably change this with \"chmod 600 %s\"",
             GWEN_Crypt_Token_GetTokenName(ct),
             GWEN_Crypt_Token_GetTokenName(ct));
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Warning,
                         "WARNING: Your keyfile is accessible ny every user on your computer!\n"
                         "Nobody but you should have access to the file.");
  }
#endif
  lct->mtime=st.st_mtime;
  lct->ctime=st.st_ctime;

  return 0;
}



int GWEN_Crypt_TokenFile__Read(GWEN_CRYPT_TOKEN *ct, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  assert(lct->readFn);
  if (lseek(lct->fd, 0, SEEK_SET)==-1) {
    DBG_ERROR(GWEN_LOGDOMAIN, "lseek(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct),
              strerror(errno));
    return GWEN_ERROR_IO;
  }
  return lct->readFn(ct, lct->fd, gid);
}



int GWEN_Crypt_TokenFile__Write(GWEN_CRYPT_TOKEN *ct, int cr, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  if (lct->writeFn==0) {
    DBG_WARN(GWEN_LOGDOMAIN,
             "No write function in crypt token type \"%s\"",
             GWEN_Crypt_Token_GetTypeName(ct));
    return GWEN_ERROR_NOT_SUPPORTED;
  }

  if (lseek(lct->fd, 0, SEEK_SET)==-1) {
    DBG_ERROR(GWEN_LOGDOMAIN, "lseek(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct),
              strerror(errno));
    return GWEN_ERROR_IO;
  }
  return lct->writeFn(ct, lct->fd, cr, gid);
}



int GWEN_Crypt_TokenFile__ReadFile(GWEN_CRYPT_TOKEN *ct, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* clear context list, it will be reloaded */
  GWEN_Crypt_Token_Context_List_Clear(lct->contextList);

  /* open file */
  rv=GWEN_Crypt_TokenFile__OpenFile(ct, 0, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN,
             "Could not open keyfile for reading (%d)", rv);
    return rv;
  }

  /* read file */
  rv=GWEN_Crypt_TokenFile__Read(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Error reading keyfile");
    GWEN_Crypt_TokenFile__CloseFile(ct, gid);
    return rv;
  }

  /* close file */
  rv=GWEN_Crypt_TokenFile__CloseFile(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Could not close keyfile");
    return rv;
  }

  return 0;
}



int GWEN_Crypt_TokenFile__WriteFile(GWEN_CRYPT_TOKEN *ct, int cr, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* open file */
  rv=GWEN_Crypt_TokenFile__OpenFile(ct, 1, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN,
             "Could not open keyfile for writing (%d)", rv);
    return rv;
  }

  /* write file */
  rv=GWEN_Crypt_TokenFile__Write(ct, cr, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Error writing keyfile");
    GWEN_Crypt_TokenFile__CloseFile(ct, gid);
    return rv;
  }

  /* close file */
  rv=GWEN_Crypt_TokenFile__CloseFile(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Could not close keyfile");
    return rv;
  }

  return 0;
}



int GWEN_Crypt_TokenFile__ReloadIfNeeded(GWEN_CRYPT_TOKEN *ct, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  struct stat st;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  if (stat(GWEN_Crypt_Token_GetTokenName(ct), &st)) {
    DBG_ERROR(GWEN_LOGDOMAIN,
              "stat(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct),
              strerror(errno));
    return -1;
  }
  if (lct->mtime!=st.st_mtime ||
      lct->ctime!=st.st_ctime) {
    int rv;

    /* file has changed, reload it */
    DBG_NOTICE(GWEN_LOGDOMAIN,
               "Keyfile changed externally, reloading it");
    /* read file */
    rv=GWEN_Crypt_TokenFile__ReadFile(ct, gid);
    if (rv) {
      DBG_WARN(GWEN_LOGDOMAIN, "Error reloading keyfile");
      return rv;
    }
  }
  else {
    DBG_NOTICE(GWEN_LOGDOMAIN, "Keyfile unchanged, not reloading");
  }
  return 0;
}



void GWEN_Crypt_TokenFile_AddContext(GWEN_CRYPT_TOKEN *ct, GWEN_CRYPT_TOKEN_CONTEXT *ctx) {
  GWEN_CRYPT_TOKEN_FILE *lct;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* make sure the context is a file context */
  assert(GWEN_CTF_Context_IsOfThisType(ctx));
  GWEN_Crypt_Token_Context_List_Add(ctx, lct->contextList);
}



GWEN_CRYPT_TOKEN_CONTEXT *GWEN_Crypt_TokenFile_GetContext(GWEN_CRYPT_TOKEN *ct, int idx) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (idx==0)
      return ctx;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    idx--;
  }

  return NULL;
}



GWEN_CRYPT_TOKEN_FILE_READ_FN GWEN_Crypt_TokenFile_SetReadFn(GWEN_CRYPT_TOKEN *ct,
    GWEN_CRYPT_TOKEN_FILE_READ_FN f) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_FILE_READ_FN of;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  of=lct->readFn;
  lct->readFn=f;

  return of;
}



GWEN_CRYPT_TOKEN_FILE_WRITE_FN GWEN_Crypt_TokenFile_SetWriteFn(GWEN_CRYPT_TOKEN *ct,
    GWEN_CRYPT_TOKEN_FILE_WRITE_FN f) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_FILE_WRITE_FN of;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  of=lct->writeFn;
  lct->writeFn=f;

  return of;
}



int GWENHYWFAR_CB GWEN_Crypt_TokenFile_Create(GWEN_CRYPT_TOKEN *ct, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  struct stat st;
  int fd;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  if (!GWEN_Crypt_Token_GetTokenName(ct)) {
    DBG_ERROR(GWEN_LOGDOMAIN, "No medium name given");
    return GWEN_ERROR_INVALID;
  }

  if (stat(GWEN_Crypt_Token_GetTokenName(ct), &st)) {
    if (errno!=ENOENT) {
      DBG_ERROR(GWEN_LOGDOMAIN,
                "Error trying to access key file \"%s\": %s",
                GWEN_Crypt_Token_GetTokenName(ct),
                strerror(errno));
      return GWEN_ERROR_IO;
    }
  }
  else {
    DBG_ERROR(GWEN_LOGDOMAIN,
              "Keyfile \"%s\" already exists, will not create it",
              GWEN_Crypt_Token_GetTokenName(ct));
    return GWEN_ERROR_INVALID;
  }


  /* create file */
  fd=open(GWEN_Crypt_Token_GetTokenName(ct),
          O_RDWR | O_CREAT | O_EXCL
#ifdef OS_WIN32
          | O_BINARY
#endif
          ,
          S_IRUSR|S_IWUSR);


  if (fd==-1) {
    DBG_ERROR(GWEN_LOGDOMAIN,
              "open(%s): %s",
              GWEN_Crypt_Token_GetTokenName(ct),
              strerror(errno));
    return GWEN_ERROR_IO;
  }

  close(fd);

  rv=GWEN_Crypt_TokenFile__WriteFile(ct, 1, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here");
    return rv;
  }

  return 0;
}



int GWENHYWFAR_CB GWEN_Crypt_TokenFile_Open(GWEN_CRYPT_TOKEN *ct, GWEN_UNUSED int admin, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  rv=GWEN_Crypt_TokenFile__ReadFile(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here");
    return rv;
  }

  return 0;
}



int GWENHYWFAR_CB GWEN_Crypt_TokenFile_Close(GWEN_CRYPT_TOKEN *ct, int abandon, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  if (!abandon)
    rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
  else
    rv=0;

  /* free/reset all data */
  GWEN_Crypt_Token_Context_List_Clear(lct->contextList);
  lct->mtime=0;
  lct->ctime=0;

  return rv;
}




int GWENHYWFAR_CB GWEN_Crypt_TokenFile__GetKeyIdList(GWEN_CRYPT_TOKEN *ct,
    uint32_t *pIdList,
    uint32_t *pCount,
    uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int i;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* count keys */
  i=0;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    i+=GWEN_CRYPT_TOKEN_CONTEXT_KEYS;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
  }

  /* if no buffer given just return number of keys */
  if (pIdList==NULL) {
    *pCount=i;
    return 0;
  }

  if (*pCount<i) {
    DBG_INFO(GWEN_LOGDOMAIN, "Buffer too small");
    return GWEN_ERROR_BUFFER_OVERFLOW;
  }

  *pCount=i;
  i=0;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    int j;

    for (j=1; j<=GWEN_CRYPT_TOKEN_CONTEXT_KEYS; j++)
      *(pIdList++)=(i<<16)+j;

    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i++;
  }

  return 0;
}



const GWEN_CRYPT_TOKEN_KEYINFO* GWENHYWFAR_CB
GWEN_Crypt_TokenFile__GetKeyInfo(GWEN_CRYPT_TOKEN *ct,
                                 uint32_t id,
                                 uint32_t flags,
                                 uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  GWEN_CRYPT_TOKEN_KEYINFO *ki;
  int i;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return NULL;
  }

  i=id>>16;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (context out of range)", id);
    return NULL;
  }

  switch(id & 0xffff) {
  case 1:
    ki=GWEN_CTF_Context_GetLocalSignKeyInfo(ctx);
    break;
  case 2:
    ki=GWEN_CTF_Context_GetLocalCryptKeyInfo(ctx);
    break;
  case 3:
    ki=GWEN_CTF_Context_GetRemoteSignKeyInfo(ctx);
    break;
  case 4:
    ki=GWEN_CTF_Context_GetRemoteCryptKeyInfo(ctx);
    break;
  case 5:
    ki=GWEN_CTF_Context_GetLocalAuthKeyInfo(ctx);
    break;
  case 6:
    ki=GWEN_CTF_Context_GetRemoteAuthKeyInfo(ctx);
    break;
  case 7:
    ki=GWEN_CTF_Context_GetTempLocalSignKeyInfo(ctx);
    break;
  default:
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (key id out of range)", id);
    return NULL;
  }

  if (ki==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No key info stored for key %d", id);
    return NULL;
  }

  return ki;
}



#if 0
int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__SetKeyInfo(GWEN_CRYPT_TOKEN *ct,
                                 uint32_t id,
                                 const GWEN_CRYPT_TOKEN_KEYINFO *ki,
                                 uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int i;
  int rv;
  GWEN_CRYPT_TOKEN_KEYINFO *nki;
  GWEN_CRYPT_KEY *key;
  uint32_t flags;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  flags=GWEN_Crypt_Token_KeyInfo_GetFlags(ki);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  i=id>>16;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (context out of range)", id);
    return GWEN_ERROR_NOT_FOUND;
  }

  nki=GWEN_Crypt_Token_KeyInfo_dup(ki);
  assert(nki);
  switch(id & 0xffff) {
  case 1:
    GWEN_CTF_Context_SetLocalSignKeyInfo(ctx, nki);
    key=GWEN_CTF_Context_GetLocalSignKey(ctx);
    break;
  case 2:
    GWEN_CTF_Context_SetLocalCryptKeyInfo(ctx, nki);
    key=GWEN_CTF_Context_GetLocalCryptKey(ctx);
    break;
  case 3:
    GWEN_CTF_Context_SetRemoteSignKeyInfo(ctx, nki);
    key=GWEN_CTF_Context_GetRemoteSignKey(ctx);
    break;
  case 4:
    GWEN_CTF_Context_SetRemoteCryptKeyInfo(ctx, nki);
    key=GWEN_CTF_Context_GetRemoteCryptKey(ctx);
    break;
  case 5:
    GWEN_CTF_Context_SetLocalAuthKeyInfo(ctx, nki);
    key=GWEN_CTF_Context_GetLocalAuthKey(ctx);
    break;
  case 6:
    GWEN_CTF_Context_SetRemoteAuthKeyInfo(ctx, nki);
    key=GWEN_CTF_Context_GetRemoteAuthKey(ctx);
    break;
  default:
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (key id out of range)", id);
    GWEN_Crypt_Token_KeyInfo_free(nki);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* replace key if modulus and exponent are given */
  if ((flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS) &&
      (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT) &&
      id!=1 && /* don't change local keys */
      id!=2 &&
      id!=5) {
    GWEN_CRYPT_KEY *nkey;

    nkey=GWEN_Crypt_KeyRsa_fromModExp(GWEN_Crypt_Token_KeyInfo_GetKeySize(ki),
                                      GWEN_Crypt_Token_KeyInfo_GetModulusData(ki),
                                      GWEN_Crypt_Token_KeyInfo_GetModulusLen(ki),
                                      GWEN_Crypt_Token_KeyInfo_GetExponentData(ki),
                                      GWEN_Crypt_Token_KeyInfo_GetExponentLen(ki));
    assert(nkey);

    if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER)
      GWEN_Crypt_Key_SetKeyNumber(nkey, GWEN_Crypt_Token_KeyInfo_GetKeyNumber(ki));
    if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION)
      GWEN_Crypt_Key_SetKeyVersion(nkey, GWEN_Crypt_Token_KeyInfo_GetKeyVersion(ki));

    /* replace public key */
    switch(id & 0xffff) {
    case 3: /* remote sign key */
      GWEN_CTF_Context_SetRemoteSignKey(ctx, nkey);
      break;
    case 4: /* remote crypt key */
      GWEN_CTF_Context_SetRemoteCryptKey(ctx, nkey);
      break;
    case 6: /* remote auth key */
      GWEN_CTF_Context_SetRemoteAuthKey(ctx, nkey);
      break;
    default:
      DBG_ERROR(GWEN_LOGDOMAIN,
                "Can't set modulus and exponent for private key");
      GWEN_Crypt_Key_free(nkey);
      return GWEN_ERROR_INVALID;
    }
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                         I18N("Public key replaced"));
  }
  else {
    if (key) {
      if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER)
        GWEN_Crypt_Key_SetKeyNumber(key, GWEN_Crypt_Token_KeyInfo_GetKeyNumber(ki));
      if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION)
        GWEN_Crypt_Key_SetKeyVersion(key, GWEN_Crypt_Token_KeyInfo_GetKeyVersion(ki));
    }
  }

  rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Unable to write file");
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Unable to write key file"));
    return rv;
  }

  GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                       I18N("Key file saved"));

  return 0;
}
#endif


int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__SetKeyInfo(GWEN_CRYPT_TOKEN *ct,
                                 uint32_t id,
                                 const GWEN_CRYPT_TOKEN_KEYINFO *ski,
                                 uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int i;
  int rv;
  GWEN_CRYPT_TOKEN_KEYINFO *ki;
  GWEN_CRYPT_KEY *key;
  uint32_t flags;
  uint32_t nflags;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  flags=GWEN_Crypt_Token_KeyInfo_GetFlags(ski);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  i=id>>16;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (context out of range)", id);
    return GWEN_ERROR_NOT_FOUND;
  }

  switch(id & 0xffff) {
  case 1:
    ki=GWEN_CTF_Context_GetLocalSignKeyInfo(ctx);
    key=GWEN_CTF_Context_GetLocalSignKey(ctx);
    break;
  case 2:
    ki=GWEN_CTF_Context_GetLocalCryptKeyInfo(ctx);
    key=GWEN_CTF_Context_GetLocalCryptKey(ctx);
    break;
  case 3:
    ki=GWEN_CTF_Context_GetRemoteSignKeyInfo(ctx);
    key=GWEN_CTF_Context_GetRemoteSignKey(ctx);
    break;
  case 4:
    ki=GWEN_CTF_Context_GetRemoteCryptKeyInfo(ctx);
    key=GWEN_CTF_Context_GetRemoteCryptKey(ctx);
    break;
  case 5:
    ki=GWEN_CTF_Context_GetLocalAuthKeyInfo(ctx);
    key=GWEN_CTF_Context_GetLocalAuthKey(ctx);
    break;
  case 6:
    ki=GWEN_CTF_Context_GetRemoteAuthKeyInfo(ctx);
    key=GWEN_CTF_Context_GetRemoteAuthKey(ctx);
    break;
  case 7:
    ki=GWEN_CTF_Context_GetTempLocalSignKeyInfo(ctx);
    key=GWEN_CTF_Context_GetTempLocalSignKey(ctx);
    break;
  default:
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (key id out of range)", id);
    return GWEN_ERROR_NOT_FOUND;
  }
  assert(ki);

  nflags=GWEN_Crypt_Token_KeyInfo_GetFlags(ki);

  if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASSTATUS) {
    /* ignore for now */
  }

  if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS) {
    nflags&=~GWEN_CRYPT_TOKEN_KEYFLAGS_ACTIONMASK;
    nflags|=(flags & GWEN_CRYPT_TOKEN_KEYFLAGS_ACTIONMASK);
  }

  if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION) {
    uint32_t i=GWEN_Crypt_Token_KeyInfo_GetKeyVersion(ski);
    GWEN_Crypt_Token_KeyInfo_SetKeyVersion(ki, i);
    nflags|=GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION;
    if (key)
      GWEN_Crypt_Key_SetKeyVersion(key, GWEN_Crypt_Token_KeyInfo_GetKeyVersion(ski));
    DBG_INFO(GWEN_LOGDOMAIN, "Setting key version");
  }

  if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER) {
    uint32_t i=GWEN_Crypt_Token_KeyInfo_GetSignCounter(ski);
    GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, i);
    nflags|=GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER;
    DBG_INFO(GWEN_LOGDOMAIN, "Setting signature counter");
  }

  if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER) {
    uint32_t i=GWEN_Crypt_Token_KeyInfo_GetKeyNumber(ski);
    GWEN_Crypt_Token_KeyInfo_SetKeyNumber(ki, i);
    nflags|=GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER;
    if (key)
      GWEN_Crypt_Key_SetKeyNumber(key, GWEN_Crypt_Token_KeyInfo_GetKeyNumber(ski));
    DBG_INFO(GWEN_LOGDOMAIN, "Setting key number");
  }

  /* replace key if modulus and exponent are given */
  if ((flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS) &&
      (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT) &&
      id!=1 && /* don't change local keys */
      id!=2 &&
      id!=5 &&
      id!=7) {
    GWEN_CRYPT_KEY *nkey;

    GWEN_Crypt_Token_KeyInfo_SetKeySize(ki, GWEN_Crypt_Token_KeyInfo_GetKeySize(ski));
    GWEN_Crypt_Token_KeyInfo_SetModulus(ki,
                                        GWEN_Crypt_Token_KeyInfo_GetModulusData(ski),
                                        GWEN_Crypt_Token_KeyInfo_GetModulusLen(ski));
    nflags|=GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS;
    GWEN_Crypt_Token_KeyInfo_SetExponent(ki,
                                         GWEN_Crypt_Token_KeyInfo_GetExponentData(ski),
                                         GWEN_Crypt_Token_KeyInfo_GetExponentLen(ski));
    nflags|=GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT;
    nkey=GWEN_Crypt_KeyRsa_fromModExp(GWEN_Crypt_Token_KeyInfo_GetKeySize(ski),
                                      GWEN_Crypt_Token_KeyInfo_GetModulusData(ski),
                                      GWEN_Crypt_Token_KeyInfo_GetModulusLen(ski),
                                      GWEN_Crypt_Token_KeyInfo_GetExponentData(ski),
                                      GWEN_Crypt_Token_KeyInfo_GetExponentLen(ski));
    assert(nkey);

    if (nflags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER)
      GWEN_Crypt_Key_SetKeyNumber(nkey, GWEN_Crypt_Token_KeyInfo_GetKeyNumber(ki));
    if (nflags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION)
      GWEN_Crypt_Key_SetKeyVersion(nkey, GWEN_Crypt_Token_KeyInfo_GetKeyVersion(ki));

    /* replace public key */
    switch(id & 0xffff) {
    case 3: /* remote sign key */
      GWEN_CTF_Context_SetRemoteSignKey(ctx, nkey);
      break;
    case 4: /* remote crypt key */
      GWEN_CTF_Context_SetRemoteCryptKey(ctx, nkey);
      break;
    case 6: /* remote auth key */
      GWEN_CTF_Context_SetRemoteAuthKey(ctx, nkey);
      break;
    default:
      DBG_ERROR(GWEN_LOGDOMAIN,
                "Can't set modulus and exponent for private key");
      GWEN_Crypt_Key_free(nkey);
      return GWEN_ERROR_INVALID;
    }
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                         I18N("Public key replaced"));
  }
  else {
    if (key) {
      if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER)
        GWEN_Crypt_Key_SetKeyNumber(key, GWEN_Crypt_Token_KeyInfo_GetKeyNumber(ki));
      if (flags & GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION)
        GWEN_Crypt_Key_SetKeyVersion(key, GWEN_Crypt_Token_KeyInfo_GetKeyVersion(ki));
    }
  }

  GWEN_Crypt_Token_KeyInfo_SetFlags(ki, nflags);

  rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Unable to write file");
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Unable to write key file"));
    return rv;
  }

  GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                       I18N("Key file saved"));

  return 0;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__ActivateKey(GWEN_CRYPT_TOKEN *ct, uint32_t id, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int rv;
  int i;
  uint32_t keyNum;
  uint8_t kbuf[GWEN_CRYPT_KEYRSA_MAX_KEYLENGTH];
  uint32_t klen;
  GWEN_CRYPT_TOKEN_KEYINFO *cki=NULL;
  GWEN_CRYPT_TOKEN_KEYINFO *ki;
  GWEN_CRYPT_KEY *key;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  keyNum=id & 0xffff;

  i=id>>16;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (context out of range)", id);
    return GWEN_ERROR_NOT_FOUND;
  }

  key=GWEN_CTF_Context_GetTempLocalSignKey(ctx);
  if (key==NULL) {
    DBG_ERROR(GWEN_LOGDOMAIN, "No temporary local sign key.");
    return GWEN_ERROR_NOT_FOUND;
  }
  key=GWEN_Crypt_KeyRsa_dup(key);

  /* set key */
  if (keyNum==1)
    cki=GWEN_CTF_Context_GetLocalSignKeyInfo(ctx);
  else if (keyNum==6)
    cki=GWEN_CTF_Context_GetLocalAuthKeyInfo(ctx);
  else {
    GWEN_Gui_ProgressLog2(gid, GWEN_LoggerLevel_Error,
                          I18N("Invalid key id %02x"), id);
    GWEN_Crypt_Key_free(key);
    return GWEN_ERROR_NO_DATA;
  }
  if (cki==NULL) {
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("No key info found"));
    GWEN_Crypt_Key_free(key);
    return GWEN_ERROR_NO_DATA;
  }

  /* update key info for the key */
  ki=GWEN_Crypt_Token_KeyInfo_dup(cki);
  assert(ki);

  /* get modulus */
  klen=sizeof(kbuf);
  rv=GWEN_Crypt_KeyRsa_GetModulus(key, kbuf, &klen);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "No modulus for key");
    GWEN_Crypt_Token_KeyInfo_free(ki);
    GWEN_Crypt_Key_free(key);
    return rv;
  }
  GWEN_Crypt_Token_KeyInfo_SetModulus(ki, kbuf, klen);

  /* get exponent */
  klen=sizeof(kbuf);
  rv=GWEN_Crypt_KeyRsa_GetExponent(key, kbuf, &klen);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "No exponent for key");
    GWEN_Crypt_Token_KeyInfo_free(ki);
    GWEN_Crypt_Key_free(key);
    return rv;
  }
  GWEN_Crypt_Token_KeyInfo_SetExponent(ki, kbuf, klen);
  GWEN_Crypt_Token_KeyInfo_SetKeyNumber(ki, GWEN_Crypt_Key_GetKeyNumber(key));
  GWEN_Crypt_Token_KeyInfo_SetKeyVersion(ki, GWEN_Crypt_Key_GetKeyVersion(key));

  if (keyNum==1) {
    if (GWEN_Crypt_Token_GetModes(ct) & GWEN_CRYPT_TOKEN_MODE_DIRECT_SIGN) {
      DBG_DEBUG(GWEN_LOGDOMAIN, "Adding mode \"direct sign\" to key");
      GWEN_Crypt_KeyRsa_AddFlags(key, GWEN_CRYPT_KEYRSA_FLAGS_DIRECTSIGN);
    }
    GWEN_CTF_Context_SetLocalSignKey(ctx, key);
    GWEN_Crypt_Token_KeyInfo_AddFlags(ki,
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANVERIFY |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANSIGN);
    GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, 1);
    GWEN_CTF_Context_SetLocalSignKeyInfo(ctx, ki);
  }
  else if (keyNum==6) {
    if (GWEN_Crypt_Token_GetModes(ct) & GWEN_CRYPT_TOKEN_MODE_DIRECT_SIGN) {
      DBG_DEBUG(GWEN_LOGDOMAIN, "Adding mode \"direct sign\" to key");
      GWEN_Crypt_KeyRsa_AddFlags(key, GWEN_CRYPT_KEYRSA_FLAGS_DIRECTSIGN);
    }
    GWEN_CTF_Context_SetLocalAuthKey(ctx, key);
    GWEN_Crypt_Token_KeyInfo_AddFlags(ki,
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANVERIFY |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANSIGN);
    GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, 1);
    GWEN_CTF_Context_SetLocalAuthKeyInfo(ctx, ki);
  }

  rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Unable to write file");
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Unable to write key file"));
    return rv;
  }

  GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                       I18N("Key file saved"));

  return 0;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__GetContextIdList(GWEN_CRYPT_TOKEN *ct,
                                       uint32_t *pIdList,
                                       uint32_t *pCount,
                                       uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int i;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* count keys */
  i=0;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    i++;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
  }

  /* store number of entries */
  *pCount=i;

  /* if no buffer given just return number of keys */
  if (pIdList==NULL)
    return 0;

  if (*pCount<i) {
    DBG_INFO(GWEN_LOGDOMAIN, "Buffer too small");
    return GWEN_ERROR_BUFFER_OVERFLOW;
  }

  i=1;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    *(pIdList++)=i;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i++;
  }

  return 0;
}



const GWEN_CRYPT_TOKEN_CONTEXT* GWENHYWFAR_CB
GWEN_Crypt_TokenFile__GetContext(GWEN_CRYPT_TOKEN *ct,
                                 uint32_t id,
                                 uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return NULL;
  }

  if (id==0) {
    DBG_INFO(GWEN_LOGDOMAIN, "Invalid context id 0");
    return NULL;
  }

  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (GWEN_Crypt_Token_Context_GetId(ctx)==id)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No context by id [%x] known", id);
    return NULL;
  }

  return ctx;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__SetContext(GWEN_CRYPT_TOKEN *ct,
                                 uint32_t id,
                                 const GWEN_CRYPT_TOKEN_CONTEXT *nctx,
                                 uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int rv;
  const char *s;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  if (id==0) {
    DBG_INFO(GWEN_LOGDOMAIN, "Invalid context id 0");
    return GWEN_ERROR_INVALID;
  }

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (GWEN_Crypt_Token_Context_GetId(ctx)==id)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No context by id [%x] known", id);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* copy user data from context */
  s=GWEN_Crypt_Token_Context_GetServiceId(nctx);
  GWEN_Crypt_Token_Context_SetServiceId(ctx, s);
  s=GWEN_Crypt_Token_Context_GetUserId(nctx);
  GWEN_Crypt_Token_Context_SetUserId(ctx, s);
  s=GWEN_Crypt_Token_Context_GetUserName(nctx);
  GWEN_Crypt_Token_Context_SetUserName(ctx, s);
  s=GWEN_Crypt_Token_Context_GetPeerId(nctx);
  GWEN_Crypt_Token_Context_SetPeerId(ctx, s);
  s=GWEN_Crypt_Token_Context_GetAddress(nctx);
  GWEN_Crypt_Token_Context_SetAddress(ctx, s);
  GWEN_Crypt_Token_Context_SetPort(ctx, GWEN_Crypt_Token_Context_GetPort(nctx));
  s=GWEN_Crypt_Token_Context_GetSystemId(nctx);
  GWEN_Crypt_Token_Context_SetSystemId(ctx, s);

  return 0;
}



GWEN_CRYPT_KEY *GWEN_Crypt_TokenFile__GetKey(GWEN_CRYPT_TOKEN *ct, uint32_t id, uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int i;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return NULL;
  }

  i=id>>16;
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (context out of range)", id);
    return NULL;
  }

  switch(id & 0xffff) {
  case 1:
    return GWEN_CTF_Context_GetLocalSignKey(ctx);
  case 2:
    return GWEN_CTF_Context_GetLocalCryptKey(ctx);
  case 3:
    return GWEN_CTF_Context_GetRemoteSignKey(ctx);
  case 4:
    return GWEN_CTF_Context_GetRemoteCryptKey(ctx);
  case 5:
    return GWEN_CTF_Context_GetLocalAuthKey(ctx);
  case 6:
    return GWEN_CTF_Context_GetRemoteAuthKey(ctx);
  default:
    DBG_INFO(GWEN_LOGDOMAIN, "No key by id [%x] known (key id out of range)", id);
    return NULL;
  }
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__Sign(GWEN_CRYPT_TOKEN *ct,
                           uint32_t keyId,
                           GWEN_CRYPT_PADDALGO *a,
                           const uint8_t *pInData,
                           uint32_t inLen,
                           uint8_t *pSignatureData,
                           uint32_t *pSignatureLen,
                           uint32_t *pSeqCounter,
                           uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  GWEN_CRYPT_KEY *k;
  int keyNum;
  GWEN_BUFFER *srcBuf;
  int i;
  int rv;
  GWEN_CRYPT_PADDALGOID aid;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  DBG_INFO(GWEN_LOGDOMAIN, "Signing with key %d", keyId);
  aid=GWEN_Crypt_PaddAlgo_GetId(a);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* get context */
  i=(keyId>>16);
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  if (ctx==NULL) {
    DBG_ERROR(GWEN_LOGDOMAIN, "Token has no context");
    return GWEN_ERROR_NOT_FOUND;
  }
  while(ctx) {
    if (i==0)
      break;
    DBG_ERROR(GWEN_LOGDOMAIN, "Checking token %d (i==%d)",
              GWEN_Crypt_Token_Context_GetId(ctx), i);
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No context by id [%x] known", (keyId>>16) & 0xffff);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* get key */
  keyNum=keyId & 0xffff;
  if (keyNum!=1 && keyNum!=5) {
    /* neither localSignKey nor localAuthKey */
    DBG_INFO(GWEN_LOGDOMAIN, "Bad key for signing (%x)", keyId);
    return GWEN_ERROR_INVALID;
  }

  k=GWEN_Crypt_TokenFile__GetKey(ct, keyId, gid);
  if (k==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "Key not found");
    return GWEN_ERROR_NOT_FOUND;
  }

  /* copy to a buffer for padding */
  srcBuf=GWEN_Buffer_new(0, inLen, 0, 0);

  if (aid==GWEN_Crypt_PaddAlgoId_Pkcs1_Pss_Sha256) {
    const GWEN_CRYPT_TOKEN_KEYINFO *ki;
    int nbits;
    const uint8_t *modPtr;
    uint32_t modLen;
    GWEN_MDIGEST *md;

    switch(keyId & 0xffff) {
    case 1:
      ki=GWEN_CTF_Context_GetLocalSignKeyInfo(ctx);
      break;
    case 5:
      ki=GWEN_CTF_Context_GetLocalAuthKeyInfo(ctx);
      break;
    default:
      ki=NULL;
    }

    if (ki==NULL) {
      DBG_ERROR(GWEN_LOGDOMAIN, "No information for key %d", keyNum);
      GWEN_Buffer_free(srcBuf);
      return GWEN_ERROR_GENERIC;
    }

    /* calculate real number of bits */
    modPtr=GWEN_Crypt_Token_KeyInfo_GetModulusData(ki);
    modLen=GWEN_Crypt_Token_KeyInfo_GetModulusLen(ki);
    nbits=modLen*8;
    while(modLen && *modPtr==0) {
      nbits-=8;
      modLen--;
      modPtr++;
    }
    if (modLen) {
      uint8_t b=*modPtr;
      int i;
      uint8_t mask=0x80;

      for (i=0; i<8; i++) {
        if (b & mask)
          break;
        nbits--;
        mask>>=1;
      }
    }

    if (nbits==0) {
      DBG_ERROR(GWEN_LOGDOMAIN, "Empty modulus");
      GWEN_Buffer_free(srcBuf);
      return GWEN_ERROR_GENERIC;
    }

    md=GWEN_MDigest_Sha256_new();
    GWEN_Buffer_AllocRoom(srcBuf, modLen);

    rv=GWEN_Padd_AddPkcs1Pss((uint8_t*) GWEN_Buffer_GetStart(srcBuf),
                             GWEN_Buffer_GetMaxUnsegmentedWrite(srcBuf),
                             nbits,
                             pInData, inLen,
                             inLen,
                             md);
    GWEN_MDigest_free(md);
    if (rv<0) {
      DBG_ERROR(GWEN_LOGDOMAIN, "here (%d)", rv);
      GWEN_Buffer_free(srcBuf);
      return rv;
    }

    GWEN_Buffer_IncrementPos(srcBuf, rv);
    GWEN_Buffer_AdjustUsedBytes(srcBuf);
  }
  else {
    GWEN_Buffer_AppendBytes(srcBuf, (const char*)pInData, inLen);

    /* padd according to given algo */
    rv=GWEN_Padd_ApplyPaddAlgo(a, srcBuf);
    if (rv) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
      GWEN_Buffer_free(srcBuf);
      return rv;
    }
  }

  /* sign with key */
  rv=GWEN_Crypt_Key_Sign(k,
                         (const uint8_t*)GWEN_Buffer_GetStart(srcBuf),
                         GWEN_Buffer_GetUsedBytes(srcBuf),
                         pSignatureData,
                         pSignatureLen);
  GWEN_Buffer_free(srcBuf);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  if (pSeqCounter) {
    GWEN_CRYPT_TOKEN_KEYINFO *ki;

    /* signature sequence counter is to be incremented */
    switch(keyId & 0xffff) {
    case 1:
      ki=GWEN_CTF_Context_GetLocalSignKeyInfo(ctx);
      break;
    case 5:
      ki=GWEN_CTF_Context_GetLocalAuthKeyInfo(ctx);
      break;
    default:
      ki=NULL;
    }
    if (ki &&
        (GWEN_Crypt_Token_KeyInfo_GetFlags(ki) & GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER)) {
      unsigned int seq;

      seq=GWEN_Crypt_Token_KeyInfo_GetSignCounter(ki);
      *pSeqCounter=seq;
      GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, ++seq);

      rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
      if (rv) {
        DBG_INFO(GWEN_LOGDOMAIN, "Unable to write file");
        return rv;
      }
    }
    else {
      DBG_WARN(GWEN_LOGDOMAIN, "No sign counter for key %04x", keyId);
      *pSeqCounter=0;
    }
  }

  return 0;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__Verify(GWEN_CRYPT_TOKEN *ct,
                             uint32_t keyId,
                             GWEN_CRYPT_PADDALGO *a,
                             const uint8_t *pInData,
                             uint32_t inLen,
                             const uint8_t *pSignatureData,
                             uint32_t signatureLen,
                             uint32_t seqCounter,
                             uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  GWEN_CRYPT_KEY *k;
  int keyNum;
  int i;
  int rv;
  GWEN_CRYPT_PADDALGOID aid;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  DBG_INFO(GWEN_LOGDOMAIN, "Verifying with key %d", keyId);

  aid=GWEN_Crypt_PaddAlgo_GetId(a);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* get context */
  i=(keyId>>16);
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No context by id [%x] known", (keyId>>16) & 0xffff);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* get key */
  keyNum=keyId & 0xffff;
  if (keyNum!=1 && keyNum!=3 && keyNum!=6) {
    /* neither remoteSignKey nor remoteAuthKey */
    DBG_INFO(GWEN_LOGDOMAIN, "Bad key for verifying (%x)", keyId);
    return GWEN_ERROR_INVALID;
  }

  k=GWEN_Crypt_TokenFile__GetKey(ct, keyId, gid);
  if (k==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "Key not found");
    return GWEN_ERROR_NO_KEY;
  }

  if (aid==GWEN_Crypt_PaddAlgoId_Iso9796_2 ||
      aid==GWEN_Crypt_PaddAlgoId_Pkcs1_2 ||
      aid==GWEN_Crypt_PaddAlgoId_Pkcs1_Pss_Sha256) {
    GWEN_BUFFER *tbuf;
    uint32_t l;

    /* these algos add random numbers, we must use encrypt fn here and
     * compare the decrypted and unpadded data with the source data */
    tbuf=GWEN_Buffer_new(0, signatureLen+16, 0, 0);
    l=GWEN_Buffer_GetMaxUnsegmentedWrite(tbuf);
    rv=GWEN_Crypt_Key_Encipher(k,
                               pSignatureData, signatureLen,
                               (uint8_t*)GWEN_Buffer_GetStart(tbuf),
                               &l);
    if (rv<0) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
      GWEN_Buffer_free(tbuf);
      return rv;
    }
    GWEN_Buffer_IncrementPos(tbuf, l);
    GWEN_Buffer_AdjustUsedBytes(tbuf);

    if (aid==GWEN_Crypt_PaddAlgoId_Pkcs1_Pss_Sha256) {
      const GWEN_CRYPT_TOKEN_KEYINFO *ki;
      int nbits;
      const uint8_t *modPtr;
      uint32_t modLen;
      GWEN_MDIGEST *md;

      if (keyNum==3)
        ki=GWEN_CTF_Context_GetRemoteSignKeyInfo(ctx);
      else
        ki=GWEN_CTF_Context_GetRemoteAuthKeyInfo(ctx);
      if (ki==NULL) {
        DBG_ERROR(GWEN_LOGDOMAIN, "No information for key %d", keyNum);
        GWEN_Buffer_free(tbuf);
        return GWEN_ERROR_GENERIC;
      }

      /* calculate real number of bits */
      modPtr=GWEN_Crypt_Token_KeyInfo_GetModulusData(ki);
      modLen=GWEN_Crypt_Token_KeyInfo_GetModulusLen(ki);
      nbits=modLen*8;
      while(modLen && *modPtr==0) {
        nbits-=8;
        modLen--;
        modPtr++;
      }
      if (modLen) {
        uint8_t b=*modPtr;
        int i;
        uint8_t mask=0x80;

        for (i=0; i<8; i++) {
          if (b & mask)
            break;
          nbits--;
          mask>>=1;
        }
      }

      if (nbits==0) {
        DBG_ERROR(GWEN_LOGDOMAIN, "Empty modulus");
        GWEN_Buffer_free(tbuf);
        return GWEN_ERROR_GENERIC;
      }

      md=GWEN_MDigest_Sha256_new();
      rv=GWEN_Padd_VerifyPkcs1Pss((const uint8_t*) GWEN_Buffer_GetStart(tbuf),
                                  GWEN_Buffer_GetUsedBytes(tbuf),
                                  nbits,
                                  pInData, inLen,
                                  inLen,
                                  md);
      GWEN_MDigest_free(md);
      if (rv<0) {
        DBG_ERROR(GWEN_LOGDOMAIN, "here (%d)", rv);
        GWEN_Buffer_free(tbuf);
        return rv;
      }
    }
    else {
      rv=GWEN_Padd_UnapplyPaddAlgo(a, tbuf);
      if (rv<0) {
        DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
        GWEN_Buffer_free(tbuf);
        return rv;
      }
      l=GWEN_Buffer_GetUsedBytes(tbuf);

      if (l!=inLen) {
        DBG_ERROR(GWEN_LOGDOMAIN, "Signature length doesn't match");
        GWEN_Buffer_free(tbuf);
        return GWEN_ERROR_VERIFY;
      }
      if (memcmp(pInData, GWEN_Buffer_GetStart(tbuf), l)!=0) {
        DBG_ERROR(GWEN_LOGDOMAIN, "Signature doesn't match:");
        GWEN_Buffer_free(tbuf);
        return GWEN_ERROR_VERIFY;
      }
    }
    GWEN_Buffer_free(tbuf);
  }
  else {
    GWEN_BUFFER *srcBuf;

    /* copy to a buffer for padding */
    srcBuf=GWEN_Buffer_new(0, inLen, 0, 0);
    GWEN_Buffer_AppendBytes(srcBuf, (const char*)pInData, inLen);

    /* padd according to given algo */
    rv=GWEN_Padd_ApplyPaddAlgo(a, srcBuf);
    if (rv) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
      GWEN_Buffer_free(srcBuf);
      return rv;
    }

    /* verify with key */
    rv=GWEN_Crypt_Key_Verify(k,
                             (const uint8_t*)GWEN_Buffer_GetStart(srcBuf),
                             GWEN_Buffer_GetUsedBytes(srcBuf),
                             pSignatureData,
                             signatureLen);
    GWEN_Buffer_free(srcBuf);
    if (rv) {
      DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
      return rv;
    }
  }

  if (seqCounter) {
    GWEN_CRYPT_TOKEN_KEYINFO *ki;

    /* signature sequence counter is to be checked */
    if (keyNum==3)
      ki=GWEN_CTF_Context_GetRemoteSignKeyInfo(ctx);
    else
      ki=GWEN_CTF_Context_GetRemoteAuthKeyInfo(ctx);
    if (ki &&
        (GWEN_Crypt_Token_KeyInfo_GetFlags(ki) & GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER)) {
      unsigned int seq;

      seq=GWEN_Crypt_Token_KeyInfo_GetSignCounter(ki);

      if (seq>=seqCounter) {
        DBG_WARN(GWEN_LOGDOMAIN, "Bad remote sequence counter (possibly replay attack!)");
        return GWEN_ERROR_VERIFY;
      }
      GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, seqCounter);

      /* write file */
      rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
      if (rv) {
        DBG_INFO(GWEN_LOGDOMAIN, "Unable to write file");
        return rv;
      }
    }
    else {
      DBG_WARN(GWEN_LOGDOMAIN, "No sign counter for key %04x", keyId);
    }

  }

  return 0;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__Encipher(GWEN_CRYPT_TOKEN *ct,
                               uint32_t keyId,
                               GWEN_CRYPT_PADDALGO *a,
                               const uint8_t *pInData,
                               uint32_t inLen,
                               uint8_t *pOutData,
                               uint32_t *pOutLen,
                               uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  GWEN_CRYPT_KEY *k;
  int keyNum;
  GWEN_BUFFER *srcBuf;
  int i;
  int rv;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  DBG_INFO(GWEN_LOGDOMAIN, "Enciphering with key %d", keyId);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* get context */
  i=(keyId>>16);
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No context by id [%x] known", (keyId>>16) & 0xffff);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* get key */
  keyNum=keyId & 0xffff;
  if (keyNum!=2 && keyNum!=4) {
    /* not remoteCryptKey */
    DBG_INFO(GWEN_LOGDOMAIN, "Bad key for encrypting (%x)", keyId);
    return GWEN_ERROR_INVALID;
  }

  k=GWEN_Crypt_TokenFile__GetKey(ct, keyId, gid);
  if (k==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "Key %d not found", keyId);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* copy to a buffer for padding */
  srcBuf=GWEN_Buffer_new(0, inLen, 0, 0);
  GWEN_Buffer_AppendBytes(srcBuf, (const char*)pInData, inLen);
  GWEN_Buffer_Rewind(srcBuf);

  /* padd according to given algo */
  rv=GWEN_Padd_ApplyPaddAlgo(a, srcBuf);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(srcBuf);
    return rv;
  }

  /* encipher with key */
  rv=GWEN_Crypt_Key_Encipher(k,
                             (const uint8_t*)GWEN_Buffer_GetStart(srcBuf),
                             GWEN_Buffer_GetUsedBytes(srcBuf),
                             pOutData,
                             pOutLen);
  GWEN_Buffer_free(srcBuf);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  return 0;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__Decipher(GWEN_CRYPT_TOKEN *ct,
                               uint32_t keyId,
                               GWEN_CRYPT_PADDALGO *a,
                               const uint8_t *pInData,
                               uint32_t inLen,
                               uint8_t *pOutData,
                               uint32_t *pOutLen,
                               uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  GWEN_CRYPT_KEY *k;
  int keyNum;
  GWEN_BUFFER *tbuf;
  int i;
  int rv;
  uint32_t l;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  DBG_INFO(GWEN_LOGDOMAIN, "Deciphering with key %d", keyId);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  /* get context */
  i=(keyId>>16);
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  if (ctx==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "No context by id [%x] known", (keyId>>16) & 0xffff);
    return GWEN_ERROR_NOT_FOUND;
  }

  /* get key */
  keyNum=keyId & 0xffff;
  if (keyNum!=2 && keyNum!=4) {
    /* not localCryptKey */
    DBG_INFO(GWEN_LOGDOMAIN, "Bad key for decrypting (%x)", keyId);
    return GWEN_ERROR_INVALID;
  }

  k=GWEN_Crypt_TokenFile__GetKey(ct, keyId, gid);
  if (k==NULL) {
    DBG_INFO(GWEN_LOGDOMAIN, "Key not found");
    return GWEN_ERROR_NOT_FOUND;
  }

  /* decipher with key */
  tbuf=GWEN_Buffer_new(0, inLen+16, 0, 1);
  l=GWEN_Buffer_GetMaxUnsegmentedWrite(tbuf);
  rv=GWEN_Crypt_Key_Decipher(k,
                             pInData, inLen,
                             (uint8_t*)GWEN_Buffer_GetStart(tbuf), &l);
  if (rv<0) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(tbuf);
    return rv;
  }
  GWEN_Buffer_IncrementPos(tbuf, l);
  GWEN_Buffer_AdjustUsedBytes(tbuf);

  /* unpadd according to given algo */
  rv=GWEN_Padd_UnapplyPaddAlgo(a, tbuf);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(tbuf);
    return rv;
  }

  /* copy resulting data to given buffer */
  l=GWEN_Buffer_GetUsedBytes(tbuf);
  if (l>*pOutLen) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    GWEN_Buffer_free(tbuf);
    return GWEN_ERROR_BUFFER_OVERFLOW;
  }
  memmove(pOutData, GWEN_Buffer_GetStart(tbuf), l);
  *pOutLen=l;
  GWEN_Buffer_free(tbuf);

  return 0;
}



int GWENHYWFAR_CB
GWEN_Crypt_TokenFile__GenerateKey(GWEN_CRYPT_TOKEN *ct,
                                  uint32_t keyId,
                                  const GWEN_CRYPT_CRYPTALGO *a,
                                  uint32_t gid) {
  GWEN_CRYPT_TOKEN_FILE *lct;
  GWEN_CRYPT_KEY *pubKey;
  GWEN_CRYPT_KEY *secKey;
  int rv;
  uint32_t keyNum;
  GWEN_CRYPT_TOKEN_CONTEXT *ctx;
  int i;
  uint8_t kbuf[GWEN_CRYPT_KEYRSA_MAX_KEYLENGTH];
  uint32_t klen;
  GWEN_CRYPT_TOKEN_KEYINFO *cki;
  GWEN_CRYPT_TOKEN_KEYINFO *ki;

  assert(ct);
  lct=GWEN_INHERIT_GETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct);
  assert(lct);

  /* reload if needed */
  rv=GWEN_Crypt_TokenFile__ReloadIfNeeded(ct, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    return rv;
  }

  keyNum=keyId & 0xffff;

  /* check key id */
  if (keyNum!=1 && keyNum!=2 && keyNum!=5 && keyNum!=7) {
    DBG_INFO(GWEN_LOGDOMAIN, "Can only generate local keys.");
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Can only generate local keys."));
    return GWEN_ERROR_NOT_SUPPORTED;
  }

  /* check for algo */
  if (GWEN_Crypt_CryptAlgo_GetId(a)!=GWEN_Crypt_CryptAlgoId_Rsa) {
    DBG_INFO(GWEN_LOGDOMAIN, "Only RSA keys supported.");
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Only RSA keys supported."));
    return GWEN_ERROR_NOT_SUPPORTED;
  }

  /* get context */
  i=(keyId>>16);
  ctx=GWEN_Crypt_Token_Context_List_First(lct->contextList);
  while(ctx) {
    if (i==0)
      break;
    ctx=GWEN_Crypt_Token_Context_List_Next(ctx);
    i--;
  }

  /* generate key pair */
  DBG_INFO(GWEN_LOGDOMAIN, "Creating key pair using %d bytes", GWEN_Crypt_CryptAlgo_GetChunkSize(a));
  rv=GWEN_Crypt_KeyRsa_GeneratePair(GWEN_Crypt_CryptAlgo_GetChunkSize(a),
                                    (GWEN_Crypt_Token_GetModes(ct) &
                                     GWEN_CRYPT_TOKEN_MODE_EXP_65537)?1:0,
                                    &pubKey,
                                    &secKey);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Could not generate key"));
    return rv;
  }

  GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                       I18N("Key generated"));

  /* set key */
  if (keyNum==1)
    cki=GWEN_CTF_Context_GetLocalSignKeyInfo(ctx);
  else if (keyNum==2)
    cki=GWEN_CTF_Context_GetLocalCryptKeyInfo(ctx);
  else if (keyNum==5)
    cki=GWEN_CTF_Context_GetLocalAuthKeyInfo(ctx);
  else if (keyNum==7)
    cki=GWEN_CTF_Context_GetTempLocalSignKeyInfo(ctx);
  else
    cki=NULL;

  if (cki==NULL) {
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("No key info found"));
    return GWEN_ERROR_NO_DATA;
  }

  /* update key info for the key */
  ki=GWEN_Crypt_Token_KeyInfo_dup(cki);
  assert(ki);

  /* get modulus */
  klen=sizeof(kbuf);
  rv=GWEN_Crypt_KeyRsa_GetModulus(pubKey, kbuf, &klen);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "No modulus for key");
    GWEN_Crypt_Token_KeyInfo_free(ki);
    GWEN_Crypt_Key_free(pubKey);
    return rv;
  }
  GWEN_Crypt_Token_KeyInfo_SetModulus(ki, kbuf, klen);

  /* get exponent */
  klen=sizeof(kbuf);
  rv=GWEN_Crypt_KeyRsa_GetExponent(pubKey, kbuf, &klen);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "No exponent for key");
    GWEN_Crypt_Token_KeyInfo_free(ki);
    GWEN_Crypt_Key_free(pubKey);
    return rv;
  }
  GWEN_Crypt_Token_KeyInfo_SetExponent(ki, kbuf, klen);
  GWEN_Crypt_Token_KeyInfo_SetKeyNumber(ki, GWEN_Crypt_Key_GetKeyNumber(pubKey));
  GWEN_Crypt_Token_KeyInfo_SetKeyVersion(ki, GWEN_Crypt_Key_GetKeyVersion(pubKey));

  if (keyNum==1) {
    if (GWEN_Crypt_Token_GetModes(ct) & GWEN_CRYPT_TOKEN_MODE_DIRECT_SIGN) {
      DBG_DEBUG(GWEN_LOGDOMAIN, "Adding mode \"direct sign\" to key");
      GWEN_Crypt_KeyRsa_AddFlags(secKey, GWEN_CRYPT_KEYRSA_FLAGS_DIRECTSIGN);
    }
    GWEN_CTF_Context_SetLocalSignKey(ctx, secKey);
    GWEN_Crypt_Token_KeyInfo_AddFlags(ki,
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANVERIFY |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANSIGN);
    GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, 1);
    GWEN_CTF_Context_SetLocalSignKeyInfo(ctx, ki);
  }
  else if (keyNum==2) {
    GWEN_CTF_Context_SetLocalCryptKey(ctx, secKey);
    GWEN_Crypt_Token_KeyInfo_AddFlags(ki,
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANENCIPHER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANDECIPHER);
    GWEN_CTF_Context_SetLocalCryptKeyInfo(ctx, ki);
  }
  else if (keyNum==5) {
    if (GWEN_Crypt_Token_GetModes(ct) & GWEN_CRYPT_TOKEN_MODE_DIRECT_SIGN) {
      DBG_DEBUG(GWEN_LOGDOMAIN, "Adding mode \"direct sign\" to key");
      GWEN_Crypt_KeyRsa_AddFlags(secKey, GWEN_CRYPT_KEYRSA_FLAGS_DIRECTSIGN);
    }
    GWEN_CTF_Context_SetLocalAuthKey(ctx, secKey);
    GWEN_Crypt_Token_KeyInfo_AddFlags(ki,
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANVERIFY |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANSIGN);
    GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, 1);
    GWEN_CTF_Context_SetLocalAuthKeyInfo(ctx, ki);
  }
  else if (keyNum==7) {
    if (GWEN_Crypt_Token_GetModes(ct) & GWEN_CRYPT_TOKEN_MODE_DIRECT_SIGN) {
      DBG_DEBUG(GWEN_LOGDOMAIN, "Adding mode \"direct sign\" to key");
      GWEN_Crypt_KeyRsa_AddFlags(secKey, GWEN_CRYPT_KEYRSA_FLAGS_DIRECTSIGN);
    }
    GWEN_CTF_Context_SetTempLocalSignKey(ctx, secKey);
    GWEN_Crypt_Token_KeyInfo_AddFlags(ki,
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASMODULUS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASEXPONENT |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYNUMBER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASKEYVERSION |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASSIGNCOUNTER |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_HASACTIONFLAGS |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANVERIFY |
                                      GWEN_CRYPT_TOKEN_KEYFLAGS_CANSIGN);
    GWEN_Crypt_Token_KeyInfo_SetSignCounter(ki, 1);
    GWEN_CTF_Context_SetTempLocalSignKeyInfo(ctx, ki);
  }

  /* the public key is not used */
  GWEN_Crypt_Key_free(pubKey);

  rv=GWEN_Crypt_TokenFile__WriteFile(ct, 0, gid);
  if (rv) {
    DBG_INFO(GWEN_LOGDOMAIN, "Unable to write file");
    GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Error,
                         I18N("Unable to write key file"));
    return rv;
  }

  GWEN_Gui_ProgressLog(gid, GWEN_LoggerLevel_Notice,
                       I18N("Key generated and set"));

  return 0;
}






GWENHYWFAR_CB
void GWEN_Crypt_TokenFile_freeData(GWEN_UNUSED void *bp, void *p) {
  GWEN_CRYPT_TOKEN_FILE *lct;

  lct=(GWEN_CRYPT_TOKEN_FILE*) p;
  GWEN_Crypt_Token_Context_List_free(lct->contextList);

  GWEN_FREE_OBJECT(lct);
}



GWEN_CRYPT_TOKEN *GWEN_Crypt_TokenFile_new(const char *typeName,
    const char *tokenName) {
  GWEN_CRYPT_TOKEN *ct;
  GWEN_CRYPT_TOKEN_FILE *lct;

  ct=GWEN_Crypt_Token_new(GWEN_Crypt_Token_Device_File, typeName, tokenName);
  assert(ct);

  GWEN_NEW_OBJECT(GWEN_CRYPT_TOKEN_FILE, lct);
  lct->contextList=GWEN_Crypt_Token_Context_List_new();
  GWEN_INHERIT_SETDATA(GWEN_CRYPT_TOKEN, GWEN_CRYPT_TOKEN_FILE, ct, lct,
                       GWEN_Crypt_TokenFile_freeData);
  GWEN_Crypt_Token_SetOpenFn(ct, GWEN_Crypt_TokenFile_Open);
  GWEN_Crypt_Token_SetCreateFn(ct, GWEN_Crypt_TokenFile_Create);
  GWEN_Crypt_Token_SetCloseFn(ct, GWEN_Crypt_TokenFile_Close);
  GWEN_Crypt_Token_SetGetKeyIdListFn(ct, GWEN_Crypt_TokenFile__GetKeyIdList);
  GWEN_Crypt_Token_SetGetKeyInfoFn(ct, GWEN_Crypt_TokenFile__GetKeyInfo);
  GWEN_Crypt_Token_SetSetKeyInfoFn(ct, GWEN_Crypt_TokenFile__SetKeyInfo);
  GWEN_Crypt_Token_SetGetContextIdListFn(ct, GWEN_Crypt_TokenFile__GetContextIdList);
  GWEN_Crypt_Token_SetGetContextFn(ct, GWEN_Crypt_TokenFile__GetContext);
  GWEN_Crypt_Token_SetSetContextFn(ct, GWEN_Crypt_TokenFile__SetContext);
  GWEN_Crypt_Token_SetSignFn(ct, GWEN_Crypt_TokenFile__Sign);
  GWEN_Crypt_Token_SetVerifyFn(ct, GWEN_Crypt_TokenFile__Verify);
  GWEN_Crypt_Token_SetEncipherFn(ct, GWEN_Crypt_TokenFile__Encipher);
  GWEN_Crypt_Token_SetDecipherFn(ct, GWEN_Crypt_TokenFile__Decipher);
  GWEN_Crypt_Token_SetGenerateKeyFn(ct, GWEN_Crypt_TokenFile__GenerateKey);
  GWEN_Crypt_Token_SetActivateKeyFn(ct, GWEN_Crypt_TokenFile__ActivateKey);

  return ct;
}





