/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */

#include "tomcrypt_private.h"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshorten-64-to-32"

/**
  @file ecc_import.c
  ECC Crypto, Tom St Denis
*/

#if defined(LTC_MECC) && defined(LTC_DER)

/**
  Import an ECC key from a binary packet
  @param in      The packet to import
  @param inlen   The length of the packet
  @param key     [out] The destination of the import
  @return CRYPT_OK if successful, upon error all allocated memory will be freed
*/
int ecc_import(const unsigned char *in, unsigned long inlen, ecc_key *key)
{
   return ecc_import_ex(in, inlen, key, NULL);
}

/**
  Import an ECC key from a binary packet, using user supplied domain params rather than one of the NIST ones
  @param in      The packet to import
  @param inlen   The length of the packet
  @param key     [out] The destination of the import
  @param cu      pointer to user supplied params; must be the same as the params used when exporting
  @return CRYPT_OK if successful, upon error all allocated memory will be freed
*/
int ecc_import_ex(const unsigned char *in, unsigned long inlen, ecc_key *key, const ltc_ecc_curve *cu)
{
   unsigned long key_size;
   unsigned char flags[1];
   int           err;

   LTC_ARGCHK(in  != NULL);
   LTC_ARGCHK(key != NULL);
   LTC_ARGCHK(ltc_mp.name != NULL);

   /* find out what type of key it is */
   err = der_decode_sequence_multi(in, inlen, LTC_ASN1_BIT_STRING,    1UL, flags,
                                              LTC_ASN1_SHORT_INTEGER, 1UL, &key_size,
                                              LTC_ASN1_EOL,           0UL, NULL);
   if (err != CRYPT_OK && err != CRYPT_INPUT_TOO_LONG) {
      return err;
   }

   /* allocate & initialize the key */
   if (cu == NULL) {
      if ((err = ecc_set_curve_by_size(key_size, key)) != CRYPT_OK) { goto done; }
   } else {
      if ((err = ecc_set_curve(cu, key)) != CRYPT_OK)               { goto done; }
   }

   if (flags[0] == 1) {
      /* private key */
      key->type = PK_PRIVATE;
      if ((err = der_decode_sequence_multi(in, inlen,
                                     LTC_ASN1_BIT_STRING,      1UL, flags,
                                     LTC_ASN1_SHORT_INTEGER,   1UL, &key_size,
                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.x,
                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.y,
                                     LTC_ASN1_INTEGER,         1UL, key->k,
                                     LTC_ASN1_EOL,             0UL, NULL)) != CRYPT_OK) {
         goto done;
      }
   } else if (flags[0] == 0) {
      /* public key */
      key->type = PK_PUBLIC;
      if ((err = der_decode_sequence_multi(in, inlen,
                                     LTC_ASN1_BIT_STRING,      1UL, flags,
                                     LTC_ASN1_SHORT_INTEGER,   1UL, &key_size,
                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.x,
                                     LTC_ASN1_INTEGER,         1UL, key->pubkey.y,
                                     LTC_ASN1_EOL,             0UL, NULL)) != CRYPT_OK) {
         goto done;
      }
   }
   else {
      err = CRYPT_INVALID_PACKET;
      goto done;
   }

   /* set z */
   if ((err = ltc_mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto done; }

   /* point on the curve + other checks */
   if ((err = ltc_ecc_verify_key(key)) != CRYPT_OK)  { goto done; }

   /* we're good */
   return CRYPT_OK;

done:
   ecc_free(key);
   return err;
}
#endif

#pragma clang diagnostic pop
