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

#ifdef LTC_PBES

/**
   Decrypt Data encrypted via either PBES1 or PBES2

   @param arg        The according PBES parameters
   @param dec_data   [out] The decrypted data
   @param dec_size   [in/out] The length of the encrypted resp. decrypted data
   @return CRYPT_OK on success
*/
int pbes_decrypt(const pbes_arg  *arg, unsigned char *dec_data, unsigned long *dec_size)
{
   int err, hid, cid;
   unsigned char k[32], *iv;
   unsigned long klen, keylen, dlen;
   long diff;
   symmetric_CBC cbc;

   LTC_ARGCHK(arg           != NULL);
   LTC_ARGCHK(arg->type.kdf != NULL);
   LTC_ARGCHK(dec_data      != NULL);
   LTC_ARGCHK(dec_size      != NULL);

   hid = find_hash(arg->type.h);
   if (hid == -1) return CRYPT_INVALID_HASH;
   cid = find_cipher(arg->type.c);
   if (cid == -1) return CRYPT_INVALID_CIPHER;

   klen = arg->type.keylen;

   /* RC2 special case */
   if (arg->key_bits != 0) {
      /* We can't handle odd lengths of Key Bits */
      if ((arg->key_bits % 8) != 0) return CRYPT_INVALID_KEYSIZE;
      /* Internally we use bytes, not bits */
      klen = arg->key_bits / 8;
   }
   keylen = klen;

   if (arg->iv != NULL) {
      iv = arg->iv->data;
   } else {
      iv = k + klen;
      klen += arg->type.blocklen;
   }

   if (klen > sizeof(k)) return CRYPT_INVALID_ARG;

   if ((err = arg->type.kdf(arg->pwd, arg->pwdlen, arg->salt->data, arg->salt->size, arg->iterations, hid, k, &klen)) != CRYPT_OK) goto LBL_ERROR;
   if ((err = cbc_start(cid, iv, k, keylen, 0, &cbc)) != CRYPT_OK) goto LBL_ERROR;
   if ((err = cbc_decrypt(arg->enc_data->data, dec_data, arg->enc_data->size, &cbc)) != CRYPT_OK) goto LBL_ERROR;
   if ((err = cbc_done(&cbc)) != CRYPT_OK) goto LBL_ERROR;
   dlen = arg->enc_data->size;
   if ((err = padding_depad(dec_data, &dlen, LTC_PAD_PKCS7)) != CRYPT_OK) goto LBL_ERROR;
   diff = (long)arg->enc_data->size - (long)dlen;
   if ((diff <= 0) || (diff > cipher_descriptor[cid]->block_length)) {
      err = CRYPT_PK_INVALID_PADDING;
      goto LBL_ERROR;
   }
   *dec_size = dlen;
   return CRYPT_OK;

LBL_ERROR:
   zeromem(k, sizeof(k));
   zeromem(dec_data, *dec_size);
   return err;
}

#endif
