do_generate_keypair (struct para_data_s *para,
		     struct output_control_s *outctrl, int card)
{
  gpg_error_t err;
  KBNODE pub_root = NULL;
  const char *s;
  PKT_public_key *pri_psk = NULL;
  PKT_public_key *sub_psk = NULL;
  struct revocation_key *revkey;
  int did_sub = 0;
  u32 timestamp;
  char *cache_nonce = NULL;

  if (outctrl->dryrun)
    {
      log_info("dry-run mode - key generation skipped\n");
      return;
    }

  if ( outctrl->use_files )
    {
      if ( outctrl->pub.newfname )
        {
          iobuf_close(outctrl->pub.stream);
          outctrl->pub.stream = NULL;
          if (outctrl->pub.fname)
            iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE,
                         0, (char*)outctrl->pub.fname);
          xfree( outctrl->pub.fname );
          outctrl->pub.fname =  outctrl->pub.newfname;
          outctrl->pub.newfname = NULL;

          if (is_secured_filename (outctrl->pub.fname) )
            {
              outctrl->pub.stream = NULL;
              gpg_err_set_errno (EPERM);
            }
          else
            outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0);
          if (!outctrl->pub.stream)
            {
              log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname,
                        strerror(errno) );
              return;
            }
          if (opt.armor)
            {
              outctrl->pub.afx->what = 1;
              push_armor_filter (outctrl->pub.afx, outctrl->pub.stream);
            }
        }
      assert( outctrl->pub.stream );
      if (opt.verbose)
        log_info (_("writing public key to '%s'\n"), outctrl->pub.fname );
    }


  /* We create the packets as a tree of kbnodes.  Because the
     structure we create is known in advance we simply generate a
     linked list.  The first packet is a dummy packet which we flag as
     deleted.  The very first packet must always be a KEY packet.  */

  start_tree (&pub_root);

  timestamp = get_parameter_u32 (para, pKEYCREATIONDATE);
  if (!timestamp)
    timestamp = make_timestamp ();

  /* Note that, depending on the backend (i.e. the used scdaemon
     version), the card key generation may update TIMESTAMP for each
     key.  Thus we need to pass TIMESTAMP to all signing function to
     make sure that the binding signature is done using the timestamp
     of the corresponding (sub)key and not that of the primary key.
     An alternative implementation could tell the signing function the
     node of the subkey but that is more work than just to pass the
     current timestamp.  */

  if (!card)
    err = do_create (get_parameter_algo( para, pKEYTYPE, NULL ),
                     get_parameter_uint( para, pKEYLENGTH ),
                     get_parameter_value (para, pKEYCURVE),
                     pub_root,
                     timestamp,
                     get_parameter_u32( para, pKEYEXPIRE ), 0,
                     outctrl->keygen_flags,
                     get_parameter_passphrase (para),
                     &cache_nonce);
  else
    err = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root,
                        &timestamp,
                        get_parameter_u32 (para, pKEYEXPIRE));

  /* Get the pointer to the generated public key packet.  */
  if (!err)
    {
      pri_psk = pub_root->next->pkt->pkt.public_key;
      assert (pri_psk);
    }

  if (!err && (revkey = get_parameter_revkey (para, pREVOKER)))
    err = write_direct_sig (pub_root, pri_psk, revkey, timestamp, cache_nonce);

  if (!err && (s = get_parameter_value (para, pUSERID)))
    {
      write_uid (pub_root, s );
      err = write_selfsigs (pub_root, pri_psk,
                            get_parameter_uint (para, pKEYUSAGE), timestamp,
                            cache_nonce);
    }

  /* Write the auth key to the card before the encryption key.  This
     is a partial workaround for a PGP bug (as of this writing, all
     versions including 8.1), that causes it to try and encrypt to
     the most recent subkey regardless of whether that subkey is
     actually an encryption type.  In this case, the auth key is an
     RSA key so it succeeds. */

  if (!err && card && get_parameter (para, pAUTHKEYTYPE))
    {
      err = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root,
                          &timestamp,
                          get_parameter_u32 (para, pKEYEXPIRE));
      if (!err)
        err = write_keybinding (pub_root, pri_psk, NULL,
                                PUBKEY_USAGE_AUTH, timestamp, cache_nonce);
    }

  if (!err && get_parameter (para, pSUBKEYTYPE))
    {
      sub_psk = NULL;
      if (!card)
        {
          err = do_create (get_parameter_algo (para, pSUBKEYTYPE, NULL),
                           get_parameter_uint (para, pSUBKEYLENGTH),
                           get_parameter_value (para, pSUBKEYCURVE),
                           pub_root,
                           timestamp,
                           get_parameter_u32 (para, pSUBKEYEXPIRE), 1,
                           outctrl->keygen_flags,
                           get_parameter_passphrase (para),
                           &cache_nonce);
          /* Get the pointer to the generated public subkey packet.  */
          if (!err)
            {
              kbnode_t node;

              for (node = pub_root; node; node = node->next)
                if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
                  sub_psk = node->pkt->pkt.public_key;
              assert (sub_psk);
            }
        }
      else
        {
          if ((s = get_parameter_value (para, pCARDBACKUPKEY)))
            {
              /* A backup of the encryption key has been requested.
                 Generate the key in software and import it then to
                 the card.  Write a backup file. */
              err = gen_card_key_with_backup
                (PUBKEY_ALGO_RSA, 2, 0, pub_root, timestamp,
                 get_parameter_u32 (para, pKEYEXPIRE), para);
            }
          else
            {
              err = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root,
                                  &timestamp,
                                  get_parameter_u32 (para, pKEYEXPIRE));
            }
        }

      if (!err)
        err = write_keybinding (pub_root, pri_psk, sub_psk,
                                get_parameter_uint (para, pSUBKEYUSAGE),
                                timestamp, cache_nonce);
      did_sub = 1;
    }

  if (!err && outctrl->use_files)  /* Direct write to specified files.  */
    {
      err = write_keyblock (outctrl->pub.stream, pub_root);
      if (err)
        log_error ("can't write public key: %s\n", gpg_strerror (err));
    }
  else if (!err) /* Write to the standard keyrings.  */
    {
      KEYDB_HANDLE pub_hd = keydb_new ();

      err = keydb_locate_writable (pub_hd, NULL);
      if (err)
        log_error (_("no writable public keyring found: %s\n"),
                   gpg_strerror (err));

      if (!err && opt.verbose)
        {
          log_info (_("writing public key to '%s'\n"),
                    keydb_get_resource_name (pub_hd));
        }

      if (!err)
        {
          err = keydb_insert_keyblock (pub_hd, pub_root);
          if (err)
            log_error (_("error writing public keyring '%s': %s\n"),
                       keydb_get_resource_name (pub_hd), gpg_strerror (err));
        }

      keydb_release (pub_hd);

      if (!err)
        {
          int no_enc_rsa;
          PKT_public_key *pk;

          no_enc_rsa = ((get_parameter_algo (para, pKEYTYPE, NULL)
                         == PUBKEY_ALGO_RSA)
                        && get_parameter_uint (para, pKEYUSAGE)
                        && !((get_parameter_uint (para, pKEYUSAGE)
                              & PUBKEY_USAGE_ENC)) );

          pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key;

          keyid_from_pk (pk, pk->main_keyid);
          register_trusted_keyid (pk->main_keyid);

          update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK)
                                  | TRUST_ULTIMATE ));

          gen_standard_revoke (pk, cache_nonce);

          if (!opt.batch)
            {
              tty_printf (_("public and secret key created and signed.\n") );
              tty_printf ("\n");
              list_keyblock (pub_root, 0, 1, 1, NULL);
            }


          if (!opt.batch
              && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA
                  || no_enc_rsa )
              && !get_parameter (para, pSUBKEYTYPE) )
            {
              tty_printf(_("Note that this key cannot be used for "
                           "encryption.  You may want to use\n"
                           "the command \"--edit-key\" to generate a "
                           "subkey for this purpose.\n") );
            }
        }
    }

  if (err)
    {
      if (opt.batch)
        log_error ("key generation failed: %s\n", gpg_strerror (err) );
      else
        tty_printf (_("Key generation failed: %s\n"), gpg_strerror (err) );
      write_status_error (card? "card_key_generate":"key_generate", err);
      print_status_key_not_created ( get_parameter_value (para, pHANDLE) );
    }
  else
    {
      PKT_public_key *pk = find_kbnode (pub_root,
                                        PKT_PUBLIC_KEY)->pkt->pkt.public_key;
      print_status_key_created (did_sub? 'B':'P', pk,
                                get_parameter_value (para, pHANDLE));
    }

  release_kbnode (pub_root);
  xfree (cache_nonce);
}