/* LibTomCrypt, modular cryptographic library -- Tom St Denis
 *
 * LibTomCrypt is a library that provides various cryptographic
 * algorithms in a highly modular and flexible manner.
 *
 * The library is free for all purposes without any express
 * guarantee it works.
 *
 */

/* Implements ECC over Z/pZ for curve y^2 = x^3 + a*x + b
 *
 */

#include "tomcrypt.h"

/* origin of this code - OLPC */

#ifdef LTC_MECC

/**
  Verify a key according to ANSI spec
  @param key     The key to validate
  @return CRYPT_OK if successful
*/

int ecc_verify_key(ecc_key *key)
{
   int err;
   void *prime = NULL;
   void *order = NULL;
   void *a = NULL;
   ecc_point *point;

   if (mp_init_multi(&order, &prime, NULL) != CRYPT_OK) {
      return CRYPT_MEM;
   }

   /* Test 1: Are the x amd y points of the public key in the field? */
   if ((err = ltc_mp.read_radix(prime, key->dp->prime, 16)) != CRYPT_OK)                  { goto done2; }

   if (ltc_mp.compare_d(key->pubkey.z, 1) == LTC_MP_EQ) {
      if ((ltc_mp.compare(key->pubkey.x, prime) != LTC_MP_LT) ||
          (ltc_mp.compare(key->pubkey.y, prime) != LTC_MP_LT) ||
          (ltc_mp.compare_d(key->pubkey.x, 0)   != LTC_MP_GT) ||
          (ltc_mp.compare_d(key->pubkey.y, 0)   != LTC_MP_GT)
         )
      {
         err = CRYPT_INVALID_PACKET;
         goto done2;
      }
   }

   /* Test 2: is the public key on the curve? */
   if ((err = ltc_ecc_is_point(key->dp, key->pubkey.x, key->pubkey.y)) != CRYPT_OK)       { goto done2; }

   /* Test 3: does nG = O? (n = order, O = point at infinity, G = public key) */
   point = ltc_ecc_new_point();
   if ((err = ltc_mp.read_radix(order, key->dp->order, 16)) != CRYPT_OK)                  { goto done1; }
   if ((err = ltc_mp.read_radix(a, key->dp->A, 16)) != CRYPT_OK)                          { goto done1; }
   if ((err = ltc_ecc_mulmod(order, &(key->pubkey), point, a, prime, 1)) != CRYPT_OK)     { goto done1; }

   if (ltc_ecc_is_point_at_infinity(point, prime)) {
      err = CRYPT_ERROR;
   }
   else {
      err = CRYPT_OK;
   }

done1:
   ltc_ecc_del_point(point);
done2:
   mp_clear_multi(prime, order, NULL);
   return err;
}

#endif
