/* $OpenBSD: keynote-keygen.c,v 1.21 2004/06/29 11:35:56 msf Exp $ */
/*
 * The author of this code is Angelos D. Keromytis (angelos@dsl.cis.upenn.edu)
 *
 * This code was written by Angelos D. Keromytis in Philadelphia, PA, USA,
 * in April-May 1998
 *
 * Copyright (C) 1998, 1999 by Angelos D. Keromytis.
 *	
 * Permission to use, copy, and modify this software with or without fee
 * is hereby granted, provided that this entire notice is included in
 * all copies of any software which is or includes a copy or
 * modification of this software. 
 *
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTY. IN PARTICULAR, THE AUTHORS MAKES NO
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
 * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
 * PURPOSE.
 */

#include <sys/types.h>
#include <sys/stat.h>

#include <ctype.h>
#include <fcntl.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <openssl/dsa.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>

#include "header.h"
#include "keynote.h"
#include "assertion.h"
#include "signature.h"

void	keygenusage(void);

void
keygenusage(void)
{
    fprintf(stderr, "Arguments:\n");
    fprintf(stderr, "\t<AlgorithmName> <keysize> "
	    "<PublicKeyFile> <PrivateKeyFile> [<print-offset>] "
	    "[<print-length>]\n");
}

/*
 * Print the specified number of spaces.
 */
void
print_space(FILE *fp, int n)
{
    while (n--)
      fprintf(fp, " ");
}

/*
 * Output a key, properly formatted.
 */
void
print_key(FILE *fp, char *algname, char *key, int start, int length)
{
    int i, k;

    print_space(fp, start);
    fprintf(fp, "\"%s", algname);

    for (i = 0, k = strlen(algname) + 2; i < strlen(key); i++, k++)
    {
	if (k == length)
	{
	    if (i == strlen(key))
	    {
		fprintf(fp, "\"\n");
		return;
	    }

	    fprintf(fp, "\\\n");
	    print_space(fp, start);
	    i--;
	    k = 0;
	}
	else
	  fprintf(fp, "%c", key[i]);
    }

    fprintf(fp, "\"\n");
}

void
keynote_keygen(int argc, char *argv[])
{
    int begin = KEY_PRINT_OFFSET, prlen = KEY_PRINT_LENGTH;
    char *foo, *privalgname, seed[SEED_LEN];
    int alg, enc, ienc, len = 0, counter;
    struct keynote_deckey dc;
    unsigned long h;
    DSA *dsa;
    RSA *rsa;
    FILE *fp;
    char *algname;

    if ((argc != 5) && (argc != 6) && (argc != 7))
    {
	keygenusage();
	exit(0);
    }

    /* Fix algorithm name */
    if (argv[1][strlen(argv[1]) - 1] != ':')
    {
	int len = strlen(argv[1]) + 2;

        fprintf(stderr, "Algorithm name [%s] should be terminated with a "
		"colon, fixing.\n", argv[1]);
	algname = (char *) calloc(len, sizeof(char));
	if (algname == (char *) NULL)
	{
	    perror("calloc()");
	    exit(1);
	}

	strlcpy(algname, argv[1], len);
	algname[strlen(algname)] = ':';
    }
    else
	algname = argv[1];

    if (argc > 5)
    {
	begin = atoi(argv[5]);
	if (begin <= -1)
	{
	    fprintf(stderr, "Erroneous value for print-offset parameter.\n");
	    exit(1);
	}
    }

    if (argc > 6)
    {
	prlen = atoi(argv[6]);
	if (prlen <= 0)
	{
	    fprintf(stderr, "Erroneous value for print-length parameter.\n");
	    exit(1);
	}
    }

    if (strlen(algname) + 2 > prlen)
    {
	fprintf(stderr, "Parameter ``print-length'' should be larger "
		"than the length of AlgorithmName (%lu)\n",
		(unsigned long) strlen(algname));
	exit(1);
    }

    alg = keynote_get_key_algorithm(algname, &enc, &ienc);
    len = atoi(argv[2]);

    if (len <= 0)
    {
	fprintf(stderr, "Invalid specified keysize %d\n", len);
	exit(1);
    }

    if ((alg == KEYNOTE_ALGORITHM_DSA) &&
	(ienc == INTERNAL_ENC_ASN1) &&
	((enc == ENCODING_HEX) || (enc == ENCODING_BASE64)))
    {
        RAND_bytes(seed, SEED_LEN);

	dsa = DSA_generate_parameters(len, seed, SEED_LEN, &counter, &h, NULL
				      , NULL);

	if (dsa == (DSA *) NULL)
	{
	    ERR_print_errors_fp(stderr);
	    exit(1);
	}

	if (DSA_generate_key(dsa) != 1)
	{
	    ERR_print_errors_fp(stderr);
	    exit(1);
	}

	dc.dec_algorithm = KEYNOTE_ALGORITHM_DSA;
	dc.dec_key = (void *) dsa;

	foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PUBLIC_KEY);
	if (foo == (char *) NULL)
	{
	    fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno);
	    exit(1);
	}

	if (!strcmp(argv[3], "-"))
	  fp = stdout;
	else
	{
	    fp = fopen(argv[3], "w");
	    if (fp == (FILE *) NULL)
	    {
		perror(argv[3]);
		exit(1);
	    }
	}

	print_key(fp, algname, foo, begin, prlen);
	free(foo);

	if (strcmp(argv[3], "-"))
	  fclose(fp);

	foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PRIVATE_KEY);
	if (foo == (char *) NULL)
	{
	    fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno);
	    exit(1);
	}

	if (!strcmp(argv[4], "-"))
	{
	    fp = stdout;
	    if (!strcmp(argv[3], "-"))
	      printf("===========================\n");
	}
	else
	{
	    fp = fopen(argv[4], "w");
	    if (fp == (FILE *) NULL)
	    {
		perror(argv[4]);
		exit(1);
	    }
	}

	len = strlen(KEYNOTE_PRIVATE_KEY_PREFIX) + strlen(foo) + 1;
	privalgname = (char *) calloc(len, sizeof(char));
	if (privalgname == (char *) NULL)
	{
	    perror("calloc()");
	    exit(1);
	}
	snprintf(privalgname, len, "%s%s", KEYNOTE_PRIVATE_KEY_PREFIX, algname);
	print_key(fp, privalgname, foo, begin, prlen);
	free(privalgname);
	free(foo);

	if (strcmp(argv[4], "-"))
	  fclose(fp);

	exit(0);
    }

    if ((alg == KEYNOTE_ALGORITHM_RSA) &&
	(ienc == INTERNAL_ENC_PKCS1) &&
	((enc == ENCODING_HEX) || (enc == ENCODING_BASE64)))
    {
	rsa = RSA_generate_key(len, DEFAULT_PUBLIC, NULL, NULL);

	if (rsa == (RSA *) NULL)
	{
	    ERR_print_errors_fp(stderr);
	    exit(1);
	}

	dc.dec_algorithm = KEYNOTE_ALGORITHM_RSA;
	dc.dec_key = (void *) rsa;

	foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PUBLIC_KEY);
	if (foo == (char *) NULL)
	{
	    fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno);
	    exit(1);
	}

	if (!strcmp(argv[3], "-"))
	  fp = stdout;
	else
	{
	    fp = fopen(argv[3], "w");
	    if (fp == (FILE *) NULL)
	    {
		perror(argv[3]);
		exit(1);
	    }
	}

	print_key(fp, algname, foo, begin, prlen);
	free(foo);

	if (strcmp(argv[3], "-"))
	  fclose(fp);

	foo = kn_encode_key(&dc, ienc, enc, KEYNOTE_PRIVATE_KEY);
	if (foo == (char *) NULL)
	{
	    fprintf(stderr, "Error encoding key (errno %d)\n", keynote_errno);
	    exit(1);
	}

	if (!strcmp(argv[4], "-"))
	{
	    fp = stdout;
	    if (!strcmp(argv[3], "-"))
	      printf("===========================\n");
	}
	else
	{
	    fp = fopen(argv[4], "w");
	    if (fp == (FILE *) NULL)
	    {
		perror(argv[4]);
		exit(1);
	    }
	}

	len = strlen(KEYNOTE_PRIVATE_KEY_PREFIX) + strlen(foo) + 1;
	privalgname = (char *) calloc(len, sizeof(char));
	if (privalgname == (char *) NULL)
	{
	    perror("calloc()");
	    exit(1);
	}
	snprintf(privalgname, len, "%s%s", KEYNOTE_PRIVATE_KEY_PREFIX, algname);
	print_key(fp, privalgname, foo, begin, prlen);
	free(privalgname);
	free(foo);

	if (strcmp(argv[4], "-"))
	  fclose(fp);

	exit(0);
    }

    /* More algorithms here */

    fprintf(stderr, "Unknown/unsupported algorithm [%s]\n", algname);
    exit(1);
}