/*
 * Copyright 2010 Trident Microsystems (Far East) Ltd. All rights reserved.
 * All prior copyright rights in this work and the accompanying software
 * products transferred to Trident Microsystems (Far East) Ltd. by written
 * agreement.
 */
/*****************************************************************************/
/* Copyright (c) 2010 NXP B.V.                                               */
/*                                                                           */
/* 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, using version 2 of the License.             */
/*                                                                           */
/* 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, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20071115    amccurdy    Original created
  2 20080215    batelaan    Update file copyright/license text
  3 20090225    amccurdy    Update EXP_DEFAULT from 3 -> 17
--------------------------------------------------------------------------------
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <getopt.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>

#include <openssl/bn.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>

#include "aimage.h"
#include "abn.h"


#define EXP_DEFAULT		(17)

#define RSA_MOD_BITS_1024	(1024)

#ifndef REVISION_STRING
#define REVISION_STRING 	"UNKNOWN_REVISION"
#endif

#define APPNAME 		"aimage_generate_rsa_key"


/*******************************************************************************
*******************************************************************************/

static void syntax (void)
{
	char spacer[32];
	int appname_length = strlen (APPNAME);

	if (appname_length >= sizeof(spacer))
		appname_length = (sizeof(spacer) - 1);

	memset (spacer, 0, sizeof(spacer));
	memset (spacer, ' ', appname_length);

	fprintf (stderr, "\n");
	fprintf (stderr, "Usage: %s --exponent <integer> (default %d)\n",     APPNAME, EXP_DEFAULT);
	fprintf (stderr, "       %s [output file]        (default stdout)\n", spacer);
	fprintf (stderr,"\n");

	exit (1);
}

int main (int argc, char *argv[])
{
	int i;
	FILE *outfile;
	char *output_filename = NULL;
	char *exp_string = NULL;
	unsigned int exp = 0;
	int option_index = 0;
	unsigned int mod_bits = RSA_MOD_BITS_1024;
	unsigned int private_exponent[RSA_MOD_WORDS_MAX];
	aimage_keypub_t aimage_keypub;
	int openssl_ok; 		/* openssl functions return 0 on error... !! */
	BIGNUM *bn_exp;
	RSA *private_key;

	struct option long_options[] =
	{
		{ "exponent", required_argument, 0, 'e' },
		{ "help",     no_argument,       0, 'h' },
		{ 0, 0, 0, 0 }
	};

	fprintf (stderr, "\n%s: (c) Andre McCurdy, Trident Microsystems. Rev %s (%s)\n\n",
			 APPNAME, REVISION_STRING, __DATE__);

	argv[0] = APPNAME;

	while (1)
	{
		int c = getopt_long (argc, argv, "e:h", long_options, &option_index);	/* ':' -> required, '::' -> optional */

		if (c == -1)
			break;

		switch (c)
		{
			case 0: 		/* option set a flag. no more processing required */
				break;

			case 'e':
				exp_string = optarg;
				break;

			case '?':		/* missing argument */
			case 'h':
			default:
				syntax();
				break;
		}
	}

	if (optind < argc)
		output_filename = argv[optind++];

	if (optind < argc) {
		fprintf (stderr, "Too many options...\n");
		syntax();
	}

	if (exp_string == NULL) {
		exp = EXP_DEFAULT;
	}
	else {
		unsigned int temp;

		if ((sscanf (exp_string, "0x%x", &temp) == 1) ||
		    (sscanf (exp_string,   "%u", &temp) == 1))
		{
			if ((temp == 3) || (temp == 17) || (temp == 65537)) {
				exp = temp;
			}
			else {
				fprintf (stderr, "\nUnsupported short exponent '%u' (expecting 3, 17 or 65537)\n", temp);
				syntax();
			}
		}
		else {
			fprintf (stderr, "\nUnable to parse exponent option '%s' (expecting unsigned integer)\n", exp_string);
			syntax();
		}
	}

//	fprintf (stderr, "exp = %u\n", exp);

	bn_exp = BN_new();
	private_key = RSA_new();
	if ((bn_exp == NULL) || (private_key == NULL)) {
		fprintf (stderr, "\nBN allocation error... aborting\n");
		exit (1);
	}
	openssl_ok = BN_set_word (bn_exp, exp);
	if (openssl_ok == 0) {
		fprintf (stderr, "\nBN_set_word() error... aborting\n");
		exit (1);
	}
retry:
	openssl_ok = RAND_status();
	if (openssl_ok == 0) {
		fprintf (stderr, "\nInsufficient entropy... aborting\n");
		exit (1);
	}
	openssl_ok = RSA_generate_key_ex (private_key, mod_bits, bn_exp, NULL);
	if (openssl_ok == 0) {
		fprintf (stderr, "\nRSA_generate_key_ex() error... aborting\n");
		exit (1);
	}
	memset (&aimage_keypub, 0, sizeof(aimage_keypub));
	aimage_keypub.mod_data[0] = (0 - opensslbn_inverse_mod_two_tothepower_thirtytwo (private_key->n));
	opensslbn_to_abn (&aimage_keypub.mod_data[1], private_key->n, (mod_bits / 32));
	if (aimage_pubkey_mod_bits (&aimage_keypub) != mod_bits)
		goto retry;
	if (aimage_pubkey_exp (&aimage_keypub) != exp)
		goto retry;

#if 0
	{
		unsigned int words = (mod_bits / 32);
		unsigned int bn_temp[RSA_MOD_WORDS_MAX];

		opensslbn_to_abn (bn_temp, private_key->n, words);
		abn_print_nlz_nsp ("modulus: ", bn_temp, NULL, words);

		opensslbn_to_abn (bn_temp, private_key->d, words);
		abn_print_nlz_nsp ("exppriv: ", bn_temp, NULL, words);

		opensslbn_to_abn (bn_temp, private_key->e, words);
		abn_print_nlz_nsp ("exppub:  ", bn_temp, NULL, words);

		exit (0);
	}
#endif

	opensslbn_to_abn (private_exponent, private_key->d, (mod_bits / 32));

	if (output_filename == NULL)
		outfile = stdout;
	else {
		outfile = fopen (output_filename, "w");
		if (outfile == NULL) {
			char ebuf[2048];
			memset (ebuf, 0, sizeof(ebuf));
			snprintf (ebuf, sizeof(ebuf) - 1, "\nError opening '%s' for writing ", output_filename);
			perror (ebuf);
			fprintf (stderr, "\n");
			exit (1);
		}
	}

	fprintf (outfile, "\n");
	fprintf (outfile, "#include \"aimage.h\"\n");
	fprintf (outfile, "\n");

	fprintf (outfile, "const aimage_keypub_t aimage_key =\n");
	fprintf (outfile, "{\n");
	fprintf (outfile, "\t{\n");
	fprintf (outfile, "\t\t0x%08x,\n\t\t", aimage_keypub.mod_data[0]);
	for (i = 0; i < (mod_bits / 32); i++) {
		if (i && ((i % 8) == 0))
			fprintf (outfile, "\n\t\t");
		fprintf (outfile, "0x%08x,", aimage_keypub.mod_data[i + 1]);
	}
	fprintf (outfile, "\n\t\t0x%08x\n", 0);
	fprintf (outfile, "\t}\n");
	fprintf (outfile, "};\n\n");

	fprintf (outfile, "#if defined (ENABLE_PRIVATE_KEY)\n");
	fprintf (outfile, "/*\n");
	fprintf (outfile, "    The private exponent is for signing purposes only...\n");
	fprintf (outfile, "    DO NOT INCLUDE THE PRIVATE EXPONENT DATA IN ANY TARGET CODE !!!\n");
	fprintf (outfile, "*/\n");
	fprintf (outfile, "const unsigned int aimage_key_private_exponent[RSA_MOD_WORDS_1024] =\n");
	fprintf (outfile, "{\n\t");
	for (i = 0; i < RSA_MOD_WORDS_1024; i++) {
		if (i && ((i % 8) == 0))
			fprintf (outfile, "\n\t");
		fprintf (outfile, "0x%08x%s", private_exponent[i], (i == (RSA_MOD_WORDS_1024 - 1)) ? "" : ",");
	}
	fprintf (outfile, "\n};\n");
	fprintf (outfile, "#endif\n\n");

	fclose (outfile);

	return 0;
}

