/*
 * rsa_clientutil.c is rsa_util.c modified to 1, avoid keeping the
 * private key in contiguous memory, and 2, use the mp library.
 *
 * The idea in 1 is to keep the key in 2 parts, unsigned char* key and
 * int* key_ndx so that to find the n'th digit of key you do key[key_ndx[n]].
 *
 * Originally written by Ray Jones.  Hacks by Sam Shen (sls@aero.org)
 */

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

#define SIZE 64

/****************************************************************************
 * these function convert between the unsigned char form (as used by the
 * netrek RSA scheme) and the MP form.
 */
void raw_to_num(out, in, digits)
     MINT *out;
     unsigned char *in;
     int digits;
{
    int i;
    MINT *temp, *twofiftysix, *thisval;
 
    temp = itom(0);
    twofiftysix = itom(256);
 
    for (i = 0; i < digits; i++)
    {
        thisval = itom(in[digits - 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);
}

void num_to_raw(out, in, digits)  /* Destroys in */
     unsigned char *out;
     MINT *in;
     int digits;
{
    int i;
    short temp;
    MINT *zero;

    zero = itom(0);
 
    for (i = 0; i < digits; i++)
    {
        if (!mcmp(in, zero))  /* gnu can't handle 0/256.. bug */
          temp = 0;
        else
          sdiv(in, 256, in, &temp);
        out[i] = temp & 0xFF;
    }
    mfree (zero);
}

static void mmod(a, b, c)
     MINT* a, *b, *c;
{
    MINT* quotient = itom(0);
    mdiv(a, b, quotient, c);
    mfree(quotient);
}

/*
 * compute (a ^ b) % m
 */
static void expmod(out, a, b, b_ndx, m, digits)
     MINT* out, *a, *m;
     unsigned char *b;
     int *b_ndx;
     int digits;
{
    int temp_b[SIZE];
    int i, done;
    MINT* temp_2;
    MINT* temp_a;
    MINT* temp;
    MINT* zero;

    zero = itom(0);

    for (i = 0; i < digits; i++)
	temp_b[b_ndx[i]] = b[b_ndx[i]];
    for ( ; i < SIZE; i++)
	temp_b[b_ndx[i]] = 0;

    temp = itom(1);
    temp_a = itom(0);
    madd(a, zero, temp_a);
    temp_2 = itom(0);
    
    while (1) {
	done = 1;
	for (i = 0; i < SIZE; i++)
	    if (temp_b[i]) {
		done = 0;
		break;
	    }
	if (done)
	    break;
	if (temp_b[b_ndx[0]] & 0x1) {
	    /* b is odd */
	    mult(temp, temp_a, temp_2);
	    mmod(temp_2, m, temp);
	}
	/* shift_right1(temp_b);
	 */
	for (i = 0; i < SIZE - 1; i++) {
	    temp_b[b_ndx[i]] >>= 1;
	    if (temp_b[b_ndx[i+1]] & 0x1)
		temp_b[b_ndx[i]] += 0x80;
	}
	temp_b[b_ndx[i]] >>= 1;

	mult(temp_a, temp_a, temp_2);
	mmod(temp_2, m, temp_a);
    }
    madd(temp, zero, out);
    mfree(temp);
    mfree(temp_2);
    mfree(temp_a);
    mfree(zero);
}

/****************************************************************************
 * The final product.  Take in a message, a key, and the global N to mod by,
 * and do the actual RSA calculation.  Return the new stuff.
 */

rsa_client_encode(out, message, key, key_ndx, global, digits)
     unsigned char *out, *message, *key, *global;
     int* key_ndx;
     int digits;
{
    MINT* m_result;
    MINT* m_message;
    MINT* m_global;

    m_result = itom(0);
    m_message = itom(0);
    raw_to_num(m_message, message, digits);
    m_global = itom(0);
    raw_to_num(m_global, global, digits);
    expmod(m_result, m_message, key, key_ndx, m_global, digits);
    num_to_raw(out, m_result, digits);
    mfree(m_result);
    mfree(m_message);
    mfree(m_global);
}
