rsa_writekey (app_t app, gpg_error_t (*pincb)(void*, const char *, char **),
              void *pincb_arg, int keyno,
              const unsigned char *buf, size_t buflen, int depth)
{
  gpg_error_t err;
  const unsigned char *tok;
  size_t toklen;
  int last_depth1, last_depth2;
  const unsigned char *rsa_n = NULL;
  const unsigned char *rsa_e = NULL;
  const unsigned char *rsa_p = NULL;
  const unsigned char *rsa_q = NULL;
  size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
  unsigned int nbits;
  unsigned int maxbits;
  unsigned char *template = NULL;
  unsigned char *tp;
  size_t template_len;
  unsigned char fprbuf[20];
  u32 created_at = 0;

  last_depth1 = depth;
  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
         && depth && depth >= last_depth1)
    {
      if (tok)
        {
          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
          goto leave;
        }
      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
        goto leave;
      if (tok && toklen == 1)
        {
          const unsigned char **mpi;
          size_t *mpi_len;

          switch (*tok)
            {
            case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
            case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
            case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
            case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
            default: mpi = NULL;  mpi_len = NULL; break;
            }
          if (mpi && *mpi)
            {
              err = gpg_error (GPG_ERR_DUP_VALUE);
              goto leave;
            }
          if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
            goto leave;
          if (tok && mpi)
            {
              /* Strip off leading zero bytes and save. */
              for (;toklen && !*tok; toklen--, tok++)
                ;
              *mpi = tok;
              *mpi_len = toklen;
            }
        }
      /* Skip until end of list. */
      last_depth2 = depth;
      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
             && depth && depth >= last_depth2)
        ;
      if (err)
        goto leave;
    }
  /* Parse other attributes. */
  last_depth1 = depth;
  while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
         && depth && depth >= last_depth1)
    {
      if (tok)
        {
          err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
          goto leave;
        }
      if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
        goto leave;
      if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
        {
          if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
            goto leave;
          if (tok)
            {
              for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
                   tok++, toklen--)
                created_at = created_at*10 + (*tok - '0');
            }
        }
      /* Skip until end of list. */
      last_depth2 = depth;
      while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
             && depth && depth >= last_depth2)
        ;
      if (err)
        goto leave;
    }


  /* Check that we have all parameters and that they match the card
     description. */
  if (!created_at)
    {
      log_error (_("creation timestamp missing\n"));
      err = gpg_error (GPG_ERR_INV_VALUE);
      goto leave;
    }

  maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
  nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
  if (opt.verbose)
    log_info ("RSA modulus size is %u bits (%u bytes)\n",
              nbits, (unsigned int)rsa_n_len);
  if (nbits && nbits != maxbits
      && app->app_local->extcap.algo_attr_change)
    {
      /* Try to switch the key to a new length.  */
      err = change_keyattr (app, keyno, nbits, pincb, pincb_arg);
      if (!err)
        maxbits = app->app_local->keyattr[keyno].rsa.n_bits;
    }
  if (nbits != maxbits)
    {
      log_error (_("RSA modulus missing or not of size %d bits\n"),
                 (int)maxbits);
      err = gpg_error (GPG_ERR_BAD_SECKEY);
      goto leave;
    }

  maxbits = app->app_local->keyattr[keyno].rsa.e_bits;
  if (maxbits > 32 && !app->app_local->extcap.is_v2)
    maxbits = 32; /* Our code for v1 does only support 32 bits.  */
  nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
  if (nbits < 2 || nbits > maxbits)
    {
      log_error (_("RSA public exponent missing or larger than %d bits\n"),
                 (int)maxbits);
      err = gpg_error (GPG_ERR_BAD_SECKEY);
      goto leave;
    }

  maxbits = app->app_local->keyattr[keyno].rsa.n_bits/2;
  nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
  if (nbits != maxbits)
    {
      log_error (_("RSA prime %s missing or not of size %d bits\n"),
                 "P", (int)maxbits);
      err = gpg_error (GPG_ERR_BAD_SECKEY);
      goto leave;
    }
  nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
  if (nbits != maxbits)
    {
      log_error (_("RSA prime %s missing or not of size %d bits\n"),
                 "Q", (int)maxbits);
      err = gpg_error (GPG_ERR_BAD_SECKEY);
      goto leave;
    }

  /* We need to remove the cached public key.  */
  xfree (app->app_local->pk[keyno].key);
  app->app_local->pk[keyno].key = NULL;
  app->app_local->pk[keyno].keylen = 0;
  app->app_local->pk[keyno].read_done = 0;


  if (app->app_local->extcap.is_v2)
    {
      unsigned char *rsa_u, *rsa_dp, *rsa_dq;
      size_t rsa_u_len, rsa_dp_len, rsa_dq_len;
      gcry_mpi_t mpi_e, mpi_p, mpi_q;
      gcry_mpi_t mpi_u = gcry_mpi_snew (0);
      gcry_mpi_t mpi_dp = gcry_mpi_snew (0);
      gcry_mpi_t mpi_dq = gcry_mpi_snew (0);
      gcry_mpi_t mpi_tmp = gcry_mpi_snew (0);
      int exmode;

      /* Calculate the u, dp and dq components needed by RSA_CRT cards */
      gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL);
      gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL);
      gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL);

      gcry_mpi_invm (mpi_u, mpi_q, mpi_p);
      gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1);
      gcry_mpi_invm (mpi_dp, mpi_e, mpi_tmp);
      gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1);
      gcry_mpi_invm (mpi_dq, mpi_e, mpi_tmp);

      gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_u, &rsa_u_len, mpi_u);
      gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dp, &rsa_dp_len, mpi_dp);
      gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dq, &rsa_dq_len, mpi_dq);

      gcry_mpi_release (mpi_e);
      gcry_mpi_release (mpi_p);
      gcry_mpi_release (mpi_q);
      gcry_mpi_release (mpi_u);
      gcry_mpi_release (mpi_dp);
      gcry_mpi_release (mpi_dq);
      gcry_mpi_release (mpi_tmp);

      /* Build the private key template as described in section 4.3.3.7 of
         the OpenPGP card specs version 2.0.  */
      err = build_privkey_template (app, keyno,
                                    rsa_n, rsa_n_len,
                                    rsa_e, rsa_e_len,
                                    rsa_p, rsa_p_len,
                                    rsa_q, rsa_q_len,
                                    rsa_u, rsa_u_len,
                                    rsa_dp, rsa_dp_len,
                                    rsa_dq, rsa_dq_len,
                                    &template, &template_len);
      xfree(rsa_u);
      xfree(rsa_dp);
      xfree(rsa_dq);

      if (err)
        goto leave;

      /* Prepare for storing the key.  */
      err = verify_chv3 (app, pincb, pincb_arg);
      if (err)
        goto leave;

      /* Store the key. */
      if (app->app_local->cardcap.ext_lc_le && template_len > 254)
        exmode = 1;    /* Use extended length w/o a limit.  */
      else if (app->app_local->cardcap.cmd_chaining && template_len > 254)
        exmode = -254;
      else
        exmode = 0;
      err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
                                  template, template_len);
    }
  else
    {
      /* Build the private key template as described in section 4.3.3.6 of
         the OpenPGP card specs version 1.1:
         0xC0   <length> public exponent
         0xC1   <length> prime p
         0xC2   <length> prime q
      */
      assert (rsa_e_len <= 4);
      template_len = (1 + 1 + 4
                      + 1 + 1 + rsa_p_len
                      + 1 + 1 + rsa_q_len);
      template = tp = xtrymalloc_secure (template_len);
      if (!template)
        {
          err = gpg_error_from_syserror ();
          goto leave;
        }
      *tp++ = 0xC0;
      *tp++ = 4;
      memcpy (tp, rsa_e, rsa_e_len);
      if (rsa_e_len < 4)
        {
          /* Right justify E. */
          memmove (tp+4-rsa_e_len, tp, rsa_e_len);
          memset (tp, 0, 4-rsa_e_len);
        }
      tp += 4;

      *tp++ = 0xC1;
      *tp++ = rsa_p_len;
      memcpy (tp, rsa_p, rsa_p_len);
      tp += rsa_p_len;

      *tp++ = 0xC2;
      *tp++ = rsa_q_len;
      memcpy (tp, rsa_q, rsa_q_len);
      tp += rsa_q_len;

      assert (tp - template == template_len);

      /* Prepare for storing the key.  */
      err = verify_chv3 (app, pincb, pincb_arg);
      if (err)
        goto leave;

      /* Store the key. */
      err = iso7816_put_data (app->slot, 0,
                              (app->card_version > 0x0007? 0xE0:0xE9)+keyno,
                              template, template_len);
    }
  if (err)
    {
      log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
      goto leave;
    }

  err = store_fpr (app, keyno, created_at, fprbuf, KEY_TYPE_RSA,
                   rsa_n, rsa_n_len, rsa_e, rsa_e_len);
  if (err)
    goto leave;


 leave:
  xfree (template);
  return err;
}