#include <mp.h>
#include <fcntl.h>
#include <stdio.h>

#ifdef __GNU_MP__
#define invert(a, b, c) my_invert(a, b, c)
#endif

#define SIZE 32

#define HALF 16

rand_raw(str, num)
unsigned char *str;
int num;
{
    int i;

    for (i = 0; i < num - 1; i++)
	str[i] = random() & 0xFF;

    /* force it to be num digits long */
    str[i] = 0;
    while (! str[i])
	str[i] = random() & 0xFF;
    i++;

    for (; i < SIZE; i++)
	str[i] = 0;
}

raw_to_num(out, in)
MINT *out;
unsigned char *in;
{
    int i;
    MINT *temp, *twofiftysix, *thisval;

    temp = itom(0);
    mult(temp, temp, out); /* IckyIcky how do you zero in libmp? */
    twofiftysix = itom(256);

    for (i = 0; i < SIZE; i++)
    {
        thisval = itom(in[SIZE - i - 1]);
	mult(temp, twofiftysix, temp);
	madd(temp, thisval, temp);
	mfree(thisval);
    }
    madd(temp, out, out); /* There is no copy function! */
    mfree(temp);
    mfree(twofiftysix);
}
 
num_to_raw(out, in)  /* Destroys in */
unsigned char *out;
MINT *in;
{
    int i;
    short temp;
    MINT *zero;

    zero = itom(0);

    for (i = 0; i < SIZE; i++)
    {
        if (!mcmp (in, zero))
          temp = 0;
        else
	  sdiv(in, 256, in, &temp);
        out[i] = temp & 0xFF;
    }
    mfree(zero);
}

is_prime(n)
MINT *n;
{
    int i;
    short j;
    MINT *temp1, *temp2;

    temp2 = itom(0);

    for (i = 0; i < 25; i++) /* 25 is quite a lot, actually */
    {
	/* 
	 * We should choose a number that is between 0 and n, but this
	 * works well enough.
	 */
	j = (short) random();
	if (j < 0) j = (-j);
	temp1 = itom(j);
	pow(temp1, n, n, temp2);
	if (mcmp(temp1, temp2))
	{
	    mfree(temp1);
	    mfree(temp2);
	    return (0);
	}
	mfree(temp1);
	mfree(temp2);
    }
    return (1);
}

my_invert(a, b, c)
/* compute 1/a mod b and store in c */
MINT *a, *b, *c;
{	MINT *x, *y, *z, *w, *u, *v, *zero;
	int i = 0;
	zero = itom(0);
	x = itom(0);
	y = itom(0);
	z = itom(0);
	w = itom(0);
	u = itom(0);
	v = itom(1);
	madd(b, zero, x);  /*silly, but gnu doesn't provide a copy*/
	madd(a, zero, y);
	while(mcmp(y,zero))
	{	mdiv(x, y, w, z);
		madd(u, zero, x);
		mult(w, u, u);
		madd(u, v, u);
		madd(x, zero, v);
		madd(y, zero, x);
		madd(z, zero, y);
		i++;
	}
	madd(v, zero, c);
	if( (i&01) == 0)
          msub(b, c, c);
	mfree(x);
	mfree(y);
	mfree(z);
	mfree(w);
	mfree(u);
	mfree(v);
	mfree(zero);
}


main()
{
    MINT *x, *y, *global, *xyminus1, *private, *public;
    MINT *one, *two;
    unsigned char temp[SIZE];
    int keyfile; /* keyfile is hard data */
    FILE *cfile; /* cfile is suitable for including in a program */
    int i;


    srandom(time(0) ^ (getpid()<<16));

    one = itom(1);
    two = itom(2);
    x = itom(0);
    y = itom(0);
    global = itom(0);
    private = itom(0);
    public = itom(0);
    xyminus1 = itom(0);

    rand_raw(temp, HALF);
    temp[0] |= 1; /* force odd */
    raw_to_num(x, temp);
    while (! is_prime(x))
	madd(x, two, x);

    rand_raw(temp, HALF);
    temp[0] |= 1; /* force odd */
    raw_to_num(y, temp);
    while (! is_prime(y))
	madd(y, two, y);
    
    mult(x, y, global);

    msub(x, one, x);
    msub(y, one, y);
    mult(x, y, xyminus1);

    rand_raw(temp, HALF + 1);
    temp[0] |= 1; /* force odd */
    raw_to_num(private, temp);
    while (! is_prime(private))
	madd(private, two, private);

    invert(private, xyminus1, public);

    keyfile = open("keys.dat", O_WRONLY | O_CREAT, 0666);
    cfile = fopen("keys.h", "w");

    num_to_raw(temp, global);
    write(keyfile, temp, SIZE);
    fprintf(cfile, "static unsigned char key_global[] =\n{\n");
    for (i = 0; i < SIZE; i++)
	fprintf(cfile, "    %d,\n", (int) temp[i]);
    fprintf(cfile, "};\n\n");

    num_to_raw(temp, public);
    write(keyfile, temp, SIZE);
    fprintf(cfile, "static unsigned char key_public[] =\n{\n");
    for (i = 0; i < SIZE; i++)
	fprintf(cfile, "    %d,\n", (int) temp[i]);
    fprintf(cfile, "};\n\n");

    num_to_raw(temp, private);
    fprintf(cfile, "static unsigned char key_private[] =\n{\n");
    for (i = 0; i < SIZE; i++)
	fprintf(cfile, "    %d,\n", (int) temp[i]);
    fprintf(cfile, "};\n\n");

    close(keyfile);
    fclose(cfile);
    mfree(one);
    mfree(two);
    mfree(x);
    mfree(y);
    mfree(global);
    mfree(public);
    mfree(private);
    mfree(xyminus1);
}
