do_decipher (app_t app, const char *keyidstr,
             gpg_error_t (*pincb)(void*, const char *, char **),
             void *pincb_arg,
             const void *indata, size_t indatalen,
             unsigned char **outdata, size_t *outdatalen,
             unsigned int *r_info)
{
  int rc;
  unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
  const char *s;
  int n;
  const char *fpr = NULL;
  int exmode, le_value;
  unsigned char *fixbuf = NULL;
  int padind = 0;

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

  /* Check whether an OpenPGP card of any version has been requested. */
  if (!strcmp (keyidstr, "OPENPGP.2"))
    ;
  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, the
     decryption won't produce the right plaintext anyway. */
  rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
  if (rc)
    return rc;

  rc = verify_chv2 (app, pincb, pincb_arg);
  if (rc)
    return rc;

  if (app->app_local->keyattr[1].key_type == KEY_TYPE_RSA)
    {
      int fixuplen;

      /* We might encounter a couple of leading zeroes in the
         cryptogram.  Due to internal use of MPIs these leading zeroes
         are stripped.  However the OpenPGP card expects exactly 128
         bytes for the cryptogram (for a 1k key).  Thus we need to fix
         it up.  We do this for up to 16 leading zero bytes; a
         cryptogram with more than this is with a very high
         probability anyway broken.  If a signed conversion was used
         we may also encounter one leading zero followed by the correct
         length.  We fix that as well.  */
      if (indatalen >= (128-16) && indatalen < 128)      /* 1024 bit key.  */
        fixuplen = 128 - indatalen;
      else if (indatalen >= (192-16) && indatalen < 192) /* 1536 bit key.  */
        fixuplen = 192 - indatalen;
      else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key.  */
        fixuplen = 256 - indatalen;
      else if (indatalen >= (384-16) && indatalen < 384) /* 3072 bit key.  */
        fixuplen = 384 - indatalen;
      else if (indatalen >= (512-16) && indatalen < 512) /* 4096 bit key.  */
        fixuplen = 512 - indatalen;
      else if (!*(const char *)indata && (indatalen == 129
                                          || indatalen == 193
                                          || indatalen == 257
                                          || indatalen == 385
                                          || indatalen == 513))
        fixuplen = -1;
      else
        fixuplen = 0;

      if (fixuplen > 0)
        {
          /* While we have to prepend stuff anyway, we can also
             include the padding byte here so that iso1816_decipher
             does not need to do another data mangling.  */
          fixuplen++;

          fixbuf = xtrymalloc (fixuplen + indatalen);
          if (!fixbuf)
            return gpg_error_from_syserror ();

          memset (fixbuf, 0, fixuplen);
          memcpy (fixbuf+fixuplen, indata, indatalen);
          indata = fixbuf;
          indatalen = fixuplen + indatalen;
          padind = -1; /* Already padded.  */
        }
      else if (fixuplen < 0)
        {
          /* We use the extra leading zero as the padding byte.  */
          padind = -1;
        }
    }
  else if (app->app_local->keyattr[1].key_type == KEY_TYPE_ECC)
    padind = -1;
  else
    return gpg_error (GPG_ERR_INV_VALUE);

  if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
    {
      exmode = 1;    /* Extended length w/o a limit.  */
      le_value = app->app_local->extcap.max_rsp_data;
    }
  else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
    {
      exmode = -254; /* Command chaining with max. 254 bytes.  */
      le_value = 0;
    }
  else
    exmode = le_value = 0;

  rc = iso7816_decipher (app->slot, exmode,
                         indata, indatalen, le_value, padind,
                         outdata, outdatalen);
  xfree (fixbuf);

  if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
      && app->app_local->manufacturer == 5
      && app->card_version == 0x0200)
    log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
              " do not work with encryption keys > 2048 bits\n");

  *r_info |= APP_DECIPHER_INFO_NOPAD;

  return rc;
}