/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include "tomcrypt_private.h"

/**
  @file ec25519_import_pkcs8.c
  Generic import of a Curve/Ed25519 private key in PKCS#8 format, Steffen Jaeckel
*/

#ifdef LTC_CURVE25519

/**
  Generic import of a Curve/Ed25519 private key in PKCS#8 format
  @param in        The DER-encoded PKCS#8-formatted private key
  @param inlen     The length of the input data
  @param passwd    The password to decrypt the private key
  @param passwdlen Password's length (octets)
  @param key       [out] Where to import the key to
  @return CRYPT_OK if successful, on error all allocated memory is freed automatically
*/
int ec25519_import_pkcs8(const unsigned char *in, unsigned long inlen,
                       const void *pwd, unsigned long pwdlen,
                       enum ltc_oid_id id, sk_to_pk fp,
                       curve25519_key *key)
{
   int err;
   ltc_asn1_list *l = NULL;
   const char *oid;
   ltc_asn1_list alg_id[1];
   unsigned char private_key[34];
   unsigned long version, key_len;
   unsigned long tmpoid[16];

   LTC_ARGCHK(in  != NULL);
   LTC_ARGCHK(key != NULL);
   LTC_ARGCHK(fp != NULL);

   if ((err = pkcs8_decode_flexi(in, inlen, pwd, pwdlen, &l)) == CRYPT_OK) {

      LTC_SET_ASN1(alg_id, 0, LTC_ASN1_OBJECT_IDENTIFIER, tmpoid, sizeof(tmpoid) / sizeof(tmpoid[0]));

      key_len = sizeof(private_key);
      if ((err = der_decode_sequence_multi(l->data, l->size,
                                           LTC_ASN1_SHORT_INTEGER,      1uL, &version,
                                           LTC_ASN1_SEQUENCE,           1uL, alg_id,
                                           LTC_ASN1_OCTET_STRING,   key_len, private_key,
                                           LTC_ASN1_EOL,                0uL, NULL))
          != CRYPT_OK) {
         /* If there are attributes added after the private_key it is tagged with version 1 and
          * we get an 'input too long' error but the rest is already decoded and can be
          * handled the same as for version 0
          */
         if ((err == CRYPT_INPUT_TOO_LONG) && (version == 1)) {
            version = 0;
         } else {
            goto out;
         }
      }

      if ((err = pk_get_oid(id, &oid)) != CRYPT_OK) {
         goto out;
      }
      if ((err = pk_oid_cmp_with_asn1(oid, &alg_id[0])) != CRYPT_OK) {
         goto out;
      }

      if (version == 0) {
         key_len = sizeof(key->priv);
         if ((err = der_decode_octet_string(private_key, sizeof(private_key), key->priv, &key_len)) == CRYPT_OK) {
            fp(key->pub, key->priv);
            key->type = PK_PRIVATE;
            key->algo = id;
         }
      } else {
         err = CRYPT_PK_INVALID_TYPE;
      }
   }
out:
   if (l) der_free_sequence_flexi(l);
#ifdef LTC_CLEAN_STACK
   zeromem(private_key, sizeof(private_key));
#endif

   return err;
}

#endif
