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

/**
  @file rsa_decrypt_key.c
  RSA PKCS #1 Decryption, Tom St Denis and Andreas Lange
*/

#ifdef LTC_MRSA
/**
   Decrypt then (PKCS #1 v2.0) OAEP or (PKCS #1 v1.5) EME depad
   @param in          The ciphertext
   @param inlen       The length of the ciphertext (octets)
   @param out         [out] The plaintext
   @param outlen      [in/out] The max size and resulting size of the plaintext (octets)
   @param params      The RSA operation's parameters
   @param stat        [out] Result of the decryption, 1==valid, 0==invalid
   @param key         The corresponding private RSA key
   @return CRYPT_OK if succcessul (even if invalid)
*/
int rsa_decrypt_key_v2(const unsigned char *in,             unsigned long  inlen,
                             unsigned char *out,            unsigned long *outlen,
                     ltc_rsa_op_parameters *params,
                             int           *stat,     const rsa_key       *key)
{
  int           err;
  unsigned char zero, one;
  unsigned char *tmp;
  unsigned long modulus_bitlen, modulus_bytelen, x;
  ltc_rsa_op_checked op_checked = ltc_rsa_op_checked_init(key, params);

  /* stat is cleared before anything else can return, it stays 0 unless the padding is valid */
  LTC_ARGCHK(stat   != NULL);
  *stat = 0;

  LTC_ARGCHK(in     != NULL);
  LTC_ARGCHK(out    != NULL);
  LTC_ARGCHK(outlen != NULL);

  /* valid padding? */
  if ((err = rsa_key_valid_op(LTC_RSA_DECRYPT, &op_checked)) != CRYPT_OK) {
    return err;
  }

  /* get modulus len in bits */
  modulus_bitlen = ltc_mp_count_bits( (key->N));

  /* outlen must be at least the size of the modulus */
  modulus_bytelen = ltc_mp_unsigned_bin_size( (key->N));
  if (modulus_bytelen != inlen) {
     return CRYPT_INVALID_PACKET;
  }

  /* SP 800-56B Rev. 2 Section 7.1.2.1 says to reject ciphertext values 0 and 1 */
  if (params->padding == LTC_PKCS_1_OAEP) {
     zero = one = 0;
     for (x = 0; x < inlen; ++x) {
        zero |= in[x];
        if (x == inlen - 1) {
           one |= (unsigned char)(in[x] ^ 0x01);
        } else {
           one |= in[x];
        }
     }
     if (zero == 0 || one == 0) return CRYPT_INVALID_PACKET;
  }

  /* allocate ram */
  tmp = XMALLOC(inlen);
  if (tmp == NULL) {
     return CRYPT_MEM;
  }

  /* rsa exptmod the packet */
  x = inlen;
  if ((err = ltc_mp.rsa_me(in, inlen, tmp, &x, PK_PRIVATE, key)) != CRYPT_OK) {
     XFREE(tmp);
     return err;
  }

  if (params->padding == LTC_PKCS_1_OAEP) {
    /* now OAEP depad the packet */
    err = ltc_pkcs_1_oaep_decode(tmp, x, params, modulus_bitlen, out, outlen, stat);
  } else {
    /* now PKCS #1 v1.5 depad the packet */
    err = ltc_pkcs_1_v1_5_decode(tmp, x, LTC_PKCS_1_EME, modulus_bitlen, out, outlen, stat);
  }

  XFREE(tmp);
  return err;
}

#endif /* LTC_MRSA */