/* 
   babeld-lor
    Copyright (C) 2017  Rodrigo Garcia

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

#include "decrypt.h"

/**
 * \brief Initializes rsa module by reading a public key file from
 *        a file. Return 0 if success, -1 otherwise.
 */
int rsa_module_init(char public_key_file[], 
		    mbedtls_rsa_context *rsa_context,
		    mbedtls_entropy_context *rsa_entropy,
		    mbedtls_ctr_drbg_context *rsa_ctr_drbg)
{
  FILE *f;
  int return_val;//, exit_val;
  const char *pers = "rsa_decrypt";

  memset(rsa_result, 0, sizeof( rsa_result ) );
  //exit_val = MBEDTLS_EXIT_SUCCESS;

  mbedtls_rsa_init( rsa_context, MBEDTLS_RSA_PKCS_V15, 0 );
  mbedtls_ctr_drbg_init( rsa_ctr_drbg );
  mbedtls_entropy_init( rsa_entropy );

  return_val = mbedtls_ctr_drbg_seed( rsa_ctr_drbg, mbedtls_entropy_func,
				      rsa_entropy, (const unsigned char *) pers,
				      strlen( pers ) );

  if( return_val != 0 )
    {
      //exit_val = MBEDTLS_EXIT_FAILURE;
      mbedtls_printf( " failed\n  ! mbedtls_ctr_drbg_seed returned %d\n",
		      return_val ); // TODO: change by perror
      goto exit;
    }

  if( ( f = fopen( public_key_file, "rb" ) ) == NULL )
    {
      //exit_val = MBEDTLS_EXIT_FAILURE;
      // TODO: change by perror
      mbedtls_printf( " failed\n  ! Could not open rsa_pub.txt\n" );

      goto exit;
    }

  if( ( return_val = mbedtls_mpi_read_file( &rsa_context->N , 16, f ) ) != 0 ||
      ( return_val = mbedtls_mpi_read_file( &rsa_context->E , 16, f ) ) != 0 )
    {
      //exit_val = MBEDTLS_EXIT_FAILURE;
      mbedtls_printf( " failed\n  ! mbedtls_mpi_read_file returned %d\n\n",
		      return_val ); // TODO: change by perror
      fclose( f );
      goto exit;
    }

  rsa_context->len = ( mbedtls_mpi_bitlen( &rsa_context->N ) + 7 ) >> 3;

  fclose( f );

exit:
  //mbedtls_ctr_drbg_free( &ctr_drbg );
  //mbedtls_entropy_free( &entropy );
  //mbedtls_rsa_free( &rsa );
  return return_val;

#if defined(_WIN32)
  //mbedtls_printf( "  + Press Enter to exit this program.\n" );
  fflush( stdout ); getchar();
#endif
}

/**
 * \brief 
 * Decrypts the given string using initialized rsa_context, rsa_entropy, 
 * rsa_ctr_drbg structures, stores the decrypted message in rsa_result string.
 * 
 * Note: context structures must have been initialized using rsa_module_init()
 * 
 * \return  0 if success
 * 
 * Note: The expected string to dechiper is like:
 * 85E986F19D9678A03C23435B7A27B455AB...
 * There is no blank spaces nor special characters within the string, this is
 * done to reduce the message being sent as a TLV.
 */
int rsa_decrypt(mbedtls_rsa_context *rsa_context,
		mbedtls_entropy_context *rsa_entropy,
		mbedtls_ctr_drbg_context *rsa_ctr_drbg,
		char *encrypted_message,
		unsigned char *rsa_result)
{
  size_t i;
  int c;

  i = 0;
  while ( sscanf(encrypted_message, "%02X", &c) > 0 && 
	  i < (int) sizeof(rsa_buff))
    {
      //printf(" %x=%d",c, (unsigned char)c);
      rsa_buff[i++] = (unsigned char) c;

      // jumps to next integer pair
      encrypted_message+=2;
    }

  if (i != rsa_context->len)
    {
      // TODO: change by perror 
      printf("\nInvalid RSA signature format\n");
      return 1;
    }

    /*
     * Decrypt the encrypted RSA data.
     */
  fflush( stdout );

  int return_val = mbedtls_rsa_pkcs1_decrypt( rsa_context, 
					      mbedtls_ctr_drbg_random,
					      rsa_ctr_drbg, MBEDTLS_RSA_PUBLIC, &i,
					      rsa_buff, rsa_result, 1024 );
  if( return_val != 0 )
    {
      //exit_val = MBEDTLS_EXIT_FAILURE;

      //TODO: change by perror
      printf ("\tfailed\n  ! mbedtls_rsa_pkcs1_decrypt (using public key) returned %d\n\n",
	      return_val );
      return 1;
    }

  //mbedtls_printf( "\n  . OK\n\n" );
  //mbedtls_printf( "The decrypted result is: '%s'\n\n", result );

  return 0;
}
