#include "testutils.h"
#include "gostdsa.h"
#include "knuth-lfib.h"

/* Check if y^2 = x^3 - 3x + b */
static int
ecc_valid_p (struct ecc_point *pub)
{
  mpz_t t, x, y;
  mpz_t lhs, rhs;
  int res;
  mp_size_t size;

  size = pub->ecc->p.size;

  /* First check range */
  if (mpn_cmp (pub->p, pub->ecc->p.m, size) >= 0
      || mpn_cmp (pub->p + size, pub->ecc->p.m, size) >= 0)
    return 0;

  mpz_init (lhs);
  mpz_init (rhs);

  mpz_roinit_n (x, pub->p, size);
  mpz_roinit_n (y, pub->p + size, size);

  mpz_mul (lhs, y, y);

  if (pub->ecc->p.bit_size == 255)
    {
      /* Check that
	 121666 (1 + x^2 - y^2) = 121665 x^2 y^2 */
      mpz_t x2;
      mpz_init (x2);
      mpz_mul (x2, x, x); /* x^2 */
      mpz_mul (rhs, x2, lhs); /* x^2 y^2 */
      mpz_sub (lhs, x2, lhs); /* x^2 - y^2 */
      mpz_add_ui (lhs, lhs, 1); /* 1 + x^2 - y^2 */
      mpz_mul_ui (lhs, lhs, 121666);
      mpz_mul_ui (rhs, rhs, 121665);

      mpz_clear (x2);
    }
  else if (pub->ecc->p.bit_size == 448)
    {
      /* Check that
	 x^2 + y^2 = 1 - 39081 x^2 y^2 */
      mpz_t x2, d;
      mpz_init (x2);
      mpz_init_set_ui (d, 39081);
      mpz_mul (x2, x, x); /* x^2 */
      mpz_mul (d, d, x2); /* 39081 x^2 */
      mpz_set_ui (rhs, 1);
      mpz_submul (rhs, d, lhs); /* 1 - 39081 x^2 y^2 */
      mpz_add (lhs, x2, lhs);	/* x^2 + y^2 */

      mpz_clear (d);
      mpz_clear (x2);
    }
  else
    {
      /* Check y^2 = x^3 - 3 x + b */
      mpz_mul (rhs, x, x);
      mpz_sub_ui (rhs, rhs, 3);
      mpz_mul (rhs, rhs, x);
      mpz_add (rhs, rhs, mpz_roinit_n (t, pub->ecc->b, size));
    }
  res = mpz_congruent_p (lhs, rhs, mpz_roinit_n (t, pub->ecc->p.m, size));

  mpz_clear (lhs);
  mpz_clear (rhs);

  return res;
}

void
test_main (void)
{
  unsigned i;
  struct knuth_lfib_ctx rctx;
  struct dsa_signature signature;

  struct tstring *digest;

  knuth_lfib_init (&rctx, 4711);
  dsa_signature_init (&signature);

  digest = SHEX (/* sha256("abc") */
		 "BA7816BF 8F01CFEA 414140DE 5DAE2223"
		 "B00361A3 96177A9C B410FF61 F20015AD");

  for (i = 0; ecc_curves[i]; i++)
    {
      const struct ecc_curve *ecc = ecc_curves[i];
      struct ecc_point pub;
      struct ecc_scalar key;

      if (ecc->p.bit_size == 255 || ecc->p.bit_size == 448)
	/* Exclude curve25519 and curve448, not supported with GOSTDSA. */
	continue;

      if (verbose)
	fprintf (stderr, "Curve %d\n", ecc->p.bit_size);

      ecc_point_init (&pub, ecc);
      ecc_scalar_init (&key, ecc);

      ecdsa_generate_keypair (&pub, &key,
			      &rctx,
			      (nettle_random_func *) knuth_lfib_random);

      if (verbose)
	{
	  fprintf (stderr, "Public key:\nx = ");
	  write_mpn (stderr, 16, pub.p, ecc->p.size);
	  fprintf (stderr, "\ny = ");
	  write_mpn (stderr, 16, pub.p + ecc->p.size, ecc->p.size);
	  fprintf (stderr, "\nPrivate key: ");
	  write_mpn (stderr, 16, key.p, ecc->p.size);
	  fprintf (stderr, "\n");
	}
      if (!ecc_valid_p (&pub))
	die ("gostdsa_generate_keypair produced an invalid point.\n");

      gostdsa_sign (&key,
		   &rctx, (nettle_random_func *) knuth_lfib_random,
		   digest->length, digest->data,
		   &signature);

      if (!gostdsa_verify (&pub, digest->length, digest->data,
			   &signature))
	die ("gostdsa_verify failed.\n");

      digest->data[3] ^= 17;
      if (gostdsa_verify (&pub, digest->length, digest->data,
			  &signature))
	die ("gostdsa_verify  returned success with invalid digest.\n");
      digest->data[3] ^= 17;

      mpz_combit (signature.r, 117);
      if (gostdsa_verify (&pub, digest->length, digest->data,
			  &signature))
	die ("gostdsa_verify  returned success with invalid signature.r.\n");

      mpz_combit (signature.r, 117);
      mpz_combit (signature.s, 93);
      if (gostdsa_verify (&pub, digest->length, digest->data,
			  &signature))
	die ("gostdsa_verify  returned success with invalid signature.s.\n");

      ecc_point_clear (&pub);
      ecc_scalar_clear (&key);
    }
  dsa_signature_clear (&signature);
}
