do_sign (app_t app, const char *keyidstr, int hashalgo,
         gpg_error_t (*pincb)(void*, const char *, char **),
         void *pincb_arg,
         const void *indata, size_t indatalen,
         unsigned char **outdata, size_t *outdatalen )
{
  static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
      0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
  static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
      0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
  int rc, i;
  int is_sigg = 0;
  int fid;
  unsigned char kid;
  unsigned char data[83];   /* Must be large enough for a SHA-1 digest
                               + the largest OID prefix. */
  size_t datalen;

  if (!keyidstr || !*keyidstr)
    return gpg_error (GPG_ERR_INV_VALUE);
  switch (indatalen)
    {
    case 16: case 20: case 35: case 47: case 51: case 67: case 83: break;
    default: return gpg_error (GPG_ERR_INV_VALUE);
    }

  /* Check that the provided ID is valid.  This is not really needed
     but we do it to enforce correct usage by the caller. */
  if (!strncmp (keyidstr, "NKS-NKS3.", 9) )
    ;
  else if (!strncmp (keyidstr, "NKS-DF01.", 9) )
    ;
  else if (!strncmp (keyidstr, "NKS-SIGG.", 9) )
    is_sigg = 1;
  else
    return gpg_error (GPG_ERR_INV_ID);
  keyidstr += 9;

  rc = switch_application (app, is_sigg);
  if (rc)
    return rc;

  if (is_sigg && app->app_local->sigg_is_msig)
    {
      log_info ("mass signature cards are not allowed\n");
      return gpg_error (GPG_ERR_NOT_SUPPORTED);
    }

  if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
      || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
      || keyidstr[4])
    return gpg_error (GPG_ERR_INV_ID);
  fid = xtoi_4 (keyidstr);
  for (i=0; filelist[i].fid; i++)
    if (filelist[i].iskeypair && filelist[i].fid == fid)
      break;
  if (!filelist[i].fid)
    return gpg_error (GPG_ERR_NOT_FOUND);
  if (!filelist[i].issignkey)
    return gpg_error (GPG_ERR_INV_ID);
  kid = filelist[i].kid;

  /* Prepare the DER object from INDATA.  */
  if (app->app_local->nks_version > 2 && (indatalen == 35
                                          || indatalen == 47
                                          || indatalen == 51
                                          || indatalen == 67
                                          || indatalen == 83))
    {
      /* The caller send data matching the length of the ASN.1 encoded
         hash for SHA-{1,224,256,384,512}.  Assume that is okay.  */
      assert (indatalen <= sizeof data);
      memcpy (data, indata, indatalen);
      datalen = indatalen;
    }
  else if (indatalen == 35)
    {
      /* Alright, the caller was so kind to send us an already
         prepared DER object.  This is for TCOS 2. */
      if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha1_prefix, 15))
        ;
      else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata,rmd160_prefix,15))
        ;
      else
        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
      memcpy (data, indata, indatalen);
      datalen = 35;
    }
  else if (indatalen == 20)
    {
      if (hashalgo == GCRY_MD_SHA1)
        memcpy (data, sha1_prefix, 15);
      else if (hashalgo == GCRY_MD_RMD160)
        memcpy (data, rmd160_prefix, 15);
      else
        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
      memcpy (data+15, indata, indatalen);
      datalen = 35;
    }
  else
    return gpg_error (GPG_ERR_INV_VALUE);


  /* Send an MSE for PSO:Computer_Signature.  */
  if (app->app_local->nks_version > 2)
    {
      unsigned char mse[6];

      mse[0] = 0x80; /* Algorithm reference.  */
      mse[1] = 1;
      mse[2] = 2;    /* RSA, card does pkcs#1 v1.5 padding, no ASN.1 check.  */
      mse[3] = 0x84; /* Private key reference.  */
      mse[4] = 1;
      mse[5] = kid;
      rc = iso7816_manage_security_env (app->slot, 0x41, 0xB6,
                                        mse, sizeof mse);
    }
  /* Verify using PW1.CH.  */
  if (!rc)
    rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
  /* Compute the signature.  */
  if (!rc)
    rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0,
                             outdata, outdatalen);
  return rc;
}