print_key_flags(int flags)
{
  if(flags&PUBKEY_USAGE_SIG)
    tty_printf("%s ",_("Sign"));

  if(flags&PUBKEY_USAGE_CERT)
    tty_printf("%s ",_("Certify"));

  if(flags&PUBKEY_USAGE_ENC)
    tty_printf("%s ",_("Encrypt"));

  if(flags&PUBKEY_USAGE_AUTH)
    tty_printf("%s ",_("Authenticate"));
}


iobuf_sockopen (int fd, const char *mode)
{
  iobuf_t a;
#ifdef HAVE_W32_SYSTEM
  sock_filter_ctx_t *scx;
  size_t len;

  a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, IOBUF_BUFFER_SIZE);
  scx = xmalloc (sizeof *scx + 25);
  scx->sock = fd;
  scx->print_only_name = 1;
  sprintf (scx->fname, "[sock %d]", fd);
  a->filter = sock_filter;
  a->filter_ov = scx;
  sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
  sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len);
  if (DBG_IOBUF)
    log_debug ("iobuf-%d.%d: sockopen '%s'\n", a->no, a->subno, scx->fname);
  iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
#else
  a = iobuf_fdopen (fd, mode);
#endif
  return a;
}


unknown_pubkey_warning (int algo)
{
  static byte unknown_pubkey_algos[256];

  /* First check whether the algorithm is usable but not suitable for
     encryption/signing.  */
  if (pubkey_get_npkey (algo))
    {
      if (opt.verbose)
        {
          if (!pubkey_get_nsig (algo))
            log_info ("public key algorithm %s not suitable for %s\n",
                      openpgp_pk_algo_name (algo), "signing");
          if (!pubkey_get_nenc (algo))
            log_info ("public key algorithm %s not suitable for %s\n",
                      openpgp_pk_algo_name (algo), "encryption");
        }
    }
  else
    {
      algo &= 0xff;
      if (!unknown_pubkey_algos[algo])
        {
          if (opt.verbose)
            log_info (_("can't handle public key algorithm %d\n"), algo);
          unknown_pubkey_algos[algo] = 1;
        }
    }
}


iobuf_readbyte (iobuf_t a)
{
  int c;

  if (a->nlimit && a->nbytes >= a->nlimit)
    return -1;			/* forced EOF */

  if (a->d.start < a->d.len)
    {
      c = a->d.buf[a->d.start++];
    }
  else if ((c = underflow (a)) == -1)
    return -1;			/* EOF */

  a->nbytes++;
  return c;
}


generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
{
  gpg_error_t err = 0;
  kbnode_t node;
  PKT_public_key *pri_psk = NULL;
  PKT_public_key *sub_psk = NULL;
  int algo;
  unsigned int use;
  u32 expire;
  unsigned int nbits = 0;
  char *curve = NULL;
  u32 cur_time;
  char *hexgrip = NULL;
  char *serialno = NULL;

  /* Break out the primary key.  */
  node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
  if (!node)
    {
      log_error ("Oops; primary key missing in keyblock!\n");
      err = gpg_error (GPG_ERR_BUG);
      goto leave;
    }
  pri_psk = node->pkt->pkt.public_key;

  cur_time = make_timestamp ();

  if (pri_psk->timestamp > cur_time)
    {
      ulong d = pri_psk->timestamp - cur_time;
      log_info ( d==1 ? _("key has been created %lu second "
                          "in future (time warp or clock problem)\n")
                 : _("key has been created %lu seconds "
                     "in future (time warp or clock problem)\n"), d );
      if (!opt.ignore_time_conflict)
        {
          err = gpg_error (GPG_ERR_TIME_CONFLICT);
          goto leave;
        }
    }

  if (pri_psk->version < 4)
    {
      log_info (_("Note: creating subkeys for v3 keys "
                  "is not OpenPGP compliant\n"));
      err = gpg_error (GPG_ERR_CONFLICT);
      goto leave;
    }

  err = hexkeygrip_from_pk (pri_psk, &hexgrip);
  if (err)
    goto leave;
  if (agent_get_keyinfo (NULL, hexgrip, &serialno))
    {
      tty_printf (_("Secret parts of primary key are not available.\n"));
      goto leave;
    }
  if (serialno)
    tty_printf (_("Secret parts of primary key are stored on-card.\n"));

  xfree (hexgrip);
  hexgrip = NULL;
  algo = ask_algo (ctrl, 1, NULL, &use, &hexgrip);
  assert (algo);

  if (hexgrip)
    nbits = 0;
  else if (algo == PUBKEY_ALGO_ECDSA
           || algo == PUBKEY_ALGO_EDDSA
           || algo == PUBKEY_ALGO_ECDH)
    curve = ask_curve (&algo, 0);
  else
    nbits = ask_keysize (algo, 0);

  expire = ask_expire_interval (0, NULL);
  if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
                                               _("Really create? (y/N) ")))
    {
      err = gpg_error (GPG_ERR_CANCELED);
      goto leave;
    }

  if (hexgrip)
    err = do_create_from_keygrip (ctrl, algo, hexgrip,
                                  keyblock, cur_time, expire, 1);
  else
    err = do_create (algo, nbits, curve,
                     keyblock, cur_time, expire, 1, 0, NULL, NULL);
  if (err)
    goto leave;

  /* Get the pointer to the generated public subkey packet.  */
  for (node = keyblock; node; node = node->next)
    if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
      sub_psk = node->pkt->pkt.public_key;

  /* Write the binding signature.  */
  err = write_keybinding (keyblock, pri_psk, sub_psk, use, cur_time, NULL);
  if (err)
    goto leave;

  write_status_text (STATUS_KEY_CREATED, "S");

 leave:
  xfree (curve);
  xfree (hexgrip);
  xfree (serialno);
  if (err)
    log_error (_("Key generation failed: %s\n"), gpg_strerror (err) );
  return err;
}


print_p2r_iccclock (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
  if (msglen < 10)
    return;
  printf ("  bClockCommand .....: 0x%02x\n", msg[7]);
  print_pr_data (msg, msglen, 8);
}


print_p2r_getparameters (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
  print_pr_data (msg, msglen, 7);
}


print_string (FILE *fp, const byte *p, size_t n, int delim)
{
  for ( ; n; n--, p++ )
    {
      if (*p < 0x20 || (*p >= 0x7f && *p < 0xa0) || *p == delim)
        {
          putc('\\', fp);
          if( *p == '\n' )
            putc('n', fp);
          else if( *p == '\r' )
            putc('r', fp);
          else if( *p == '\f' )
            putc('f', fp);
          else if( *p == '\v' )
            putc('v', fp);
          else if( *p == '\b' )
            putc('b', fp);
          else if( !*p )
            putc('0', fp);
          else
            fprintf(fp, "x%02x", *p );
	}
      else
        putc(*p, fp);
    }
}


buf16_to_u16 (const void *buffer)
{
  const unsigned char *p = buffer;

  return (((u16)p[0] << 8) | p[1]);
}


openpgp_md_algo_name (int algo)
{
  switch (algo)
    {
    case DIGEST_ALGO_MD5:    return "MD5";
    case DIGEST_ALGO_SHA1:   return "SHA1";
    case DIGEST_ALGO_RMD160: return "RIPEMD160";
    case DIGEST_ALGO_SHA256: return "SHA256";
    case DIGEST_ALGO_SHA384: return "SHA384";
    case DIGEST_ALGO_SHA512: return "SHA512";
    case DIGEST_ALGO_SHA224: return "SHA224";
    }
  return "?";
}


sock_filter (void *opaque, int control, iobuf_t chain, byte * buf,
	     size_t * ret_len)
{
  sock_filter_ctx_t *a = opaque;
  size_t size = *ret_len;
  size_t nbytes = 0;
  int rc = 0;

  (void)chain;

  if (control == IOBUFCTRL_UNDERFLOW)
    {
      assert (size);		/* need a buffer */
      if (a->eof_seen)
	{
	  rc = -1;
	  *ret_len = 0;
	}
      else
	{
	  int nread;

	  nread = recv (a->sock, buf, size, 0);
	  if (nread == SOCKET_ERROR)
	    {
	      int ec = (int) WSAGetLastError ();
	      rc = gpg_error_from_errno (ec);
	      log_error ("socket read error: ec=%d\n", ec);
	    }
	  else if (!nread)
	    {
	      a->eof_seen = 1;
	      rc = -1;
	    }
	  else
	    {
	      nbytes = nread;
	    }
	  *ret_len = nbytes;
	}
    }
  else if (control == IOBUFCTRL_FLUSH)
    {
      if (size)
	{
	  byte *p = buf;
	  int n;

	  nbytes = size;
	  do
	    {
	      n = send (a->sock, p, nbytes, 0);
	      if (n == SOCKET_ERROR)
		{
		  int ec = (int) WSAGetLastError ();
		  rc = gpg_error_from_errno (ec);
		  log_error ("socket write error: ec=%d\n", ec);
		  break;
		}
	      p += n;
	      nbytes -= n;
	    }
	  while (nbytes);
	  nbytes = p - buf;
	}
      *ret_len = nbytes;
    }
  else if (control == IOBUFCTRL_INIT)
    {
      a->eof_seen = 0;
      a->keep_open = 0;
      a->no_cache = 0;
    }
  else if (control == IOBUFCTRL_DESC)
    {
      *(char **) buf = "sock_filter";
    }
  else if (control == IOBUFCTRL_FREE)
    {
      if (!a->keep_open)
	closesocket (a->sock);
      xfree (a);		/* we can free our context now */
    }
  return rc;
}


print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
{
  DEBUGOUT_1 ("%s:\n", name);
  if (msglen < 9)
    return;
  DEBUGOUT_1 ("  dwLength ..........: %u\n", convert_le_u32 (msg+1));
  DEBUGOUT_1 ("  bSlot .............: %u\n", msg[5]);
  DEBUGOUT_1 ("  bSeq ..............: %u\n", msg[6]);
  DEBUGOUT_1 ("  bStatus ...........: %u\n", msg[7]);
  if (msg[8])
    DEBUGOUT_1 ("  bError ............: %u\n", msg[8]);
}


keystr_from_pk(PKT_public_key *pk)
{
  keyid_from_pk(pk,NULL);

  return keystr(pk->keyid);
}


ccid_poll (ccid_driver_t handle)
{
  int rc;
  unsigned char msg[10];
  size_t msglen;
  int i, j;

  if (handle->idev)
    {
      rc = usb_bulk_read (handle->idev,
                          handle->ep_intr,
                          (char*)msg, sizeof msg,
                          0 /* ms timeout */ );
      if (rc < 0 && errno == ETIMEDOUT)
        return 0;
    }
  else
    return 0;

  if (rc < 0)
    {
      DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
      return CCID_DRIVER_ERR_CARD_IO_ERROR;
    }

  msglen = rc;
  rc = 0;

  if (msglen < 1)
    {
      DEBUGOUT ("intr-in msg too short\n");
      return CCID_DRIVER_ERR_INV_VALUE;
    }

  if (msg[0] == RDR_to_PC_NotifySlotChange)
    {
      DEBUGOUT ("notify slot change:");
      for (i=1; i < msglen; i++)
        for (j=0; j < 4; j++)
          DEBUGOUT_CONT_3 (" %d:%c%c",
                           (i-1)*4+j,
                           (msg[i] & (1<<(j*2)))? 'p':'-',
                           (msg[i] & (2<<(j*2)))? '*':' ');
      DEBUGOUT_LF ();
    }
  else if (msg[0] == RDR_to_PC_HardwareError)
    {
      DEBUGOUT ("hardware error occured\n");
    }
  else
    {
      DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]);
    }

  return 0;
}


tdbio_read_model(void)
{
  TRUSTREC vr;
  int rc;

  rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
  if( rc )
    log_fatal( _("%s: error reading version record: %s\n"),
	       db_name, gpg_strerror (rc) );
  return vr.r.ver.trust_model;
}


dump_sig_subpkt (int hashed, int type, int critical,
		 const byte * buffer, size_t buflen, size_t length)
{
  const char *p = NULL;
  int i;

  /* The CERT has warning out with explains how to use GNUPG to detect
   * the ARRs - we print our old message here when it is a faked ARR
   * and add an additional notice.  */
  if (type == SIGSUBPKT_ARR && !hashed)
    {
      es_fprintf (listfp,
                  "\tsubpkt %d len %u (additional recipient request)\n"
                  "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
                  "encrypt to this key and thereby reveal the plaintext to "
                  "the owner of this ARR key. Detailed info follows:\n",
                  type, (unsigned) length);
    }

  buffer++;
  length--;

  es_fprintf (listfp, "\t%s%ssubpkt %d len %u (",	/*) */
              critical ? "critical " : "",
              hashed ? "hashed " : "", type, (unsigned) length);
  if (length > buflen)
    {
      es_fprintf (listfp, "too short: buffer is only %u)\n", (unsigned) buflen);
      return;
    }
  switch (type)
    {
    case SIGSUBPKT_SIG_CREATED:
      if (length >= 4)
	es_fprintf (listfp, "sig created %s",
                    strtimestamp (buf32_to_u32 (buffer)));
      break;
    case SIGSUBPKT_SIG_EXPIRE:
      if (length >= 4)
	{
	  if (buf32_to_u32 (buffer))
	    es_fprintf (listfp, "sig expires after %s",
                        strtimevalue (buf32_to_u32 (buffer)));
	  else
	    es_fprintf (listfp, "sig does not expire");
	}
      break;
    case SIGSUBPKT_EXPORTABLE:
      if (length)
	es_fprintf (listfp, "%sexportable", *buffer ? "" : "not ");
      break;
    case SIGSUBPKT_TRUST:
      if (length != 2)
	p = "[invalid trust subpacket]";
      else
	es_fprintf (listfp, "trust signature of depth %d, value %d", buffer[0],
                    buffer[1]);
      break;
    case SIGSUBPKT_REGEXP:
      if (!length)
	p = "[invalid regexp subpacket]";
      else
        {
          es_fprintf (listfp, "regular expression: \"");
          es_write_sanitized (listfp, buffer, length, "\"", NULL);
          p = "\"";
        }
      break;
    case SIGSUBPKT_REVOCABLE:
      if (length)
	es_fprintf (listfp, "%srevocable", *buffer ? "" : "not ");
      break;
    case SIGSUBPKT_KEY_EXPIRE:
      if (length >= 4)
	{
	  if (buf32_to_u32 (buffer))
	    es_fprintf (listfp, "key expires after %s",
                        strtimevalue (buf32_to_u32 (buffer)));
	  else
	    es_fprintf (listfp, "key does not expire");
	}
      break;
    case SIGSUBPKT_PREF_SYM:
      es_fputs ("pref-sym-algos:", listfp);
      for (i = 0; i < length; i++)
	es_fprintf (listfp, " %d", buffer[i]);
      break;
    case SIGSUBPKT_REV_KEY:
      es_fputs ("revocation key: ", listfp);
      if (length < 22)
	p = "[too short]";
      else
	{
	  es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
	  for (i = 2; i < length; i++)
	    es_fprintf (listfp, "%02X", buffer[i]);
	}
      break;
    case SIGSUBPKT_ISSUER:
      if (length >= 8)
	es_fprintf (listfp, "issuer key ID %08lX%08lX",
                    (ulong) buf32_to_u32 (buffer),
                    (ulong) buf32_to_u32 (buffer + 4));
      break;
    case SIGSUBPKT_NOTATION:
      {
	es_fputs ("notation: ", listfp);
	if (length < 8)
	  p = "[too short]";
	else
	  {
	    const byte *s = buffer;
	    size_t n1, n2;

	    n1 = (s[4] << 8) | s[5];
	    n2 = (s[6] << 8) | s[7];
	    s += 8;
	    if (8 + n1 + n2 != length)
	      p = "[error]";
	    else
	      {
		es_write_sanitized (listfp, s, n1, ")", NULL);
		es_putc ('=', listfp);

		if (*buffer & 0x80)
		  es_write_sanitized (listfp, s + n1, n2, ")", NULL);
		else
		  p = "[not human readable]";
	      }
	  }
      }
      break;
    case SIGSUBPKT_PREF_HASH:
      es_fputs ("pref-hash-algos:", listfp);
      for (i = 0; i < length; i++)
	es_fprintf (listfp, " %d", buffer[i]);
      break;
    case SIGSUBPKT_PREF_COMPR:
      es_fputs ("pref-zip-algos:", listfp);
      for (i = 0; i < length; i++)
	es_fprintf (listfp, " %d", buffer[i]);
      break;
    case SIGSUBPKT_KS_FLAGS:
      es_fputs ("key server preferences:", listfp);
      for (i = 0; i < length; i++)
	es_fprintf (listfp, " %02X", buffer[i]);
      break;
    case SIGSUBPKT_PREF_KS:
      es_fputs ("preferred key server: ", listfp);
      es_write_sanitized (listfp, buffer, length, ")", NULL);
      break;
    case SIGSUBPKT_PRIMARY_UID:
      p = "primary user ID";
      break;
    case SIGSUBPKT_POLICY:
      es_fputs ("policy: ", listfp);
      es_write_sanitized (listfp, buffer, length, ")", NULL);
      break;
    case SIGSUBPKT_KEY_FLAGS:
      es_fputs ("key flags:", listfp);
      for (i = 0; i < length; i++)
	es_fprintf (listfp, " %02X", buffer[i]);
      break;
    case SIGSUBPKT_SIGNERS_UID:
      p = "signer's user ID";
      break;
    case SIGSUBPKT_REVOC_REASON:
      if (length)
	{
	  es_fprintf (listfp, "revocation reason 0x%02x (", *buffer);
	  es_write_sanitized (listfp, buffer + 1, length - 1, ")", NULL);
	  p = ")";
	}
      break;
    case SIGSUBPKT_ARR:
      es_fputs ("Big Brother's key (ignored): ", listfp);
      if (length < 22)
	p = "[too short]";
      else
	{
	  es_fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1]);
          if (length > 2)
            es_write_hexstring (listfp, buffer+2, length-2, 0, NULL);
	}
      break;
    case SIGSUBPKT_FEATURES:
      es_fputs ("features:", listfp);
      for (i = 0; i < length; i++)
	es_fprintf (listfp, " %02x", buffer[i]);
      break;
    case SIGSUBPKT_SIGNATURE:
      es_fputs ("signature: ", listfp);
      if (length < 17)
	p = "[too short]";
      else
	es_fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d",
                    buffer[0],
                    buffer[0] == 3 ? buffer[2] : buffer[1],
                    buffer[0] == 3 ? buffer[15] : buffer[2],
                    buffer[0] == 3 ? buffer[16] : buffer[3]);
      break;
    default:
      if (type >= 100 && type <= 110)
	p = "experimental / private subpacket";
      else
	p = "?";
      break;
    }

  es_fprintf (listfp, "%s)\n", p ? p : "");
}


ask_curve (int *algo, int both)
{
  struct {
    const char *name;
    int available;
    int expert_only;
    int fix_curve;
    const char *pretty_name;
  } curves[] = {
#if GPG_USE_EDDSA
    { "Curve25519",      0, 0, 1, "Curve 25519" },
#endif
#if GPG_USE_ECDSA || GPG_USE_ECDH
    { "NIST P-256",      0, 1, 0, },
    { "NIST P-384",      0, 0, 0, },
    { "NIST P-521",      0, 1, 0, },
    { "brainpoolP256r1", 0, 1, 0, "Brainpool P-256" },
    { "brainpoolP384r1", 0, 1, 0, "Brainpool P-384" },
    { "brainpoolP512r1", 0, 1, 0, "Brainpool P-512" },
    { "secp256k1",       0, 1, 0  },
#endif
  };
  int idx;
  char *answer;
  char *result = NULL;
  gcry_sexp_t keyparms;

  tty_printf (_("Please select which elliptic curve you want:\n"));

 again:
  keyparms = NULL;
  for (idx=0; idx < DIM(curves); idx++)
    {
      int rc;

      curves[idx].available = 0;
      if (!opt.expert && curves[idx].expert_only)
        continue;

      /* FIXME: The strcmp below is a temporary hack during
         development.  It shall be removed as soon as we have proper
         Curve25519 support in Libgcrypt.  */
      gcry_sexp_release (keyparms);
      rc = gcry_sexp_build (&keyparms, NULL,
                            "(public-key(ecc(curve %s)))",
                            (!strcmp (curves[idx].name, "Curve25519")
                             ? "Ed25519" : curves[idx].name));
      if (rc)
        continue;
      if (!gcry_pk_get_curve (keyparms, 0, NULL))
        continue;
      if (both && curves[idx].fix_curve)
        {
          /* Both Curve 25519 keys are to be created.  Check that
             Libgcrypt also supports the real Curve25519.  */
          gcry_sexp_release (keyparms);
          rc = gcry_sexp_build (&keyparms, NULL,
                                "(public-key(ecc(curve %s)))",
                                 curves[idx].name);
          if (rc)
            continue;
          if (!gcry_pk_get_curve (keyparms, 0, NULL))
            continue;
        }

      curves[idx].available = 1;
      tty_printf ("   (%d) %s\n", idx + 1,
                  curves[idx].pretty_name?
                  curves[idx].pretty_name:curves[idx].name);
    }
  gcry_sexp_release (keyparms);

  for (;;)
    {
      answer = cpr_get ("keygen.curve", _("Your selection? "));
      cpr_kill_prompt ();
      idx = *answer? atoi (answer) : 1;
      if (*answer && !idx)
        {
          /* See whether the user entered the name of the curve.  */
          for (idx=0; idx < DIM(curves); idx++)
            {
              if (!opt.expert && curves[idx].expert_only)
                continue;
              if (!stricmp (curves[idx].name, answer)
                  || (curves[idx].pretty_name
                      && !stricmp (curves[idx].pretty_name, answer)))
                break;
            }
          if (idx == DIM(curves))
            idx = -1;
        }
      else
        idx--;
      xfree(answer);
      answer = NULL;
      if (idx < 0 || idx >= DIM (curves) || !curves[idx].available)
        tty_printf (_("Invalid selection.\n"));
      else
        {
          if (curves[idx].fix_curve)
            {
              log_info ("WARNING: Curve25519 is not yet part of the"
                        " OpenPGP standard.\n");

              if (!cpr_get_answer_is_yes("experimental_curve.override",
                                         "Use this curve anyway? (y/N) ")  )
                goto again;
            }

          /* If the user selected a signing algorithm and Curve25519
             we need to update the algo and and the curve name.  */
          if ((*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA)
              && curves[idx].fix_curve)
            {
              *algo = PUBKEY_ALGO_EDDSA;
              result = xstrdup ("Ed25519");
            }
          else
            result = xstrdup (curves[idx].name);
          break;
        }
    }

  if (!result)
    result = xstrdup (curves[0].name);

  return result;
}


print_p2r_setparameters (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
  if (msglen < 10)
    return;
  printf ("  bProtocolNum ......: 0x%02x\n", msg[7]);
  print_pr_data (msg, msglen, 8);
}


parse_user_id (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * packet)
{
  byte *p;

  /* Cap the size of a user ID at 2k: a value absurdly large enough
     that there is no sane user ID string (which is printable text
     as of RFC2440bis) that won't fit in it, but yet small enough to
     avoid allocation problems.  A large pktlen may not be
     allocatable, and a very large pktlen could actually cause our
     allocation to wrap around in xmalloc to a small number. */

  if (pktlen > MAX_UID_PACKET_LENGTH)
    {
      log_error ("packet(%d) too large\n", pkttype);
      if (list_mode)
        es_fprintf (listfp, ":user ID packet: [too large]\n");
      iobuf_skip_rest (inp, pktlen, 0);
      return GPG_ERR_INV_PACKET;
    }

  packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + pktlen);
  packet->pkt.user_id->len = pktlen;
  packet->pkt.user_id->ref = 1;

  p = packet->pkt.user_id->name;
  for (; pktlen; pktlen--, p++)
    *p = iobuf_get_noeof (inp);
  *p = 0;

  if (list_mode)
    {
      int n = packet->pkt.user_id->len;
      es_fprintf (listfp, ":user ID packet: \"");
      /* fixme: Hey why don't we replace this with es_write_sanitized?? */
      for (p = packet->pkt.user_id->name; n; p++, n--)
	{
	  if (*p >= ' ' && *p <= 'z')
	    es_putc (*p, listfp);
	  else
	    es_fprintf (listfp, "\\x%02x", *p);
	}
      es_fprintf (listfp, "\"\n");
    }
  return 0;
}


namehash_from_uid (PKT_user_id *uid)
{
  if (!uid->namehash)
    {
      uid->namehash = xmalloc (20);

      if (uid->attrib_data)
	rmd160_hash_buffer (uid->namehash, uid->attrib_data, uid->attrib_len);
      else
	rmd160_hash_buffer (uid->namehash, uid->name, uid->len);
    }

  return uid->namehash;
}


nbits_from_pk (PKT_public_key *pk)
{
    return pubkey_nbits (pk->pubkey_algo, pk->pkey);
}


set_progress_cb_ccid_reader (int slot, gcry_handler_progress_t cb, void *cb_arg)
{
  reader_table_t slotp = reader_table + slot;

  return ccid_set_progress_cb (slotp->ccid.handle, cb, cb_arg);
}


uid_from_string (const char *string)
{
  size_t n;
  PKT_user_id *uid;

  n = strlen (string);
  uid = xmalloc_clear (sizeof *uid + n);
  uid->len = n;
  strcpy (uid->name, string);
  uid->ref = 1;
  return uid;
}


blob_x509_has_grip (KEYBOXBLOB blob, const unsigned char *grip)
{
  int rc;
  const unsigned char *buffer;
  size_t length;
  size_t cert_off, cert_len;
  ksba_reader_t reader = NULL;
  ksba_cert_t cert = NULL;
  ksba_sexp_t p = NULL;
  gcry_sexp_t s_pkey;
  unsigned char array[20];
  unsigned char *rcp;
  size_t n;

  buffer = _keybox_get_blob_image (blob, &length);
  if (length < 40)
    return 0; /* Too short. */
  cert_off = get32 (buffer+8);
  cert_len = get32 (buffer+12);
  if (cert_off+cert_len > length)
    return 0; /* Too short.  */

  rc = ksba_reader_new (&reader);
  if (rc)
    return 0; /* Problem with ksba. */
  rc = ksba_reader_set_mem (reader, buffer+cert_off, cert_len);
  if (rc)
    goto failed;
  rc = ksba_cert_new (&cert);
  if (rc)
    goto failed;
  rc = ksba_cert_read_der (cert, reader);
  if (rc)
    goto failed;
  p = ksba_cert_get_public_key (cert);
  if (!p)
    goto failed;
  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
  if (!n)
    goto failed;
  rc = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n);
  if (rc)
    {
      gcry_sexp_release (s_pkey);
      goto failed;
    }
  rcp = gcry_pk_get_keygrip (s_pkey, array);
  gcry_sexp_release (s_pkey);
  if (!rcp)
    goto failed; /* Can't calculate keygrip. */

  xfree (p);
  ksba_cert_release (cert);
  ksba_reader_release (reader);
  return !memcmp (array, grip, 20);
 failed:
  xfree (p);
  ksba_cert_release (cert);
  ksba_reader_release (reader);
  return 0;
}


send_le (int slot, int class, int ins, int p0, int p1,
         int lc, const char *data, int le,
         unsigned char **retbuf, size_t *retbuflen,
         pininfo_t *pininfo, int extended_mode)
{
#define SHORT_RESULT_BUFFER_SIZE 258
  /* We allocate 8 extra bytes as a safety margin towards a driver bug.  */
  unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10];
  unsigned char *result_buffer = NULL;
  size_t result_buffer_size;
  unsigned char *result;
  size_t resultlen;
  unsigned char short_apdu_buffer[5+256+1];
  unsigned char *apdu_buffer = NULL;
  size_t apdu_buffer_size;
  unsigned char *apdu;
  size_t apdulen;
  int sw;
  long rc; /* We need a long here due to PC/SC. */
  int did_exact_length_hack = 0;
  int use_chaining = 0;
  int use_extended_length = 0;
  int lc_chunk;

  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
    return SW_HOST_NO_DRIVER;

  if (DBG_CARD_IO)
    log_debug ("send apdu: c=%02X i=%02X p1=%02X p2=%02X lc=%d le=%d em=%d\n",
               class, ins, p0, p1, lc, le, extended_mode);

  if (lc != -1 && (lc > 255 || lc < 0))
    {
      /* Data does not fit into an APDU.  What we do now depends on
         the EXTENDED_MODE parameter.  */
      if (!extended_mode)
        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
      else if (extended_mode > 0)
        use_extended_length = 1;
      else if (extended_mode < 0)
        {
          /* Send APDU using chaining mode.  */
          if (lc > 16384)
            return SW_WRONG_LENGTH;   /* Sanity check.  */
          if ((class&0xf0) != 0)
            return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0.  */
          use_chaining = extended_mode == -1? 255 : -extended_mode;
          use_chaining &= 0xff;
        }
      else
        return SW_HOST_INV_VALUE;
    }
  else if (lc == -1 && extended_mode > 0)
    use_extended_length = 1;

  if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
    {
      /* Expected Data does not fit into an APDU.  What we do now
         depends on the EXTENDED_MODE parameter.  Note that a check
         for command chaining does not make sense because we are
         looking at Le.  */
      if (!extended_mode)
        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
      else if (use_extended_length)
        ; /* We are already using extended length.  */
      else if (extended_mode > 0)
        use_extended_length = 1;
      else
        return SW_HOST_INV_VALUE;
    }

  if ((!data && lc != -1) || (data && lc == -1))
    return SW_HOST_INV_VALUE;

  if (use_extended_length)
    {
      if (reader_table[slot].is_t0)
        return SW_HOST_NOT_SUPPORTED;

      /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le.  */
      apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
      apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
      if (!apdu_buffer)
        return SW_HOST_OUT_OF_CORE;
      apdu = apdu_buffer;
    }
  else
    {
      apdu_buffer_size = sizeof short_apdu_buffer;
      apdu = short_apdu_buffer;
    }

  if (use_extended_length && (le > 256 || le < 0))
    {
      result_buffer_size = le < 0? 4096 : le;
      result_buffer = xtrymalloc (result_buffer_size + 10);
      if (!result_buffer)
        {
          xfree (apdu_buffer);
          return SW_HOST_OUT_OF_CORE;
        }
      result = result_buffer;
    }
  else
    {
      result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
      result = short_result_buffer;
    }
#undef SHORT_RESULT_BUFFER_SIZE

  if ((sw = lock_slot (slot)))
    {
      xfree (apdu_buffer);
      xfree (result_buffer);
      return sw;
    }

  do
    {
      if (use_extended_length)
        {
          use_chaining = 0;
          apdulen = 0;
          apdu[apdulen++] = class;
          apdu[apdulen++] = ins;
          apdu[apdulen++] = p0;
          apdu[apdulen++] = p1;
          apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
          if (lc >= 0)
            {
              apdu[apdulen++] = ((lc >> 8) & 0xff);
              apdu[apdulen++] = (lc & 0xff);
              memcpy (apdu+apdulen, data, lc);
              data += lc;
              apdulen += lc;
            }
          if (le != -1)
            {
              apdu[apdulen++] = ((le >> 8) & 0xff);
              apdu[apdulen++] = (le & 0xff);
            }
        }
      else
        {
          apdulen = 0;
          apdu[apdulen] = class;
          if (use_chaining && lc > 255)
            {
              apdu[apdulen] |= 0x10;
              assert (use_chaining < 256);
              lc_chunk = use_chaining;
              lc -= use_chaining;
            }
          else
            {
              use_chaining = 0;
              lc_chunk = lc;
            }
          apdulen++;
          apdu[apdulen++] = ins;
          apdu[apdulen++] = p0;
          apdu[apdulen++] = p1;
          if (lc_chunk != -1)
            {
              apdu[apdulen++] = lc_chunk;
              memcpy (apdu+apdulen, data, lc_chunk);
              data += lc_chunk;
              apdulen += lc_chunk;
              /* T=0 does not allow the use of Lc together with Le;
                 thus disable Le in this case.  */
              if (reader_table[slot].is_t0)
                le = -1;
            }
          if (le != -1 && !use_chaining)
            apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
        }

    exact_length_hack:
      /* As a safeguard don't pass any garbage to the driver.  */
      assert (apdulen <= apdu_buffer_size);
      memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
      resultlen = result_buffer_size;
      rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
      if (rc || resultlen < 2)
        {
          log_info ("apdu_send_simple(%d) failed: %s\n",
                    slot, apdu_strerror (rc));
          unlock_slot (slot);
          xfree (apdu_buffer);
          xfree (result_buffer);
          return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
        }
      sw = (result[resultlen-2] << 8) | result[resultlen-1];
      if (!use_extended_length
          && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
        {
          apdu[apdulen-1] = (sw & 0x00ff);
          did_exact_length_hack = 1;
          goto exact_length_hack;
        }
    }
  while (use_chaining && sw == SW_SUCCESS);

  if (apdu_buffer)
    {
      xfree (apdu_buffer);
      apdu_buffer = NULL;
      apdu_buffer_size = 0;
    }

  /* Store away the returned data but strip the statusword. */
  resultlen -= 2;
  if (DBG_CARD_IO)
    {
      log_debug (" response: sw=%04X  datalen=%d\n",
                 sw, (unsigned int)resultlen);
      if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
        log_printhex ("    dump: ", result, resultlen);
    }

  if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
    {
      if (retbuf)
        {
          *retbuf = xtrymalloc (resultlen? resultlen : 1);
          if (!*retbuf)
            {
              unlock_slot (slot);
              xfree (result_buffer);
              return SW_HOST_OUT_OF_CORE;
            }
          *retbuflen = resultlen;
          memcpy (*retbuf, result, resultlen);
        }
    }
  else if ((sw & 0xff00) == SW_MORE_DATA)
    {
      unsigned char *p = NULL, *tmp;
      size_t bufsize = 4096;

      /* It is likely that we need to return much more data, so we
         start off with a large buffer. */
      if (retbuf)
        {
          *retbuf = p = xtrymalloc (bufsize);
          if (!*retbuf)
            {
              unlock_slot (slot);
              xfree (result_buffer);
              return SW_HOST_OUT_OF_CORE;
            }
          assert (resultlen < bufsize);
          memcpy (p, result, resultlen);
          p += resultlen;
        }

      do
        {
          int len = (sw & 0x00ff);

          if (DBG_CARD_IO)
            log_debug ("apdu_send_simple(%d): %d more bytes available\n",
                       slot, len);
          apdu_buffer_size = sizeof short_apdu_buffer;
          apdu = short_apdu_buffer;
          apdulen = 0;
          apdu[apdulen++] = class;
          apdu[apdulen++] = 0xC0;
          apdu[apdulen++] = 0;
          apdu[apdulen++] = 0;
          apdu[apdulen++] = len;
          assert (apdulen <= apdu_buffer_size);
          memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
          resultlen = result_buffer_size;
          rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
          if (rc || resultlen < 2)
            {
              log_error ("apdu_send_simple(%d) for get response failed: %s\n",
                         slot, apdu_strerror (rc));
              unlock_slot (slot);
              xfree (result_buffer);
              return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
            }
          sw = (result[resultlen-2] << 8) | result[resultlen-1];
          resultlen -= 2;
          if (DBG_CARD_IO)
            {
              log_debug ("     more: sw=%04X  datalen=%d\n",
                         sw, (unsigned int)resultlen);
              if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
                log_printhex ("     dump: ", result, resultlen);
            }

          if ((sw & 0xff00) == SW_MORE_DATA
              || sw == SW_SUCCESS
              || sw == SW_EOF_REACHED )
            {
              if (retbuf && resultlen)
                {
                  if (p - *retbuf + resultlen > bufsize)
                    {
                      bufsize += resultlen > 4096? resultlen: 4096;
                      tmp = xtryrealloc (*retbuf, bufsize);
                      if (!tmp)
                        {
                          unlock_slot (slot);
                          xfree (result_buffer);
                          return SW_HOST_OUT_OF_CORE;
                        }
                      p = tmp + (p - *retbuf);
                      *retbuf = tmp;
                    }
                  memcpy (p, result, resultlen);
                  p += resultlen;
                }
            }
          else
            log_info ("apdu_send_simple(%d) "
                      "got unexpected status %04X from get response\n",
                      slot, sw);
        }
      while ((sw & 0xff00) == SW_MORE_DATA);

      if (retbuf)
        {
          *retbuflen = p - *retbuf;
          tmp = xtryrealloc (*retbuf, *retbuflen);
          if (tmp)
            *retbuf = tmp;
        }
    }

  unlock_slot (slot);
  xfree (result_buffer);

  if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
    log_printhex ("      dump: ", *retbuf, *retbuflen);

  return sw;
}


apdu_disconnect (int slot)
{
  int sw;

  if (DBG_READER)
    log_debug ("enter: apdu_disconnect: slot=%d\n", slot);

  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
    {
      if (DBG_READER)
        log_debug ("leave: apdu_disconnect => SW_HOST_NO_DRIVER\n");
      return SW_HOST_NO_DRIVER;
    }

  if (reader_table[slot].disconnect_card)
    {
      sw = lock_slot (slot);
      if (!sw)
        {
          sw = reader_table[slot].disconnect_card (slot);
          unlock_slot (slot);
        }
    }
  else
    sw = 0;

  if (DBG_READER)
    log_debug ("leave: apdu_disconnect => sw=0x%x\n", sw);
  return sw;
}


get_public_key (app_t app, int keyno)
{
  gpg_error_t err = 0;
  unsigned char *buffer;
  const unsigned char *keydata, *m, *e;
  size_t buflen, keydatalen;
  size_t mlen = 0;
  size_t elen = 0;
  unsigned char *mbuf = NULL;
  unsigned char *ebuf = NULL;
  char *keybuf = NULL;
  gcry_sexp_t s_pkey;
  size_t len;

  if (keyno < 1 || keyno > 3)
    return gpg_error (GPG_ERR_INV_ID);
  keyno--;

  /* Already cached? */
  if (app->app_local->pk[keyno].read_done)
    return 0;

  xfree (app->app_local->pk[keyno].key);
  app->app_local->pk[keyno].key = NULL;
  app->app_local->pk[keyno].keylen = 0;

  m = e = NULL; /* (avoid cc warning) */

  if (app->card_version > 0x0100)
    {
      int exmode, le_value;

      /* We may simply read the public key out of these cards.  */
      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 = 256; /* Use legacy value. */
        }

      err = iso7816_read_public_key
        (app->slot, exmode,
         (const unsigned char*)(keyno == 0? "\xB6" :
                                keyno == 1? "\xB8" : "\xA4"), 2,
         le_value,
         &buffer, &buflen);
      if (err)
        {
          log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
          goto leave;
        }

      keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
      if (!keydata)
        {
          err = gpg_error (GPG_ERR_CARD);
          log_error (_("response does not contain the public key data\n"));
          goto leave;
        }

      if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
        {
          m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
          if (!m)
            {
              err = gpg_error (GPG_ERR_CARD);
              log_error (_("response does not contain the RSA modulus\n"));
              goto leave;
            }

          e = find_tlv (keydata, keydatalen, 0x0082, &elen);
          if (!e)
            {
              err = gpg_error (GPG_ERR_CARD);
              log_error (_("response does not contain the RSA public exponent\n"));
              goto leave;
            }
        }
      else
        {
          m = find_tlv (keydata, keydatalen, 0x0086, &mlen);
          if (!m)
            {
              err = gpg_error (GPG_ERR_CARD);
              log_error (_("response does not contain the EC public point\n"));
              goto leave;
            }
        }
    }
  else
    {
      /* Due to a design problem in v1.0 cards we can't get the public
         key out of these cards without doing a verify on CHV3.
         Clearly that is not an option and thus we try to locate the
         key using an external helper.

	 The helper we use here is gpg itself, which should know about
	 the key in any case.  */

      char fpr[41];
      char *hexkeyid;
      char *command = NULL;
      FILE *fp;
      int ret;

      buffer = NULL; /* We don't need buffer.  */

      err = retrieve_fpr_from_card (app, keyno, fpr);
      if (err)
	{
	  log_error ("error while retrieving fpr from card: %s\n",
		     gpg_strerror (err));
	  goto leave;
	}
      hexkeyid = fpr + 24;

      ret = gpgrt_asprintf
        (&command, "gpg --list-keys --with-colons --with-key-data '%s'", fpr);
      if (ret < 0)
	{
	  err = gpg_error_from_syserror ();
	  goto leave;
	}

      fp = popen (command, "r");
      xfree (command);
      if (!fp)
	{
	  err = gpg_error_from_syserror ();
	  log_error ("running gpg failed: %s\n", gpg_strerror (err));
	  goto leave;
	}

      err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
      pclose (fp);
      if (err)
	{
	  log_error ("error while retrieving key material through pipe: %s\n",
                     gpg_strerror (err));
	  goto leave;
	}
    }

  mbuf = xtrymalloc ( mlen + 1);
  if (!mbuf)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  /* Prepend numbers with a 0 if needed.  */
  if (app->app_local->keyattr[keyno].key_type != KEY_TYPE_EDDSA
      && mlen && (*m & 0x80))
    {
      *mbuf = 0;
      memcpy (mbuf+1, m, mlen);
      mlen++;
    }
  else
    memcpy (mbuf, m, mlen);

  ebuf = xtrymalloc ( elen + 1);
  if (!ebuf)
    {
      err = gpg_error_from_syserror ();
      goto leave;
    }
  /* Prepend numbers with a 0 if needed.  */
  if (elen && (*e & 0x80))
    {
      *ebuf = 0;
      memcpy (ebuf+1, e, elen);
      elen++;
    }
  else
    memcpy (ebuf, e, elen);

  if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_RSA)
    {
      err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%b)(e%b)))",
                             mlen, mbuf, elen, ebuf);
      if (err)
        goto leave;

      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
      keybuf = xtrymalloc (len);
      if (!keybuf)
        {
          gcry_sexp_release (s_pkey);
          err = gpg_error_from_syserror ();
          goto leave;
        }
      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
      gcry_sexp_release (s_pkey);
    }
  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_ECC)
    {
      const char *curve_name
        = get_curve_name (app->app_local->keyattr[keyno].ecc.curve);

      err = gcry_sexp_build (&s_pkey, NULL,
                             "(public-key(ecc(curve%s)(q%b)))",
                             curve_name, mlen, mbuf);
      if (err)
        goto leave;

      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);

      keybuf = xtrymalloc (len);
      if (!keybuf)
        {
          gcry_sexp_release (s_pkey);
          err = gpg_error_from_syserror ();
          goto leave;
        }
      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
      gcry_sexp_release (s_pkey);
    }
  else if (app->app_local->keyattr[keyno].key_type == KEY_TYPE_EDDSA)
    {
      const char *curve_name
        = get_curve_name (app->app_local->keyattr[keyno].eddsa.curve);

      err = gcry_sexp_build (&s_pkey, NULL,
                             "(public-key(ecc(curve%s)(flags eddsa)(q%b)))",
                             curve_name, mlen, mbuf);
      if (err)
        goto leave;

      len = gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);

      keybuf = xtrymalloc (len);
      if (!keybuf)
        {
          gcry_sexp_release (s_pkey);
          err = gpg_error_from_syserror ();
          goto leave;
        }
      gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, keybuf, len);
      gcry_sexp_release (s_pkey);
    }
  else
    {
      err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
      goto leave;
    }

  app->app_local->pk[keyno].key = (unsigned char*)keybuf;
  app->app_local->pk[keyno].keylen = len - 1; /* Decrement for trailing '\0' */

 leave:
  /* Set a flag to indicate that we tried to read the key.  */
  app->app_local->pk[keyno].read_done = 1;

  xfree (buffer);
  xfree (mbuf);
  xfree (ebuf);
  return 0;
}


print_data (const unsigned char *data, size_t length)
{
  if (length >= 2)
    {
      fprintf (stderr, "operation status: %02X%02X\n",
               data[length-2], data[length-1]);
      length -= 2;
    }
  if (length)
    {
        fputs ("   returned data:", stderr);
        for (; length; length--, data++)
          fprintf (stderr, " %02X", *data);
        putc ('\n', stderr);
    }
}


gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high)
{
  unsigned char digest[20];

  gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL);
  if (r_high)
    *r_high = buf32_to_ulong (digest+12);
  return buf32_to_ulong (digest + 16);
}


ct_activate_card (int slot)
{
  int rc;
  unsigned char dad[1], sad[1], cmd[11], buf[256];
  unsigned short buflen;

  /* Check whether card has been inserted. */
  dad[0] = 1;     /* Destination address: CT. */
  sad[0] = 2;     /* Source address: Host. */

  cmd[0] = 0x20;  /* Class byte. */
  cmd[1] = 0x13;  /* Request status. */
  cmd[2] = 0x00;  /* From kernel. */
  cmd[3] = 0x80;  /* Return card's DO. */
  cmd[4] = 0x00;

  buflen = DIM(buf);

  rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
  if (rc || buflen < 2 || buf[buflen-2] != 0x90)
    {
      log_error ("ct_activate_card: can't get status of reader %d: %s\n",
                 slot, ct_error_string (rc));
      return SW_HOST_CARD_IO_ERROR;
    }

  /* Connected, now activate the card. */
  dad[0] = 1;    /* Destination address: CT. */
  sad[0] = 2;    /* Source address: Host. */

  cmd[0] = 0x20;  /* Class byte. */
  cmd[1] = 0x12;  /* Request ICC. */
  cmd[2] = 0x01;  /* From first interface. */
  cmd[3] = 0x01;  /* Return card's ATR. */
  cmd[4] = 0x00;

  buflen = DIM(buf);

  rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
  if (rc || buflen < 2 || buf[buflen-2] != 0x90)
    {
      log_error ("ct_activate_card(%d): activation failed: %s\n",
                 slot, ct_error_string (rc));
      if (!rc)
        log_printhex ("  received data:", buf, buflen);
      return SW_HOST_CARD_IO_ERROR;
    }

  /* Store the type and the ATR. */
  if (buflen - 2 > DIM (reader_table[0].atr))
    {
      log_error ("ct_activate_card(%d): ATR too long\n", slot);
      return SW_HOST_CARD_IO_ERROR;
    }

  reader_table[slot].status = buf[buflen - 1];
  memcpy (reader_table[slot].atr, buf, buflen - 2);
  reader_table[slot].atrlen = buflen - 2;
  return 0;
}


close_pcsc_reader_wrapped (int slot)
{
  long err;
  reader_table_t slotp;
  size_t len;
  int i;
  unsigned char msgbuf[9];

  slotp = reader_table + slot;

  if (slotp->pcsc.req_fd == -1
      || slotp->pcsc.rsp_fd == -1
      || slotp->pcsc.pid == (pid_t)(-1) )
    {
      log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
      return 0;
    }

  msgbuf[0] = 0x02; /* CLOSE command. */
  len = 0;
  msgbuf[1] = (len >> 24);
  msgbuf[2] = (len >> 16);
  msgbuf[3] = (len >>  8);
  msgbuf[4] = (len      );
  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
    {
      log_error ("error sending PC/SC CLOSE request: %s\n",
                 strerror (errno));
      goto command_failed;
    }

  /* Read the response. */
  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
    {
      log_error ("error receiving PC/SC CLOSE response: %s\n",
                 i? strerror (errno) : "premature EOF");
      goto command_failed;
    }
  len = buf32_to_size_t (msgbuf+1);
  if (msgbuf[0] != 0x81 || len < 4)
    {
      log_error ("invalid response header from PC/SC received\n");
      goto command_failed;
    }
  len -= 4; /* Already read the error code. */
  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
  if (err)
    log_error ("pcsc_close failed: %s (0x%lx)\n",
               pcsc_error_string (err), err);

  /* We will close the wrapper in any case - errors are merely
     informational. */

 command_failed:
  close (slotp->pcsc.req_fd);
  close (slotp->pcsc.rsp_fd);
  slotp->pcsc.req_fd = -1;
  slotp->pcsc.rsp_fd = -1;
  if (slotp->pcsc.pid != -1)
    kill (slotp->pcsc.pid, SIGTERM);
  slotp->pcsc.pid = (pid_t)(-1);
  slotp->used = 0;
  return 0;
}


abort_cmd (ccid_driver_t handle, int seqno)
{
  int rc;
  char dummybuf[8];
  unsigned char msg[100];
  size_t msglen;

  if (!handle->idev)
    {
      /* I don't know how to send an abort to non-USB devices.  */
      rc = CCID_DRIVER_ERR_NOT_SUPPORTED;
    }

  seqno &= 0xff;
  DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno);
  /* Send the abort command to the control pipe.  Note that we don't
     need to keep track of sent abort commands because there should
     never be another thread using the same slot concurrently.  */
  rc = usb_control_msg (handle->idev,
                        0x21,/* bmRequestType: host-to-device,
                                class specific, to interface.  */
                        1,   /* ABORT */
                        (seqno << 8 | 0 /* slot */),
                        handle->ifc_no,
                        dummybuf, 0,
                        1000 /* ms timeout */);
  if (rc < 0)
    {
      DEBUGOUT_1 ("usb_control_msg error: %s\n", strerror (errno));
      return CCID_DRIVER_ERR_CARD_IO_ERROR;
    }

  /* Now send the abort command to the bulk out pipe using the same
     SEQNO and SLOT.  Do this in a loop to so that all seqno are
     tried.  */
  seqno--;  /* Adjust for next increment.  */
  do
    {
      seqno++;
      msg[0] = PC_to_RDR_Abort;
      msg[5] = 0; /* slot */
      msg[6] = seqno;
      msg[7] = 0; /* RFU */
      msg[8] = 0; /* RFU */
      msg[9] = 0; /* RFU */
      msglen = 10;
      set_msg_len (msg, 0);

      rc = usb_bulk_write (handle->idev,
                           handle->ep_bulk_out,
                           (char*)msg, msglen,
                           5000 /* ms timeout */);
      if (rc == msglen)
        rc = 0;
      else if (rc == -1)
        DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n",
                    strerror (errno));
      else
        DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);

      if (rc)
        return rc;

      rc = usb_bulk_read (handle->idev,
                          handle->ep_bulk_in,
                          (char*)msg, sizeof msg,
                          5000 /*ms timeout*/);
      if (rc < 0)
        {
          DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n",
                      strerror (errno));
          return CCID_DRIVER_ERR_CARD_IO_ERROR;
        }
      msglen = rc;

      if (msglen < 10)
        {
          DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n",
                      (unsigned int)msglen);
          return CCID_DRIVER_ERR_INV_VALUE;
        }
      if (msg[5] != 0)
        {
          DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
          return CCID_DRIVER_ERR_INV_VALUE;
        }

      DEBUGOUT_3 ("status: %02X  error: %02X  octet[9]: %02X\n",
                  msg[7], msg[8], msg[9]);
      if (CCID_COMMAND_FAILED (msg))
        print_command_failed (msg);
    }
  while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);

  handle->seqno = ((seqno + 1) & 0xff);
  DEBUGOUT ("sending abort sequence succeeded\n");

  return 0;
}


b64enc_finish (struct b64state *state)
{
  gpg_error_t err = 0;
  unsigned char radbuf[4];
  int idx, quad_count;
  char tmp[4];

  if (state->lasterr)
    return state->lasterr;

  if (!(state->flags & B64ENC_DID_HEADER))
    goto cleanup;

  /* Flush the base64 encoding */
  idx = state->idx;
  quad_count = state->quad_count;
  assert (idx < 4);
  memcpy (radbuf, state->radbuf, idx);

  if (idx)
    {
      tmp[0] = bintoasc[(*radbuf>>2)&077];
      if (idx == 1)
        {
          tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077];
          tmp[2] = '=';
          tmp[3] = '=';
        }
      else
        {
          tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
          tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077];
          tmp[3] = '=';
        }
      if (state->stream)
        {
          for (idx=0; idx < 4; idx++)
            es_putc (tmp[idx], state->stream);
          idx = 0;
          if (es_ferror (state->stream))
            goto write_error;
        }
      else
        {
          for (idx=0; idx < 4; idx++)
            putc (tmp[idx], state->fp);
          idx = 0;
          if (ferror (state->fp))
            goto write_error;
        }

      if (++quad_count >= (64/4))
        {
          quad_count = 0;
          if (!(state->flags & B64ENC_NO_LINEFEEDS)
              && my_fputs ("\n", state) == EOF)
            goto write_error;
        }
    }

  /* Finish the last line and write the trailer. */
  if (quad_count
      && !(state->flags & B64ENC_NO_LINEFEEDS)
      && my_fputs ("\n", state) == EOF)
    goto write_error;

  if ( (state->flags & B64ENC_USE_PGPCRC) )
    {
      /* Write the CRC.  */
      my_fputs ("=", state);
      radbuf[0] = state->crc >>16;
      radbuf[1] = state->crc >> 8;
      radbuf[2] = state->crc;
      tmp[0] = bintoasc[(*radbuf>>2)&077];
      tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
      tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
      tmp[3] = bintoasc[radbuf[2]&077];
      if (state->stream)
        {
          for (idx=0; idx < 4; idx++)
            es_putc (tmp[idx], state->stream);
          if (es_ferror (state->stream))
            goto write_error;
        }
      else
        {
          for (idx=0; idx < 4; idx++)
            putc (tmp[idx], state->fp);
          if (ferror (state->fp))
            goto write_error;
        }
      if (!(state->flags & B64ENC_NO_LINEFEEDS)
          && my_fputs ("\n", state) == EOF)
        goto write_error;
    }

  if (state->title)
    {
      if ( my_fputs ("-----END ", state) == EOF
           || my_fputs (state->title, state) == EOF
           || my_fputs ("-----\n", state) == EOF)
        goto write_error;
    }

  goto cleanup;

 write_error:
  err = gpg_error_from_syserror ();

 cleanup:
  if (state->title)
    {
      xfree (state->title);
      state->title = NULL;
    }
  state->fp = NULL;
  state->stream = NULL;
  state->lasterr = err;
  return err;
}


start_agent (ctrl_t ctrl, int for_card)
{
  int rc;

  (void)ctrl;  /* Not yet used.  */

  /* Fixme: We need a context for each thread or serialize the access
     to the agent. */
  if (agent_ctx)
    rc = 0;
  else
    {
      rc = start_new_gpg_agent (&agent_ctx,
                                GPG_ERR_SOURCE_DEFAULT,
                                opt.homedir,
                                opt.agent_program,
                                opt.lc_ctype, opt.lc_messages,
                                opt.session_env,
                                opt.autostart, opt.verbose, DBG_ASSUAN,
                                NULL, NULL);
      if (!opt.autostart && gpg_err_code (rc) == GPG_ERR_NO_AGENT)
        {
          static int shown;

          if (!shown)
            {
              shown = 1;
              log_info (_("no gpg-agent running in this session\n"));
            }
        }
      else if (!rc)
        {
          /* Tell the agent that we support Pinentry notifications.
             No error checking so that it will work also with older
             agents.  */
          assuan_transact (agent_ctx, "OPTION allow-pinentry-notify",
                           NULL, NULL, NULL, NULL, NULL, NULL);
          /* Tell the agent about what version we are aware.  This is
             here used to indirectly enable GPG_ERR_FULLY_CANCELED.  */
          assuan_transact (agent_ctx, "OPTION agent-awareness=2.1.0",
                           NULL, NULL, NULL, NULL, NULL, NULL);
          /* Pass on the pinentry mode.  */
          if (opt.pinentry_mode)
            {
              char *tmp = xasprintf ("OPTION pinentry-mode=%s",
                                     str_pinentry_mode (opt.pinentry_mode));
              rc = assuan_transact (agent_ctx, tmp,
                               NULL, NULL, NULL, NULL, NULL, NULL);
              xfree (tmp);
              if (rc)
                log_error ("setting pinentry mode '%s' failed: %s\n",
                           str_pinentry_mode (opt.pinentry_mode),
                           gpg_strerror (rc));
            }

          check_hijacking (agent_ctx);
        }
    }

  if (!rc && for_card && !did_early_card_test)
    {
      /* Request the serial number of the card for an early test.  */
      struct agent_card_info_s info;

      memset (&info, 0, sizeof info);
      rc = assuan_transact (agent_ctx, "SCD SERIALNO openpgp",
                            NULL, NULL, NULL, NULL,
                            learn_status_cb, &info);
      if (rc)
        {
          switch (gpg_err_code (rc))
            {
            case GPG_ERR_NOT_SUPPORTED:
            case GPG_ERR_NO_SCDAEMON:
              write_status_text (STATUS_CARDCTRL, "6");
              break;
            case GPG_ERR_OBJ_TERM_STATE:
              write_status_text (STATUS_CARDCTRL, "7");
              break;
            default:
              write_status_text (STATUS_CARDCTRL, "4");
              log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
              break;
            }
        }

      if (!rc && is_status_enabled () && info.serialno)
        {
          char *buf;

          buf = xasprintf ("3 %s", info.serialno);
          write_status_text (STATUS_CARDCTRL, buf);
          xfree (buf);
        }

      agent_release_card_info (&info);

      if (!rc)
        did_early_card_test = 1;
    }

  return rc;
}


pcsc_get_status_direct (int slot, unsigned int *status)
{
  long err;
  struct pcsc_readerstate_s rdrstates[1];

  memset (rdrstates, 0, sizeof *rdrstates);
  rdrstates[0].reader = reader_table[slot].rdrname;
  rdrstates[0].current_state = PCSC_STATE_UNAWARE;
  err = pcsc_get_status_change (reader_table[slot].pcsc.context,
                                0,
                                rdrstates, 1);
  if (err == PCSC_E_TIMEOUT)
    err = 0; /* Timeout is no error error here. */
  if (err)
    {
      log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
                 pcsc_error_string (err), err);
      return pcsc_error_to_sw (err);
    }

  /*   log_debug  */
  /*     ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
  /*      (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
  /*      (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */

  *status = 0;
  if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
    {
      *status |= APDU_CARD_PRESENT;
      if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
	*status |= APDU_CARD_ACTIVE;
    }
#ifndef HAVE_W32_SYSTEM
  /* We indicate a useful card if it is not in use by another
     application.  This is because we only use exclusive access
     mode.  */
  if ( (*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
       == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE)
       && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
    *status |= APDU_CARD_USABLE;
#else
  /* Some winscard drivers may set EXCLUSIVE and INUSE at the same
     time when we are the only user (SCM SCR335) under Windows.  */
  if ((*status & (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
      == (APDU_CARD_PRESENT|APDU_CARD_ACTIVE))
    *status |= APDU_CARD_USABLE;
#endif

  return 0;
}


retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
{
  gpg_error_t err = 0;
  void *relptr;
  unsigned char *value;
  size_t valuelen;

  assert (keyno >=0 && keyno <= 2);

  relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
  if (relptr && valuelen >= 60)
    bin2hex (value+keyno*20, 20, fpr);
  else
    err = gpg_error (GPG_ERR_NOT_FOUND);
  xfree (relptr);
  return err;
}


add_feature_mdc (PKT_signature *sig,int enabled)
{
    const byte *s;
    size_t n;
    int i;
    char *buf;

    s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n );
    /* Already set or cleared */
    if (s && n &&
	((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01))))
      return;

    if (!s || !n) { /* create a new one */
        n = 1;
        buf = xmalloc_clear (n);
    }
    else {
        buf = xmalloc (n);
        memcpy (buf, s, n);
    }

    if(enabled)
      buf[0] |= 0x01; /* MDC feature */
    else
      buf[0] &= ~0x01;

    /* Are there any bits set? */
    for(i=0;i<n;i++)
      if(buf[i]!=0)
	break;

    if(i==n)
      delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES);
    else
      build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n);

    xfree (buf);
}


count_bits (const unsigned char *a, size_t len)
{
  unsigned int n = len * 8;
  int i;

  for (; len && !*a; len--, a++, n -=8)
    ;
  if (len)
    {
      for (i=7; i && !(*a & (1<<i)); i--)
        n--;
    }
  return n;
}


blob_get_blob_flags (KEYBOXBLOB blob)
{
  const unsigned char *buffer;
  size_t length;

  buffer = _keybox_get_blob_image (blob, &length);
  if (length < 8)
    return 0; /* oops */

  return get16 (buffer + 6);
}


check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
{
  int r;

  if (reader_table[slot].pcsc.pinmin >= 0)
    pininfo->minlen = reader_table[slot].pcsc.pinmin;

  if (reader_table[slot].pcsc.pinmax >= 0)
    pininfo->maxlen = reader_table[slot].pcsc.pinmax;

  if (!pininfo->minlen)
    pininfo->minlen = 1;
  if (!pininfo->maxlen)
    pininfo->maxlen = 15;

  if ((command == ISO7816_VERIFY && reader_table[slot].pcsc.verify_ioctl != 0)
      || (command == ISO7816_CHANGE_REFERENCE_DATA
          && reader_table[slot].pcsc.modify_ioctl != 0))
    r = 0;                       /* Success */
  else
    r = SW_NOT_SUPPORTED;

  if (DBG_CARD_IO)
    log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
               (unsigned int)command, r);

  if (reader_table[slot].pinpad_varlen_supported)
    pininfo->fixedlen = 0;

  return r;
}


calc_header_length( u32 len, int new_ctb )
{
    if( !len )
	return 1; /* only the ctb */

    if( new_ctb ) {
	if( len < 192 )
	    return 2;
	if( len < 8384 )
	    return 3;
	else
	    return 6;
    }
    if( len < 256 )
	return 2;
    if( len < 65536 )
	return 3;

    return 5;
}


print_p2r_mechanical (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
  if (msglen < 10)
    return;
  printf ("  bFunction .........: 0x%02x\n", msg[7]);
  print_pr_data (msg, msglen, 8);
}


reset_pcsc_reader_wrapped (int slot)
{
  long err;
  reader_table_t slotp;
  size_t len;
  int i, n;
  unsigned char msgbuf[9];
  unsigned int dummy_status;
  int sw = SW_HOST_CARD_IO_ERROR;

  slotp = reader_table + slot;

  if (slotp->pcsc.req_fd == -1
      || slotp->pcsc.rsp_fd == -1
      || slotp->pcsc.pid == (pid_t)(-1) )
    {
      log_error ("pcsc_get_status: pcsc-wrapper not running\n");
      return sw;
    }

  msgbuf[0] = 0x05; /* RESET command. */
  len = 0;
  msgbuf[1] = (len >> 24);
  msgbuf[2] = (len >> 16);
  msgbuf[3] = (len >>  8);
  msgbuf[4] = (len      );
  if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
    {
      log_error ("error sending PC/SC RESET request: %s\n",
                 strerror (errno));
      goto command_failed;
    }

  /* Read the response. */
  if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
    {
      log_error ("error receiving PC/SC RESET response: %s\n",
                 i? strerror (errno) : "premature EOF");
      goto command_failed;
    }
  len = buf32_to_size_t (msgbuf+1);
  if (msgbuf[0] != 0x81 || len < 4)
    {
      log_error ("invalid response header from PC/SC received\n");
      goto command_failed;
    }
  len -= 4; /* Already read the error code. */
  if (len > DIM (slotp->atr))
    {
      log_error ("PC/SC returned a too large ATR (len=%lx)\n",
                 (unsigned long)len);
      sw = SW_HOST_GENERAL_ERROR;
      goto command_failed;
    }
  err = PCSC_ERR_MASK (buf32_to_ulong (msgbuf+5));
  if (err)
    {
      log_error ("PC/SC RESET failed: %s (0x%lx)\n",
                 pcsc_error_string (err), err);
      /* If the error code is no smart card, we should not considere
         this a major error and close the wrapper.  */
      sw = pcsc_error_to_sw (err);
      if (err == PCSC_E_NO_SMARTCARD)
        return sw;
      goto command_failed;
    }

  /* The open function may return a zero for the ATR length to
     indicate that no card is present.  */
  n = len;
  if (n)
    {
      if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
        {
          log_error ("error receiving PC/SC RESET response: %s\n",
                     i? strerror (errno) : "premature EOF");
          goto command_failed;
        }
    }
  slotp->atrlen = len;

  /* Read the status so that IS_T0 will be set. */
  pcsc_get_status (slot, &dummy_status);

  return 0;

 command_failed:
  close (slotp->pcsc.req_fd);
  close (slotp->pcsc.rsp_fd);
  slotp->pcsc.req_fd = -1;
  slotp->pcsc.rsp_fd = -1;
  if (slotp->pcsc.pid != -1)
    kill (slotp->pcsc.pid, SIGTERM);
  slotp->pcsc.pid = (pid_t)(-1);
  slotp->used = 0;
  return sw;
}


parse_login_data (app_t app)
{
  unsigned char *buffer, *p;
  size_t buflen, len;
  void *relptr;

  /* Set defaults.  */
  app->app_local->flags.no_sync = 0;
  app->app_local->flags.def_chv2 = 0;
  app->app_local->pinpad.specified = 0;
  app->app_local->pinpad.fixedlen_user = -1;
  app->app_local->pinpad.fixedlen_admin = -1;

  /* Read the DO.  */
  relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
  if (!relptr)
    return; /* Ooops. */
  for (; buflen; buflen--, buffer++)
    if (*buffer == '\n')
      break;
  if (buflen < 2 || buffer[1] != '\x14')
    {
      xfree (relptr);
      return; /* No control sequences.  */
    }

  buflen--;
  buffer++;
  do
    {
      buflen--;
      buffer++;
      if (buflen > 1 && *buffer == 'F' && buffer[1] == '=')
        {
          /* Flags control sequence found.  */
          int lastdig = 0;

          /* For now we are only interested in the last digit, so skip
             any leading digits but bail out on invalid characters. */
          for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
            lastdig = xtoi_1 (p);
          buffer = p;
          buflen = len;
          if (len && !(*p == '\n' || *p == '\x18'))
            goto next;  /* Invalid characters in field.  */
          app->app_local->flags.no_sync = !!(lastdig & 1);
          app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
        }
      else if (buflen > 1 && *buffer == 'P' && buffer[1] == '=')
        {
          /* Pinpad request control sequence found.  */
          buffer += 2;
          buflen -= 2;

          if (buflen)
            {
              if (digitp (buffer))
                {
                  char *q;
                  int n, m;

                  n = strtol (buffer, &q, 10);
                  if (q >= (char *)buffer + buflen
                      || *q == '\x18' || *q == '\n')
                    m = n;
                  else
                    {
                      if (*q++ != ',' || !digitp (q))
                        goto next;
                      m = strtol (q, &q, 10);
                    }

                  if (buflen < ((unsigned char *)q - buffer))
                    break;

                  buflen -= ((unsigned char *)q - buffer);
                  buffer = q;

                  if (buflen && !(*buffer == '\n' || *buffer == '\x18'))
                    goto next;
                  app->app_local->pinpad.specified = 1;
                  app->app_local->pinpad.fixedlen_user = n;
                  app->app_local->pinpad.fixedlen_admin = m;
                }
            }
        }
    next:
      /* Skip to FS (0x18) or LF (\n).  */
      for (; buflen && *buffer != '\x18' && *buffer != '\n'; buflen--)
        buffer++;
    }
  while (buflen && *buffer != '\n');

  xfree (relptr);
}


clean_uid_from_key (kbnode_t keyblock, kbnode_t uidnode, int noisy)
{
  kbnode_t node;
  PKT_user_id *uid = uidnode->pkt->pkt.user_id;
  int deleted = 0;

  assert (keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
  assert (uidnode->pkt->pkttype==PKT_USER_ID);

  /* Skip valid user IDs, compacted user IDs, and non-self-signed user
     IDs if --allow-non-selfsigned-uid is set. */
  if (uid->created
      || uid->flags.compacted
      || (!uid->is_expired && !uid->is_revoked && opt.allow_non_selfsigned_uid))
    return 0;

  for (node=uidnode->next;
       node && node->pkt->pkttype == PKT_SIGNATURE;
      node=node->next)
    {
      if (!node->pkt->pkt.signature->flags.chosen_selfsig)
        {
          delete_kbnode (node);
          deleted = 1;
          uidnode->pkt->pkt.user_id->flags.compacted = 1;
        }
    }

  if (noisy)
    {
      const char *reason;
      char *user = utf8_to_native (uid->name, uid->len, 0);

      if (uid->is_revoked)
	reason = _("revoked");
      else if (uid->is_expired)
	reason = _("expired");
      else
	reason = _("invalid");

      log_info ("compacting user ID \"%s\" on key %s: %s\n",
                user, keystr_from_pk (keyblock->pkt->pkt.public_key),
                reason);

      xfree (user);
    }

  return deleted;
}


apdu_reset (int slot)
{
  int sw;

  if (DBG_READER)
    log_debug ("enter: apdu_reset: slot=%d\n", slot);

  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
    {
      if (DBG_READER)
        log_debug ("leave: apdu_reset => SW_HOST_NO_DRIVER\n");
      return SW_HOST_NO_DRIVER;
    }

  if ((sw = lock_slot (slot)))
    {
      if (DBG_READER)
        log_debug ("leave: apdu_reset => sw=0x%x (lock_slot)\n", sw);
      return sw;
    }

  reader_table[slot].last_status = 0;
  if (reader_table[slot].reset_reader)
    sw = reader_table[slot].reset_reader (slot);

  if (!sw)
    {
      /* If we got to here we know that a card is present
         and usable.  Thus remember this.  */
      reader_table[slot].last_status = (APDU_CARD_USABLE
                                        | APDU_CARD_PRESENT
                                        | APDU_CARD_ACTIVE);
    }

  unlock_slot (slot);
  if (DBG_READER)
    log_debug ("leave: apdu_reset => sw=0x%x\n", sw);
  return sw;
}


copy_all_packets (IOBUF inp, IOBUF out)
{
  PACKET pkt;
  int skip, rc = 0;
  do
    {
      init_packet (&pkt);
    }
  while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0)));
  return rc;
}


get_pubkey_fast (PKT_public_key * pk, u32 * keyid)
{
  int rc = 0;
  KEYDB_HANDLE hd;
  KBNODE keyblock;
  u32 pkid[2];

  assert (pk);
#if MAX_PK_CACHE_ENTRIES
  {
    /* Try to get it from the cache */
    pk_cache_entry_t ce;

    for (ce = pk_cache; ce; ce = ce->next)
      {
	if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1])
	  {
	    if (pk)
	      copy_public_key (pk, ce->pk);
	    return 0;
	  }
      }
  }
#endif

  hd = keydb_new ();
  rc = keydb_search_kid (hd, keyid);
  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
    {
      keydb_release (hd);
      return GPG_ERR_NO_PUBKEY;
    }
  rc = keydb_get_keyblock (hd, &keyblock);
  keydb_release (hd);
  if (rc)
    {
      log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc));
      return GPG_ERR_NO_PUBKEY;
    }

  assert (keyblock && keyblock->pkt
          && (keyblock->pkt->pkttype == PKT_PUBLIC_KEY
              || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY));

  keyid_from_pk (keyblock->pkt->pkt.public_key, pkid);
  if (keyid[0] == pkid[0] && keyid[1] == pkid[1])
    copy_public_key (pk, keyblock->pkt->pkt.public_key);
  else
    rc = GPG_ERR_NO_PUBKEY;

  release_kbnode (keyblock);

  /* Not caching key here since it won't have all of the fields
     properly set. */

  return rc;
}


ccid_transceive_apdu_level (ccid_driver_t handle,
                            const unsigned char *apdu_buf, size_t apdu_buflen,
                            unsigned char *resp, size_t maxresplen,
                            size_t *nresp)
{
  int rc;
  unsigned char send_buffer[10+261+300], recv_buffer[10+261+300];
  const unsigned char *apdu;
  size_t apdulen;
  unsigned char *msg;
  size_t msglen;
  unsigned char seqno;
  int bwi = 4;

  msg = send_buffer;

  apdu = apdu_buf;
  apdulen = apdu_buflen;
  assert (apdulen);

  /* The maximum length for a short APDU T=1 block is 261.  For an
     extended APDU T=1 block the maximum length 65544; however
     extended APDU exchange level is not fully supported yet.  */
  if (apdulen > sizeof (send_buffer) - 10)
    return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */

  msg[0] = PC_to_RDR_XfrBlock;
  msg[5] = 0; /* slot */
  msg[6] = seqno = handle->seqno++;
  msg[7] = bwi; /* bBWI */
  msg[8] = 0; /* RFU */
  msg[9] = 0; /* RFU */
  memcpy (msg+10, apdu, apdulen);
  set_msg_len (msg, apdulen);
  msglen = 10 + apdulen;

  rc = bulk_out (handle, msg, msglen, 0);
  if (rc)
    return rc;

  msg = recv_buffer;
  rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
                RDR_to_PC_DataBlock, seqno, 5000, 0);
  if (rc)
    return rc;

  if (msg[9] == 1)
    {
      size_t total_msglen = msglen;

      while (1)
        {
          unsigned char status;

          msg = recv_buffer + total_msglen;

          msg[0] = PC_to_RDR_XfrBlock;
          msg[5] = 0; /* slot */
          msg[6] = seqno = handle->seqno++;
          msg[7] = bwi; /* bBWI */
          msg[8] = 0x10;                /* Request next data block */
          msg[9] = 0;
          set_msg_len (msg, 0);
          msglen = 10;

          rc = bulk_out (handle, msg, msglen, 0);
          if (rc)
            return rc;

          rc = bulk_in (handle, msg, sizeof recv_buffer - total_msglen, &msglen,
                        RDR_to_PC_DataBlock, seqno, 5000, 0);
          if (rc)
            return rc;
          status = msg[9];
          memmove (msg, msg+10, msglen - 10);
          total_msglen += msglen - 10;
          if (total_msglen >= sizeof recv_buffer)
            return CCID_DRIVER_ERR_OUT_OF_CORE;

          if (status == 0x02)
            break;
        }

      apdu = recv_buffer + 10;
      apdulen = total_msglen - 10;
    }
  else
    {
      apdu = msg + 10;
      apdulen = msglen - 10;
    }

  if (resp)
    {
      if (apdulen > maxresplen)
        {
          DEBUGOUT_2 ("provided buffer too short for received data "
                      "(%u/%u)\n",
                      (unsigned int)apdulen, (unsigned int)maxresplen);
          return CCID_DRIVER_ERR_INV_VALUE;
        }

      memcpy (resp, apdu, apdulen);
      *nresp = apdulen;
    }

  return 0;
}


iobuf_flush (iobuf_t a)
{
  size_t len;
  int rc;

  if (a->directfp)
    return 0;

  if (a->use == 3)
    {				/* increase the temp buffer */
      unsigned char *newbuf;
      size_t newsize = a->d.size + IOBUF_BUFFER_SIZE;

      if (DBG_IOBUF)
	log_debug ("increasing temp iobuf from %lu to %lu\n",
		   (ulong) a->d.size, (ulong) newsize);
      newbuf = xmalloc (newsize);
      memcpy (newbuf, a->d.buf, a->d.len);
      xfree (a->d.buf);
      a->d.buf = newbuf;
      a->d.size = newsize;
      return 0;
    }
  else if (a->use != 2)
    log_bug ("flush on non-output iobuf\n");
  else if (!a->filter)
    log_bug ("iobuf_flush: no filter\n");
  len = a->d.len;
  rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len);
  if (!rc && len != a->d.len)
    {
      log_info ("iobuf_flush did not write all!\n");
      rc = GPG_ERR_INTERNAL;
    }
  else if (rc)
    a->error = rc;
  a->d.len = 0;

  return rc;
}


print_p2r_secure (const unsigned char *msg, size_t msglen)
{
  unsigned int val;

  print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
  if (msglen < 10)
    return;
  DEBUGOUT_1 ("  bBMI ..............: 0x%02x\n", msg[7]);
  val = convert_le_u16 (msg+8);
  DEBUGOUT_2 ("  wLevelParameter ...: 0x%04x%s\n", val,
              val == 1? " (continued)":
              val == 2? " (continues+ends)":
              val == 3? " (continues+continued)":
              val == 16? " (DataBlock-expected)":"");
  print_pr_data (msg, msglen, 10);
}


hash_public_key (gcry_md_hd_t md, PKT_public_key *pk)
{
  unsigned int n = 6;
  unsigned int nn[PUBKEY_MAX_NPKEY];
  byte *pp[PUBKEY_MAX_NPKEY];
  int i;
  unsigned int nbits;
  size_t nbytes;
  int npkey = pubkey_get_npkey (pk->pubkey_algo);

  /* FIXME: We can avoid the extra malloc by calling only the first
     mpi_print here which computes the required length and calling the
     real mpi_print only at the end.  The speed advantage would only be
     for ECC (opaque MPIs) or if we could implement an mpi_print
     variant with a callback handler to do the hashing.  */
  if (npkey==0 && pk->pkey[0]
      && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE))
    {
      pp[0] = gcry_mpi_get_opaque (pk->pkey[0], &nbits);
      nn[0] = (nbits+7)/8;
      n+=nn[0];
    }
  else
    {
      for (i=0; i < npkey; i++ )
        {
          if (!pk->pkey[i])
            {
              /* This case may only happen if the parsing of the MPI
                 failed but the key was anyway created.  May happen
                 during "gpg KEYFILE".  */
              pp[i] = NULL;
              nn[i] = 0;
            }
          else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
            {
              const void *p;

              p = gcry_mpi_get_opaque (pk->pkey[i], &nbits);
              pp[i] = xmalloc ((nbits+7)/8);
              memcpy (pp[i], p, (nbits+7)/8);
              nn[i] = (nbits+7)/8;
              n += nn[i];
            }
          else
            {
              if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0,
                                  &nbytes, pk->pkey[i]))
                BUG ();
              pp[i] = xmalloc (nbytes);
              if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes,
                                  &nbytes, pk->pkey[i]))
                BUG ();
              nn[i] = nbytes;
              n += nn[i];
            }
        }
    }

  gcry_md_putc ( md, 0x99 );     /* ctb */
  /* What does it mean if n is greater than than 0xFFFF ? */
  gcry_md_putc ( md, n >> 8 );   /* 2 byte length header */
  gcry_md_putc ( md, n );
  gcry_md_putc ( md, pk->version );

  gcry_md_putc ( md, pk->timestamp >> 24 );
  gcry_md_putc ( md, pk->timestamp >> 16 );
  gcry_md_putc ( md, pk->timestamp >>  8 );
  gcry_md_putc ( md, pk->timestamp       );

  gcry_md_putc ( md, pk->pubkey_algo );

  if(npkey==0 && pk->pkey[0]
     && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE))
    {
      gcry_md_write (md, pp[0], nn[0]);
    }
  else
    for(i=0; i < npkey; i++ )
      {
	gcry_md_write ( md, pp[i], nn[i] );
	xfree(pp[i]);
      }
}


apdu_get_atr (int slot, size_t *atrlen)
{
  unsigned char *buf;

  if (DBG_READER)
    log_debug ("enter: apdu_get_atr: slot=%d\n", slot);

  if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
    {
      if (DBG_READER)
        log_debug ("leave: apdu_get_atr => NULL (bad slot)\n");
      return NULL;
    }
  if (!reader_table[slot].atrlen)
    {
      if (DBG_READER)
        log_debug ("leave: apdu_get_atr => NULL (no ATR)\n");
      return NULL;
    }

  buf = xtrymalloc (reader_table[slot].atrlen);
  if (!buf)
    {
      if (DBG_READER)
        log_debug ("leave: apdu_get_atr => NULL (out of core)\n");
      return NULL;
    }
  memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
  *atrlen = reader_table[slot].atrlen;
  if (DBG_READER)
    log_debug ("leave: apdu_get_atr => atrlen=%zu\n", *atrlen);
  return buf;
}


check_special_filename (const char *fname)
{
  if (special_names_enabled && fname && *fname == '-' && fname[1] == '&')
    {
      int i;

      fname += 2;
      for (i = 0; digitp (fname+i); i++)
	;
      if (!fname[i])
	return atoi (fname);
    }
  return -1;
}


print_pubkey_algo_note (pubkey_algo_t algo)
{
  if(algo >= 100 && algo <= 110)
    {
      static int warn=0;
      if(!warn)
	{
	  warn=1;
          es_fflush (es_stdout);
	  log_info (_("WARNING: using experimental public key algorithm %s\n"),
		    openpgp_pk_algo_name (algo));
	}
    }
  else if (algo == PUBKEY_ALGO_ELGAMAL)
    {
      es_fflush (es_stdout);
      log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n"));
    }
}


ccid_slot_status (ccid_driver_t handle, int *statusbits)
{
  int rc;
  unsigned char msg[100];
  size_t msglen;
  unsigned char seqno;
  int retries = 0;

 retry:
  msg[0] = PC_to_RDR_GetSlotStatus;
  msg[5] = 0; /* slot */
  msg[6] = seqno = handle->seqno++;
  msg[7] = 0; /* RFU */
  msg[8] = 0; /* RFU */
  msg[9] = 0; /* RFU */
  set_msg_len (msg, 0);

  rc = bulk_out (handle, msg, 10, 1);
  if (rc)
    return rc;
  /* Note that we set the NO_DEBUG flag here, so that the logs won't
     get cluttered up by a ticker function checking for the slot
     status and debugging enabled. */
  rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
                seqno, retries? 1000 : 200, 1);
  if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3)
    {
      if (!retries)
        {
          DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n");
          usb_clear_halt (handle->idev, handle->ep_bulk_in);
          usb_clear_halt (handle->idev, handle->ep_bulk_out);
        }
      else
          DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n");
      retries++;
      goto retry;
    }
  if (rc && rc != CCID_DRIVER_ERR_NO_CARD
      && rc != CCID_DRIVER_ERR_CARD_INACTIVE)
    return rc;
  *statusbits = (msg[7] & 3);

  return 0;
}


print_p2r_escape (const unsigned char *msg, size_t msglen)
{
  if (skip_escape)
    return;
  print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
  print_pr_data (msg, msglen, 7);
}


verify_pin (app_t app, int pwid, const char *desc,
            gpg_error_t (*pincb)(void*, const char *, char **),
            void *pincb_arg)
{
  pininfo_t pininfo;
  int rc;

  if (!desc)
    desc = "PIN";

  memset (&pininfo, 0, sizeof pininfo);
  pininfo.fixedlen = -1;
  pininfo.minlen = 6;
  pininfo.maxlen = 16;

  if (!opt.disable_pinpad
      && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
    {
      rc = pincb (pincb_arg, desc, NULL);
      if (rc)
        {
          log_info (_("PIN callback returned error: %s\n"),
                    gpg_strerror (rc));
          return rc;
        }

      rc = iso7816_verify_kp (app->slot, pwid, &pininfo);
      pincb (pincb_arg, NULL, NULL);  /* Dismiss the prompt. */
    }
  else
    {
      char *pinvalue;

      rc = pincb (pincb_arg, desc, &pinvalue);
      if (rc)
        {
          log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
          return rc;
        }

      rc = basic_pin_checks (pinvalue, pininfo.minlen, pininfo.maxlen);
      if (rc)
        {
          xfree (pinvalue);
          return rc;
        }

      rc = iso7816_verify (app->slot, pwid, pinvalue, strlen (pinvalue));
      xfree (pinvalue);
    }

  if (rc)
    {
      if ( gpg_err_code (rc) == GPG_ERR_USE_CONDITIONS )
        log_error (_("the NullPIN has not yet been changed\n"));
      else
        log_error ("verify PIN failed\n");
      return rc;
    }

  return 0;
}


parse_input (FILE *fp)
{
  char line[2000];
  size_t length;
  unsigned int lineno = 0;

  while (fgets (line, sizeof (line), fp))
    {
      lineno++;
      length = strlen (line);
      if (length && line[length - 1] == '\n')
	line[--length] = 0;
      else
        err ("line number %u too long or last line not terminated", lineno);
      if (length && line[length - 1] == '\r')
	line[--length] = 0;
      if (sniffusb)
        parse_line_sniffusb (line, lineno);
      else
        parse_line (line, lineno);
    }
  flush_data ();
  if (ferror (fp))
    err ("error reading input at line %u: %s", lineno, strerror (errno));
}


rapdu_status_to_sw (int status)
{
  int rc;

  switch (status)
    {
    case RAPDU_STATUS_SUCCESS:  rc = 0; break;

    case RAPDU_STATUS_INVCMD:
    case RAPDU_STATUS_INVPROT:
    case RAPDU_STATUS_INVSEQ:
    case RAPDU_STATUS_INVCOOKIE:
    case RAPDU_STATUS_INVREADER:  rc = SW_HOST_INV_VALUE;  break;

    case RAPDU_STATUS_TIMEOUT:  rc = SW_HOST_CARD_IO_ERROR; break;
    case RAPDU_STATUS_CARDIO:   rc = SW_HOST_CARD_IO_ERROR; break;
    case RAPDU_STATUS_NOCARD:   rc = SW_HOST_NO_CARD; break;
    case RAPDU_STATUS_CARDCHG:  rc = SW_HOST_NO_CARD; break;
    case RAPDU_STATUS_BUSY:     rc = SW_HOST_BUSY; break;
    case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;

    default: rc = SW_HOST_GENERAL_ERROR; break;
    }

  return rc;
}


iobuf_write_temp (iobuf_t a, iobuf_t temp)
{
  while (temp->chain)
    pop_filter (temp, temp->filter, NULL);
  return iobuf_write (a, temp->d.buf, temp->d.len);
}


is_valid_user_id (const char *uid)
{
  if (!uid || !*uid)
    return 0;

  return 1;
}


agent_keywrap_key (ctrl_t ctrl, int forexport, void **r_kek, size_t *r_keklen)
{
  gpg_error_t err;
  membuf_t data;
  size_t len;
  unsigned char *buf;
  char line[ASSUAN_LINELENGTH];
  struct default_inq_parm_s dfltparm;

  memset (&dfltparm, 0, sizeof dfltparm);
  dfltparm.ctrl = ctrl;

  *r_kek = NULL;
  err = start_agent (ctrl, 0);
  if (err)
    return err;
  dfltparm.ctx = agent_ctx;

  snprintf (line, DIM(line)-1, "KEYWRAP_KEY %s",
            forexport? "--export":"--import");

  init_membuf_secure (&data, 64);
  err = assuan_transact (agent_ctx, line,
                         membuf_data_cb, &data,
                         default_inq_cb, &dfltparm,
                         NULL, NULL);
  if (err)
    {
      xfree (get_membuf (&data, &len));
      return err;
    }
  buf = get_membuf (&data, &len);
  if (!buf)
    return gpg_error_from_syserror ();
  *r_kek = buf;
  *r_keklen = len;
  return 0;
}


iobuf_get_fd (iobuf_t a)
{
  if (a->directfp)
    return fileno ( (FILE*)a->directfp );

  for ( ; a; a = a->chain )
    if (!a->chain && a->filter == file_filter)
      {
        file_filter_ctx_t *b = a->filter_ov;
        gnupg_fd_t fp = b->fp;

        return FD2INT (fp);
      }

  return -1;
}


print_chain (iobuf_t a)
{
  if (!DBG_IOBUF)
    return;
  for (; a; a = a->chain)
    {
      size_t dummy_len = 0;
      const char *desc = "[none]";

      if (a->filter)
	a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
		   (byte *) & desc, &dummy_len);

      log_debug ("iobuf chain: %d.%d '%s' filter_eof=%d start=%d len=%d\n",
		 a->no, a->subno, desc?desc:"?", a->filter_eof,
		 (int) a->d.start, (int) a->d.len);
    }
}


ccid_set_debug_level (int level)
{
  int old = debug_level;
  if (level != -1)
    debug_level = level;
  return old;
}


die (const char *format, ...)
{
  va_list arg_ptr;

  fflush (stdout);
  fprintf (stderr, "%s: ", PGM);

  va_start (arg_ptr, format);
  vfprintf (stderr, format, arg_ptr);
  va_end (arg_ptr);
  putc ('\n', stderr);

  exit (1);
}


argsplit(char *string)
{
  char *equals,*arg=NULL;

  equals=strchr(string,'=');
  if(equals)
    {
      char *quote,*space;

      *equals='\0';
      arg=equals+1;

      /* Quoted arg? */
      quote=strchr(arg,'"');
      if(quote)
	{
	  arg=quote+1;

	  quote=strchr(arg,'"');
	  if(quote)
	    *quote='\0';
	}
      else
	{
	  size_t spaces;

	  /* Trim leading spaces off of the arg */
	  spaces=strspn(arg," ");
	  arg+=spaces;
	}

      /* Trim tailing spaces off of the tag */
      space=strchr(string,' ');
      if(space)
	*space='\0';
    }

  return arg;
}


iobuf_get_fname_nonnull (iobuf_t a)
{
  const char *fname;

  fname = iobuf_get_fname (a);
  return fname? fname : "[?]";
}


reset_pcsc_reader_direct (int slot)
{
  int sw;

  sw = disconnect_pcsc_card (slot);
  if (!sw)
    sw = connect_pcsc_card (slot);

  return sw;
}


agent_scd_getattr (const char *name, struct agent_card_info_s *info)
{
  int rc;
  char line[ASSUAN_LINELENGTH];
  struct default_inq_parm_s parm;

  memset (&parm, 0, sizeof parm);

  if (!*name)
    return gpg_error (GPG_ERR_INV_VALUE);

  /* We assume that NAME does not need escaping. */
  if (12 + strlen (name) > DIM(line)-1)
    return gpg_error (GPG_ERR_TOO_LARGE);
  stpcpy (stpcpy (line, "SCD GETATTR "), name);

  rc = start_agent (NULL, 1);
  if (rc)
    return rc;

  parm.ctx = agent_ctx;
  rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm,
                        learn_status_cb, info);

  return rc;
}


print_p2r_mechanical (const unsigned char *msg, size_t msglen)
{
  print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
  if (msglen < 10)
    return;
  DEBUGOUT_1 ("  bFunction .........: 0x%02x\n", msg[7]);
  print_pr_data (msg, msglen, 8);
}


print_r2p_datarate (const unsigned char *msg, size_t msglen)
{
  print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
  if (msglen < 10)
    return;
  if (msglen >= 18)
    {
      printf ("  dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
      printf ("  dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
      print_pr_data (msg, msglen, 18);
    }
  else
    print_pr_data (msg, msglen, 10);
}


convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
                    const char *curve)
{
  gpg_error_t err;
  gcry_sexp_t s_skey = NULL;

  *r_key = NULL;

  switch (pubkey_algo)
    {
    case GCRY_PK_DSA:
      err = gcry_sexp_build (&s_skey, NULL,
                             "(private-key(dsa(p%m)(q%m)(g%m)(y%m)(x%m)))",
                             skey[0], skey[1], skey[2], skey[3], skey[4]);
      break;

    case GCRY_PK_ELG:
    case GCRY_PK_ELG_E:
      err = gcry_sexp_build (&s_skey, NULL,
                             "(private-key(elg(p%m)(g%m)(y%m)(x%m)))",
                             skey[0], skey[1], skey[2], skey[3]);
      break;

    case GCRY_PK_RSA:
    case GCRY_PK_RSA_E:
    case GCRY_PK_RSA_S:
      err = gcry_sexp_build (&s_skey, NULL,
                             "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))",
                             skey[0], skey[1], skey[2], skey[3], skey[4],
                             skey[5]);
      break;

    case GCRY_PK_ECC:
      if (!curve)
        err = gpg_error (GPG_ERR_BAD_SECKEY);
      else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
        {
          /* Do not store the OID as name but the real name and the
             EdDSA flag.  */
          err = gcry_sexp_build (&s_skey, NULL,
                                 "(private-key(ecc(curve%s)(flags eddsa)"
                                 "(q%m)(d%m)))",
                                 "Ed25519", skey[0], skey[1]);
        }
      else
        err = gcry_sexp_build (&s_skey, NULL,
                               "(private-key(ecc(curve%s)(q%m)(d%m)))",
                               curve, skey[0], skey[1]);
      break;

    default:
      err = gpg_error (GPG_ERR_PUBKEY_ALGO);
      break;
    }

  if (!err)
    *r_key = s_skey;
  return err;
}


ccid_shutdown_reader (ccid_driver_t handle)
{
  int rc = 0;
  struct usb_device *dev = NULL;
  usb_dev_handle *idev = NULL;
  unsigned char *ifcdesc_extra = NULL;
  size_t ifcdesc_extra_len;
  int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;

  if (!handle || !handle->rid)
    return CCID_DRIVER_ERR_INV_VALUE;

  do_close_reader (handle);

  if (scan_or_find_devices (-1, handle->rid, NULL, &dev,
                            &ifcdesc_extra, &ifcdesc_extra_len,
                            &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr,
                            &idev, NULL) || !idev)
    {
      DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid);
      return CCID_DRIVER_ERR_NO_READER;
    }

  if (idev)
    {
      handle->idev = idev;
      handle->ifc_no = ifc_no;
      handle->ep_bulk_out = ep_bulk_out;
      handle->ep_bulk_in = ep_bulk_in;
      handle->ep_intr = ep_intr;

      if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len))
        {
          DEBUGOUT ("device not supported\n");
          rc = CCID_DRIVER_ERR_NO_READER;
          goto leave;
        }

      rc = usb_claim_interface (idev, ifc_no);
      if (rc)
        {
          DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
          rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
          goto leave;
        }
    }

 leave:
  free (ifcdesc_extra);
  if (rc)
    {
      if (handle->idev)
        usb_close (handle->idev);
      handle->idev = NULL;
      if (handle->dev_fd != -1)
        close (handle->dev_fd);
      handle->dev_fd = -1;
    }

  return rc;

}


underflow (iobuf_t a)
{
  size_t len;
  int rc;

  assert (a->d.start == a->d.len);
  if (a->use == 3)
    return -1;			/* EOF because a temp buffer can't do an underflow */

  if (a->filter_eof)
    {
      if (a->chain)
	{
	  iobuf_t b = a->chain;
	  if (DBG_IOBUF)
	    log_debug ("iobuf-%d.%d: pop '%s' in underflow\n",
		       a->no, a->subno, a->desc?a->desc:"?");
	  xfree (a->d.buf);
	  xfree (a->real_fname);
	  memcpy (a, b, sizeof *a);
	  xfree (b);
	  print_chain (a);
	}
      else
	a->filter_eof = 0;	/* for the top level filter */
      if (DBG_IOBUF)
	log_debug ("iobuf-%d.%d: underflow: eof (due to filter eof)\n",
		   a->no, a->subno);
      return -1;		/* return one(!) EOF */
    }
  if (a->error)
    {
      if (DBG_IOBUF)
	log_debug ("iobuf-%d.%d: error\n", a->no, a->subno);
      return -1;
    }

  if (a->directfp)
    {
      FILE *fp = a->directfp;

      len = fread (a->d.buf, 1, a->d.size, fp);
      if (len < a->d.size)
	{
	  if (ferror (fp))
	    a->error = gpg_error_from_syserror ();
	}
      a->d.len = len;
      a->d.start = 0;
      return len ? a->d.buf[a->d.start++] : -1;
    }

  if (a->filter)
    {
      len = a->d.size;
      if (DBG_IOBUF)
	log_debug ("iobuf-%d.%d: underflow: req=%lu\n",
		   a->no, a->subno, (ulong) len);
      rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
		      a->d.buf, &len);
      if (DBG_IOBUF)
	{
	  log_debug ("iobuf-%d.%d: underflow: got=%lu rc=%d\n",
		     a->no, a->subno, (ulong) len, rc);
/*  	    if( a->no == 1 ) */
/*                   log_hexdump ("     data:", a->d.buf, len); */
	}
      if (a->use == 1 && rc == -1)
	{			/* EOF: we can remove the filter */
	  size_t dummy_len = 0;

	  /* and tell the filter to free itself */
	  if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain,
			       NULL, &dummy_len)))
	    log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
	  if (a->filter_ov && a->filter_ov_owner)
	    {
	      xfree (a->filter_ov);
	      a->filter_ov = NULL;
	    }
	  a->filter = NULL;
	  a->desc = NULL;
	  a->filter_ov = NULL;
	  a->filter_eof = 1;
	  if (!len && a->chain)
	    {
	      iobuf_t b = a->chain;
	      if (DBG_IOBUF)
		log_debug ("iobuf-%d.%d: pop in underflow (!len)\n",
			   a->no, a->subno);
	      xfree (a->d.buf);
	      xfree (a->real_fname);
	      memcpy (a, b, sizeof *a);
	      xfree (b);
	      print_chain (a);
	    }
	}
      else if (rc)
	a->error = rc;

      if (!len)
	{
	  if (DBG_IOBUF)
	    log_debug ("iobuf-%d.%d: underflow: eof\n", a->no, a->subno);
	  return -1;
	}
      a->d.len = len;
      a->d.start = 0;
      return a->d.buf[a->d.start++];
    }
  else
    {
      if (DBG_IOBUF)
	log_debug ("iobuf-%d.%d: underflow: eof (no filter)\n",
		   a->no, a->subno);
      return -1;		/* no filter; return EOF */
    }
}


control_pcsc_direct (int slot, pcsc_dword_t ioctl_code,
                     const unsigned char *cntlbuf, size_t len,
                     unsigned char *buffer, pcsc_dword_t *buflen)
{
  long err;

  err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
                      cntlbuf, len, buffer, *buflen, buflen);
  if (err)
    {
      log_error ("pcsc_control failed: %s (0x%lx)\n",
                 pcsc_error_string (err), err);
      return pcsc_error_to_sw (err);
    }

  return 0;
}


do_check_pin (app_t app, const char *keyidstr,
              gpg_error_t (*pincb)(void*, const char *, char **),
              void *pincb_arg)
{
  unsigned char tmp_sn[20];
  const char *s;
  int n;
  int admin_pin = 0;

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

  /* Check whether an OpenPGP card of any version has been requested. */
  if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
    return gpg_error (GPG_ERR_INV_ID);

  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 == '/')
    ; /* We ignore a fingerprint. */
  else if (!strcmp (s, "[CHV3]") )
    admin_pin = 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);

  /* Yes, there is a race conditions: The user might pull the card
     right here and we won't notice that.  However this is not a
     problem and the check above is merely for a graceful failure
     between operations. */

  if (admin_pin)
    {
      void *relptr;
      unsigned char *value;
      size_t valuelen;
      int count;

      relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
      if (!relptr || valuelen < 7)
        {
          log_error (_("error retrieving CHV status from card\n"));
          xfree (relptr);
          return gpg_error (GPG_ERR_CARD);
        }
      count = value[6];
      xfree (relptr);

      if (!count)
        {
          log_info (_("card is permanently locked!\n"));
          return gpg_error (GPG_ERR_BAD_PIN);
        }
      else if (count < 3)
        {
          log_info (_("verification of Admin PIN is currently prohibited "
                      "through this command\n"));
          return gpg_error (GPG_ERR_GENERAL);
        }

      app->did_chv3 = 0; /* Force verification.  */
      return verify_chv3 (app, pincb, pincb_arg);
    }
  else
    return verify_chv2 (app, pincb, pincb_arg);
}