do_key (iobuf_t out, int ctb, PKT_public_key *pk)
{
  gpg_error_t err = 0;
  int i, nskey, npkey;
  iobuf_t a = iobuf_temp(); /* Build in a self-enlarging buffer.  */

  /* Write the version number - if none is specified, use 4 */
  if ( !pk->version )
    iobuf_put ( a, 4 );
  else
    iobuf_put ( a, pk->version );
  write_32 (a, pk->timestamp );

  iobuf_put (a, pk->pubkey_algo );

  /* Get number of secret and public parameters.  They are held in one
     array first the public ones, then the secret ones.  */
  nskey = pubkey_get_nskey (pk->pubkey_algo);
  npkey = pubkey_get_npkey (pk->pubkey_algo);

  /* If we don't have any public parameters - which is for example the
     case if we don't know the algorithm used - the parameters are
     stored as one blob in a faked (opaque) MPI. */
  if (!npkey)
    {
      write_fake_data (a, pk->pkey[0]);
      goto leave;
    }
  assert (npkey < nskey);

  for (i=0; i < npkey; i++ )
    {
      if (   (pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0))
          || (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0))
          || (pk->pubkey_algo == PUBKEY_ALGO_ECDH  && (i == 0 || i == 2)))
        err = gpg_mpi_write_nohdr (a, pk->pkey[i]);
      else
        err = gpg_mpi_write (a, pk->pkey[i]);
      if (err)
        goto leave;
    }


  if (pk->seckey_info)
    {
      /* This is a secret key packet.  */
      struct seckey_info *ski = pk->seckey_info;

      /* Build the header for protected (encrypted) secret parameters.  */
      if (ski->is_protected)
        {
          /* OpenPGP protection according to rfc2440. */
          iobuf_put (a, ski->sha1chk? 0xfe : 0xff);
          iobuf_put (a, ski->algo);
          if (ski->s2k.mode >= 1000)
            {
              /* These modes are not possible in OpenPGP, we use them
                 to implement our extensions, 101 can be viewed as a
                 private/experimental extension (this is not specified
                 in rfc2440 but the same scheme is used for all other
                 algorithm identifiers). */
              iobuf_put (a, 101);
              iobuf_put (a, ski->s2k.hash_algo);
              iobuf_write (a, "GNU", 3 );
              iobuf_put (a, ski->s2k.mode - 1000);
            }
          else
            {
              iobuf_put (a, ski->s2k.mode);
              iobuf_put (a, ski->s2k.hash_algo);
            }

          if (ski->s2k.mode == 1 || ski->s2k.mode == 3)
            iobuf_write (a, ski->s2k.salt, 8);

          if (ski->s2k.mode == 3)
            iobuf_put (a, ski->s2k.count);

          /* For our special modes 1001, 1002 we do not need an IV. */
          if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002)
            iobuf_write (a, ski->iv, ski->ivlen);

        }
      else /* Not protected. */
        iobuf_put (a, 0 );

      if (ski->s2k.mode == 1001)
        ; /* GnuPG extension - don't write a secret key at all. */
      else if (ski->s2k.mode == 1002)
        {
          /* GnuPG extension - divert to OpenPGP smartcard. */
          /* Length of the serial number or 0 for no serial number. */
          iobuf_put (a, ski->ivlen );
          /* The serial number gets stored in the IV field.  */
          iobuf_write (a, ski->iv, ski->ivlen);
        }
      else if (ski->is_protected)
        {
          /* The secret key is protected - write it out as it is.  */
          byte *p;
          unsigned int ndatabits;

          assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE));
          p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits);
          iobuf_write (a, p, (ndatabits+7)/8 );
        }
      else
        {
          /* Non-protected key. */
          for ( ; i < nskey; i++ )
            if ( (err = gpg_mpi_write (a, pk->pkey[i])))
              goto leave;
          write_16 (a, ski->csum );
        }
    }

 leave:
  if (!err)
    {
      /* Build the header of the packet - which we must do after
         writing all the other stuff, so that we know the length of
         the packet */
      write_header2 (out, ctb, iobuf_get_temp_length(a), pk->hdrbytes);
      /* And finally write it out to the real stream. */
      err = iobuf_write_temp (out, a);
    }

  iobuf_close (a); /* Close the temporary buffer */
  return err;
}