/* demos/spkigen.c
   * 18-Mar-1997 - eay - A quick hack :-)
   * version 1.1, it would probably help to save or load the
   * private key.
 */
#include <stdio.h>
#include <stdlib.h>
#include "err.h"
#include "asn1.h"
#include "objects.h"
#include "envelope.h"
#include "x509.h"
#include "pem.h"

/* The following two don't exist in SSLeay but they are in here as
   * examples */
#define PEM_write_SPKI(fp,x) \
PEM_ASN1_write((int (*)())i2d_NETSCAPE_SPKI,"SPKI",fp,\
(char *)x,NULL,NULL,0,NULL)
int SPKI_set_pubkey(NETSCAPE_SPKI * x, EVP_PKEY * pkey);

/* These are defined in the next version of SSLeay */
int EVP_PKEY_assign(EVP_PKEY * pkey, int type, char *key);
#define RSA_F4 0x10001
#define EVP_PKEY_assign_RSA(pkey,rsa) EVP_PKEY_assign((pkey),EVP_PKEY_RSA,\
(char *)(rsa))

int main(argc, argv)
int argc;
char *argv[];
{
    RSA *rsa = NULL;
    NETSCAPE_SPKI *spki = NULL;
    EVP_PKEY *pkey = NULL;
    char buf[128];
    int ok = 0, i;
    FILE *fp;

    pkey = EVP_PKEY_new();

    if (argc < 2) {
/* Generate an RSA key, the random state should have been seeded
   * with lots of calls to RAND_seed(....) */
	fprintf(stderr, "generating RSA key, could take some time...\n");
	if ((rsa = RSA_generate_key(512, RSA_F4, NULL)) == NULL)
	    goto err;
    } else {
	if ((fp = fopen(argv[1], "r")) == NULL) {
	    perror(argv[1]);
	    goto err;
	}
	if ((rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL)) == NULL)
	    goto err;
	fclose(fp);
    }

    if (!EVP_PKEY_assign_RSA(pkey, rsa))
	goto err;
    rsa = NULL;

/* lets make the spki and set the public key and challenge */
    if ((spki = NETSCAPE_SPKI_new()) == NULL)
	goto err;

    if (!SPKI_set_pubkey(spki, pkey))
	goto err;

    fprintf(stderr, "please enter challenge string:");
    fflush(stderr);
    fgets(buf, 120, stdin);
    i = strlen(buf);
    if (i > 0)
	buf[--i] = '\0';
    if (!ASN1_STRING_set((ASN1_STRING *) spki->spkac->challenge,
			 buf, i))
	goto err;

    if (!NETSCAPE_SPKI_sign(spki, pkey, EVP_md5()))
	goto err;
    PEM_write_SPKI(stdout, spki);
    if (argc < 2)
	PEM_write_RSAPrivateKey(stdout, pkey->pkey.rsa, NULL, NULL, 0, NULL);

    ok = 1;
  err:
    if (!ok) {
	fprintf(stderr, "something bad happened....");
	ERR_print_errors_fp(stderr);
    }
    NETSCAPE_SPKI_free(spki);
    EVP_PKEY_free(pkey);
    exit(!ok);
}

/* This function is in the next version of SSLeay */
int EVP_PKEY_assign(pkey, type, key)
EVP_PKEY *pkey;
int type;
char *key;
{
    if (pkey == NULL)
	return (0);
    if (pkey->pkey.ptr != NULL) {
	if (pkey->type == EVP_PKEY_RSA)
	    RSA_free(pkey->pkey.rsa);
/* else memory leak */
    }
    pkey->type = type;
    pkey->pkey.ptr = key;
    return (1);
}

/* While I have a
   * X509_set_pubkey() and X509_REQ_set_pubkey(), SPKI_set_pubkey() does
   * not currently exist so here is a version of it.
   * The next SSLeay release will probably have
   * X509_set_pubkey(),
   * X509_REQ_set_pubkey() and
   * NETSCAPE_SPKI_set_pubkey()
   * as macros calling the same function */
int SPKI_set_pubkey(x, pkey)
NETSCAPE_SPKI *x;
EVP_PKEY *pkey;
{
    int ok = 0;
    X509_PUBKEY *pk;
    X509_ALGOR *a;
    ASN1_OBJECT *o;
    unsigned char *s, *p;
    int i;

    if (x == NULL)
	return (0);

    if ((pk = X509_PUBKEY_new()) == NULL)
	goto err;
    a = pk->algor;

/* set the algorithm id */
    if ((o = OBJ_nid2obj(pkey->type)) == NULL)
	goto err;
    ASN1_OBJECT_free(a->algorithm);
    a->algorithm = o;

/* Set the parameter list */
    if ((a->parameter == NULL) || (a->parameter->type != V_ASN1_NULL)) {
	ASN1_TYPE_free(a->parameter);
	a->parameter = ASN1_TYPE_new();
	a->parameter->type = V_ASN1_NULL;
    }
    i = i2d_PublicKey(pkey, NULL);
    if ((s = (unsigned char *) malloc(i + 1)) == NULL)
	goto err;
    p = s;
    i2d_PublicKey(pkey, &p);
    if (!ASN1_BIT_STRING_set(pk->public_key, s, i))
	goto err;
    free(s);

    X509_PUBKEY_free(x->spkac->pubkey);
    x->spkac->pubkey = pk;
    pk = NULL;
    ok = 1;
  err:
    if (pk != NULL)
	X509_PUBKEY_free(pk);
    return (ok);
}
