/*
 * RSA implementation just sufficient for ssh client-side
 * initialisation step
 *
 * Rewritten for more speed by Joris van Rantwijk, Jun 1999.
 */


#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "macros.h"
#include "sshbn.h"
#include "xmalloc.h"

/* RSA key structure */

typedef struct {
    unsigned short bits;
    unsigned short bytes;
    Bignum modulus;
    Bignum exponent;
    Bignum private_exponent;
    Bignum p;
    Bignum q;
    Bignum iqmp;
    char *comment;
} RSAKey;

extern Bignum One;

int makekey(unsigned char *data, RSAKey *result,
	    char **keystr, short order)
{
    unsigned char *p = data;
    int i;

    if (result) {
	result->bits = 0;
	for (i = 0; i < 4; i++)
	    result->bits = (result->bits << 8) + *p++;
    } else
	p += 4;

    /*
     * order=0 means exponent then modulus (the keys sent by the
     * server). order=1 means modulus then exponent (the keys
     * stored in a keyfile).
     */

    if (order == 0)
	p += ssh1_read_bignum(p, result ? &result->exponent : NULL);
    if (result)
	result->bytes = (((p[0] << 8) + p[1]) + 7) / 8;
    if (keystr)
	*keystr = p + 2;
    p += ssh1_read_bignum(p, result ? &result->modulus : NULL);
    if (order == 1)
	p += ssh1_read_bignum(p, result ? &result->exponent : NULL);

    return p - data;
}

int makeprivate(unsigned char *data, RSAKey *result)
{
    return ssh1_read_bignum(data, &result->private_exponent);
}

void rsaencrypt(char *data, int length, RSAKey *key)
{
    Bignum b1, b2;
    int i;
    unsigned char *p;

    memmove(data + key->bytes - length, data, length);
    data[0] = 0;
    data[1] = 2;

    for (i = 2; i < key->bytes - length - 1; i++) {
       do {
          data[i] = rand() % 256;
	} while (data[i] == 0);
    }
    data[key->bytes - length - 1] = 0;

    b1 = bignum_from_bytes(data, key->bytes);

    b2 = modpow(b1, key->exponent, key->modulus);

    p = data;
    for (i = key->bytes; i--;) {
	*p++ = bignum_byte(b2, i);
    }

    freebn(b1);
    freebn(b2);
}

Bignum rsadecrypt(Bignum input, RSAKey *key)
{
    Bignum ret;
    ret = modpow(input, key->private_exponent, key->modulus);
    return ret;
}

/*
 * Verify that the public data in an RSA key matches the private
 * data. We also check the private data itself: we ensure that p >
 * q and that iqmp really is the inverse of q mod p.
 */
int rsa_verify(RSAKey *key)
{
    Bignum n, ed, pm1, qm1;
    int cmp;

    /* n must equal pq. */
    n = bigmul(key->p, key->q);
    cmp = bignum_cmp(n, key->modulus);
    freebn(n);
    if (cmp != 0)
	return 0;

    /* e * d must be congruent to 1, modulo (p-1) and modulo (q-1). */
    pm1 = copybn(key->p);
    decbn(pm1);
    ed = modmul(key->exponent, key->private_exponent, pm1);
    freebn(pm1);
    cmp = bignum_cmp(ed, One);
    xfree(ed);
    if (cmp != 0)
	return 0;

    qm1 = copybn(key->q);
    decbn(qm1);
    ed = modmul(key->exponent, key->private_exponent, qm1);
    cmp = bignum_cmp(ed, One);
    xfree(qm1);
    xfree(ed);
    if (cmp != 0)
	return 0;

    /*
     * Ensure p > q.
     */
    if (bignum_cmp(key->p, key->q) <= 0)
	return 0;

    /*
     * Ensure iqmp * q is congruent to 1, modulo p.
     */
    n = modmul(key->iqmp, key->q, key->p);
    cmp = bignum_cmp(n, One);
    xfree(n);
    if (cmp != 0)
	return 0;

    return 1;
}

void freersakey(RSAKey *key)
{
    if (key->modulus)
	freebn(key->modulus);
    if (key->exponent)
	freebn(key->exponent);
    if (key->private_exponent)
	freebn(key->private_exponent);
    if (key->comment)
	xfree(key->comment);
}
