generate_fips186 (DSA_secret_key *sk, unsigned int nbits, unsigned int qbits,
                  gcry_sexp_t deriveparms, int use_fips186_2,
                  dsa_domain_t *domain,
                  int *r_counter, void **r_seed, size_t *r_seedlen,
                  gcry_mpi_t *r_h)
{
  gpg_err_code_t ec;
  struct {
    gcry_sexp_t sexp;
    const void *seed;
    size_t seedlen;
  } initial_seed = { NULL, NULL, 0 };
  gcry_mpi_t prime_q = NULL;
  gcry_mpi_t prime_p = NULL;
  gcry_mpi_t value_g = NULL; /* The generator. */
  gcry_mpi_t value_y = NULL; /* g^x mod p */
  gcry_mpi_t value_x = NULL; /* The secret exponent. */
  gcry_mpi_t value_h = NULL; /* Helper.  */
  gcry_mpi_t value_e = NULL; /* Helper.  */
  gcry_mpi_t value_c = NULL; /* helper for x */
  gcry_mpi_t value_qm2 = NULL; /* q - 2 */

  /* Preset return values.  */
  *r_counter = 0;
  *r_seed = NULL;
  *r_seedlen = 0;
  *r_h = NULL;

  /* Derive QBITS from NBITS if requested  */
  if (!qbits)
    {
      if (nbits == 1024)
        qbits = 160;
      else if (nbits == 2048)
        qbits = 224;
      else if (nbits == 3072)
        qbits = 256;
    }

  /* Check that QBITS and NBITS match the standard.  Note that FIPS
     186-3 uses N for QBITS and L for NBITS.  */
  if (nbits == 1024 && qbits == 160 && use_fips186_2)
    ; /* Allowed in FIPS 186-2 mode.  */
  else if (nbits == 2048 && qbits == 224)
    ;
  else if (nbits == 2048 && qbits == 256)
    ;
  else if (nbits == 3072 && qbits == 256)
    ;
  else
    return GPG_ERR_INV_VALUE;

  if (domain->p && domain->q && domain->g)
    {
      /* Domain parameters are given; use them.  */
      prime_p = mpi_copy (domain->p);
      prime_q = mpi_copy (domain->q);
      value_g = mpi_copy (domain->g);
      gcry_assert (mpi_get_nbits (prime_p) == nbits);
      gcry_assert (mpi_get_nbits (prime_q) == qbits);
      gcry_assert (!deriveparms);
      ec = 0;
    }
  else
    {
      /* Generate new domain parameters.  */

      /* Get an initial seed value.  */
      if (deriveparms)
        {
          initial_seed.sexp = sexp_find_token (deriveparms, "seed", 0);
          if (initial_seed.sexp)
            initial_seed.seed = sexp_nth_data (initial_seed.sexp, 1,
                                               &initial_seed.seedlen);
        }

      if (use_fips186_2)
        ec = _gcry_generate_fips186_2_prime (nbits, qbits,
                                             initial_seed.seed,
                                             initial_seed.seedlen,
                                             &prime_q, &prime_p,
                                             r_counter,
                                             r_seed, r_seedlen);
      else
        ec = _gcry_generate_fips186_3_prime (nbits, qbits, NULL, 0,
                                             &prime_q, &prime_p,
                                             r_counter,
                                             r_seed, r_seedlen, NULL);
      sexp_release (initial_seed.sexp);
      if (ec)
        goto leave;

      /* Find a generator g (h and e are helpers).
       *    e = (p-1)/q
       */
      value_e = mpi_alloc_like (prime_p);
      mpi_sub_ui (value_e, prime_p, 1);
      mpi_fdiv_q (value_e, value_e, prime_q );
      value_g = mpi_alloc_like (prime_p);
      value_h = mpi_alloc_set_ui (1);
      do
        {
          mpi_add_ui (value_h, value_h, 1);
          /* g = h^e mod p */
          mpi_powm (value_g, value_h, value_e, prime_p);
        }
      while (!mpi_cmp_ui (value_g, 1));  /* Continue until g != 1.  */
    }

  value_c = mpi_snew (qbits);
  value_x = mpi_snew (qbits);
  value_qm2 = mpi_snew (qbits);
  mpi_sub_ui (value_qm2, prime_q, 2);

  /* FIPS 186-4 B.1.2 steps 4-6 */
  do
    {
      if( DBG_CIPHER )
        progress('.');
      _gcry_mpi_randomize (value_c, qbits, GCRY_VERY_STRONG_RANDOM);
      mpi_clear_highbit (value_c, qbits+1);
    }
  while (!(mpi_cmp_ui (value_c, 0) > 0 && mpi_cmp (value_c, value_qm2) < 0));
  /* while (mpi_cmp (value_c, value_qm2) > 0); */

  /* x = c + 1 */
  mpi_add_ui(value_x, value_c, 1);

  /* y = g^x mod p */
  value_y = mpi_alloc_like (prime_p);
  mpi_powm (value_y, value_g, value_x, prime_p);

  if (DBG_CIPHER)
    {
      progress('\n');
      log_mpidump("dsa  p", prime_p );
      log_mpidump("dsa  q", prime_q );
      log_mpidump("dsa  g", value_g );
      log_mpidump("dsa  y", value_y );
      log_mpidump("dsa  x", value_x );
      log_mpidump("dsa  h", value_h );
    }

  /* Copy the stuff to the key structures. */
  sk->p = prime_p; prime_p = NULL;
  sk->q = prime_q; prime_q = NULL;
  sk->g = value_g; value_g = NULL;
  sk->y = value_y; value_y = NULL;
  sk->x = value_x; value_x = NULL;
  *r_h = value_h; value_h = NULL;

 leave:
  _gcry_mpi_release (prime_p);
  _gcry_mpi_release (prime_q);
  _gcry_mpi_release (value_g);
  _gcry_mpi_release (value_y);
  _gcry_mpi_release (value_x);
  _gcry_mpi_release (value_h);
  _gcry_mpi_release (value_e);
  _gcry_mpi_release (value_c);
  _gcry_mpi_release (value_qm2);

  /* As a last step test this keys (this should never fail of course). */
  if (!ec && test_keys (sk, qbits) )
    {
      _gcry_mpi_release (sk->p); sk->p = NULL;
      _gcry_mpi_release (sk->q); sk->q = NULL;
      _gcry_mpi_release (sk->g); sk->g = NULL;
      _gcry_mpi_release (sk->y); sk->y = NULL;
      _gcry_mpi_release (sk->x); sk->x = NULL;
      fips_signal_error ("self-test after key generation failed");
      ec = GPG_ERR_SELFTEST_FAILED;
    }

  if (ec)
    {
      *r_counter = 0;
      xfree (*r_seed); *r_seed = NULL;
      *r_seedlen = 0;
      _gcry_mpi_release (*r_h); *r_h = NULL;
    }

  return ec;
}