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

#ifdef LTC_PKCS_8

/**
   PKCS#8 decrypt if necessary & flexi-decode

   @param in            Pointer to the ASN.1 encoded input data
   @param inlen         Length of the input data
   @param pwd           Pointer to the password that was used when encrypting
   @param pwdlen        Length of the password
   @param decoded_list  Pointer to a pointer for the flexi-decoded list
   @return CRYPT_OK on success
*/
int pkcs8_decode_flexi(const unsigned char  *in,  unsigned long inlen,
                       const password_ctx   *pw_ctx,
                             ltc_asn1_list **decoded_list)
{
   unsigned long len = inlen;
   unsigned long dec_size;
   unsigned char *dec_data = NULL;
   ltc_asn1_list *l = NULL;
   int err;
   pbes_arg pbes;

   LTC_ARGCHK(in           != NULL);
   LTC_ARGCHK(decoded_list != NULL);

   XMEMSET(&pbes, 0, sizeof(pbes));

   *decoded_list = NULL;
   if ((err = der_decode_sequence_flexi(in, &len, &l)) == CRYPT_OK) {
      /* the following "if" detects whether it is encrypted or not */
      /* PKCS8 Setup
       *  0:d=0  hl=4 l= 380 cons: SEQUENCE
       *  4:d=1  hl=2 l=  78 cons:   SEQUENCE
       *  6:d=2  hl=2 l=   9 prim:     OBJECT             :OID indicating PBES1 or PBES2 (== *lalgoid)
       * 17:d=2  hl=2 l=  65 cons:     SEQUENCE
       *     Stuff in between is dependent on whether it's PBES1 or PBES2
       * 84:d=1  hl=4 l= 296 prim:   OCTET STRING         :bytes (== encrypted data)
       */
      if (l->type == LTC_ASN1_SEQUENCE &&
          LTC_ASN1_IS_TYPE(l->child, LTC_ASN1_SEQUENCE) &&
          LTC_ASN1_IS_TYPE(l->child->child, LTC_ASN1_OBJECT_IDENTIFIER) &&
          LTC_ASN1_IS_TYPE(l->child->child->next, LTC_ASN1_SEQUENCE) &&
          LTC_ASN1_IS_TYPE(l->child->next, LTC_ASN1_OCTET_STRING)) {
         ltc_asn1_list *lalgoid = l->child->child;

         if ((pw_ctx == NULL) || (pw_ctx->callback == NULL)) {
            err = CRYPT_PW_CTX_MISSING;
            goto LBL_DONE;
         }

         if (pbes1_extract(lalgoid, &pbes) == CRYPT_OK) {
            /* Successfully extracted PBES1 parameters */
         } else if (pbes2_extract(lalgoid, &pbes) == CRYPT_OK) {
            /* Successfully extracted PBES2 parameters */
         } else {
            /* unsupported encryption */
            err = CRYPT_INVALID_PACKET;
            goto LBL_DONE;
         }

         if (pw_ctx->callback(&pbes.pw.pw, &pbes.pw.l, pw_ctx->userdata)) {
            err = CRYPT_ERROR;
            goto LBL_DONE;
         }

         pbes.enc_data = l->child->next;

         dec_size = pbes.enc_data->size;
         if ((dec_data = XMALLOC(dec_size)) == NULL) {
            err = CRYPT_MEM;
            goto LBL_DONE;
         }

         if ((err = pbes_decrypt(&pbes, dec_data, &dec_size)) != CRYPT_OK) goto LBL_DONE;

         der_free_sequence_flexi(l);
         l = NULL;
         err = der_decode_sequence_flexi(dec_data, &dec_size, &l);
         if (err != CRYPT_OK) goto LBL_DONE;
         *decoded_list = l;
      }
      else {
         /* not encrypted */
         err = CRYPT_OK;
         *decoded_list = l;
      }
      /* Set l to NULL so it won't be free'd */
      l = NULL;
   }

LBL_DONE:
   if (dec_data) {
      zeromem(dec_data, dec_size);
      XFREE(dec_data);
   }
   password_free(&pbes.pw, pw_ctx);
   if (l) der_free_sequence_flexi(l);
   return err;
}

#endif
