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 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  };
  static unsigned char sha1_prefix[15] =   /* (1.3.14.3.2.26) */
    { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
      0x02, 0x1a, 0x05, 0x00, 0x04, 0x14  };
  static unsigned char sha224_prefix[19] = /* (2.16.840.1.101.3.4.2.4) */
    { 0x30, 0x2D, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
      0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
      0x1C  };
  static unsigned char sha256_prefix[19] = /* (2.16.840.1.101.3.4.2.1) */
    { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
      0x00, 0x04, 0x20  };
  static unsigned char sha384_prefix[19] = /* (2.16.840.1.101.3.4.2.2) */
    { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
      0x00, 0x04, 0x30  };
  static unsigned char sha512_prefix[19] = /* (2.16.840.1.101.3.4.2.3) */
    { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
      0x00, 0x04, 0x40  };
  int rc;
  unsigned char data[19+64];
  size_t datalen;
  unsigned char tmp_sn[20]; /* Actually 16 bytes but also for the fpr. */
  const char *s;
  int n;
  const char *fpr = NULL;
  unsigned long sigcount;
  int use_auth = 0;
  int exmode, le_value;

  if (!keyidstr || !*keyidstr)
    return gpg_error (GPG_ERR_INV_VALUE);

  /* Strip off known prefixes.  */
#define X(a,b,c,d) \
  if (hashalgo == GCRY_MD_ ## a                               \
      && (d)                                                  \
      && indatalen == sizeof b ## _prefix + (c)               \
      && !memcmp (indata, b ## _prefix, sizeof b ## _prefix)) \
    {                                                         \
      indata = (const char*)indata + sizeof b ## _prefix;     \
      indatalen -= sizeof b ## _prefix;                       \
    }

  if (indatalen == 20)
    ;  /* Assume a plain SHA-1 or RMD160 digest has been given.  */
  else X(SHA1,   sha1,   20, 1)
  else X(RMD160, rmd160, 20, 1)
  else X(SHA224, sha224, 28, app->app_local->extcap.is_v2)
  else X(SHA256, sha256, 32, app->app_local->extcap.is_v2)
  else X(SHA384, sha384, 48, app->app_local->extcap.is_v2)
  else X(SHA512, sha512, 64, app->app_local->extcap.is_v2)
  else if ((indatalen == 28 || indatalen == 32
            || indatalen == 48 || indatalen ==64)
           && app->app_local->extcap.is_v2)
    ;  /* Assume a plain SHA-3 digest has been given.  */
  else
    {
      log_error (_("card does not support digest algorithm %s\n"),
                 gcry_md_algo_name (hashalgo));
      /* Or the supplied digest length does not match an algorithm.  */
      return gpg_error (GPG_ERR_INV_VALUE);
    }
#undef X

  /* Check whether an OpenPGP card of any version has been requested. */
  if (!strcmp (keyidstr, "OPENPGP.1"))
    ;
  else if (!strcmp (keyidstr, "OPENPGP.3"))
    use_auth = 1;
  else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
    return gpg_error (GPG_ERR_INV_ID);
  else
    {
      for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
	;
      if (n != 32)
	return gpg_error (GPG_ERR_INV_ID);
      else if (!*s)
	; /* no fingerprint given: we allow this for now. */
      else if (*s == '/')
	fpr = s + 1;
      else
	return gpg_error (GPG_ERR_INV_ID);

      for (s=keyidstr, n=0; n < 16; s += 2, n++)
	tmp_sn[n] = xtoi_2 (s);

      if (app->serialnolen != 16)
	return gpg_error (GPG_ERR_INV_CARD);
      if (memcmp (app->serialno, tmp_sn, 16))
	return gpg_error (GPG_ERR_WRONG_CARD);
    }

  /* If a fingerprint has been specified check it against the one on
     the card.  This is allows for a meaningful error message in case
     the key on the card has been replaced but the shadow information
     known to gpg was not updated.  If there is no fingerprint, gpg
     will detect a bogus signature anyway due to the
     verify-after-signing feature. */
  rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0;
  if (rc)
    return rc;

  /* Concatenate prefix and digest.  */
#define X(a,b,d) \
  if (hashalgo == GCRY_MD_ ## a && (d) )                      \
    {                                                         \
      datalen = sizeof b ## _prefix + indatalen;              \
      assert (datalen <= sizeof data);                        \
      memcpy (data, b ## _prefix, sizeof b ## _prefix);       \
      memcpy (data + sizeof b ## _prefix, indata, indatalen); \
    }

  if (use_auth
      || app->app_local->keyattr[use_auth? 2: 0].key_type == KEY_TYPE_RSA)
    {
      X(SHA1,   sha1,   1)
      else X(RMD160, rmd160, 1)
      else X(SHA224, sha224, app->app_local->extcap.is_v2)
      else X(SHA256, sha256, app->app_local->extcap.is_v2)
      else X(SHA384, sha384, app->app_local->extcap.is_v2)
      else X(SHA512, sha512, app->app_local->extcap.is_v2)
      else
        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
    }
  else
    {
      datalen = indatalen;
      memcpy (data, indata, indatalen);
    }
#undef X

  /* Redirect to the AUTH command if asked to. */
  if (use_auth)
    {
      return do_auth (app, "OPENPGP.3", pincb, pincb_arg,
                      data, datalen,
                      outdata, outdatalen);
    }

  /* Show the number of signature done using this key.  */
  sigcount = get_sig_counter (app);
  log_info (_("signatures created so far: %lu\n"), sigcount);

  /* Check CHV if needed.  */
  if (!app->did_chv1 || app->force_chv1 )
    {
      char *pinvalue;

      rc = verify_a_chv (app, pincb, pincb_arg, 1, sigcount, &pinvalue);
      if (rc)
        return rc;

      app->did_chv1 = 1;

      /* For cards with versions < 2 we want to keep CHV1 and CHV2 in
         sync, thus we verify CHV2 here using the given PIN.  Cards
         with version2 to not have the need for a separate CHV2 and
         internally use just one.  Obviously we can't do that if the
         pinpad has been used. */
      if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
        {
          rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
          if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
            rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
          if (rc)
            {
              log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
              xfree (pinvalue);
              flush_cache_after_error (app);
              return rc;
            }
          app->did_chv2 = 1;
        }
      xfree (pinvalue);
    }


  if (app->app_local->cardcap.ext_lc_le)
    {
      exmode = 1;    /* Use extended length.  */
      le_value = app->app_local->extcap.max_rsp_data;
    }
  else
    {
      exmode = 0;
      le_value = 0;
    }
  rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
                           outdata, outdatalen);
  return rc;
}