/*
 * pgpAltRSAGlu.c - Interface to RSAREF Cryptographic toolkit from
 * RSA Data Security, Inc.
 *
 * This is an alternative to pgpRSAGlue.c, and should be used if the
 * use of the RSAREF library is required for legal reasons.
 *
 * This file uses RSAREF to perform the actual encryption and decryption.
 * It must be linked with the RSAREF library (rsaref.a, rsaref.lib,
 * or whatever it's called on your system) to function.
 *
 * This code only accepts PKCS-style padding.  Very old versions of PGP
 * used a different padding style, which will not be compatible with
 * this module.
 *
 * $Id: pgpAltRSAGlu.c,v 1.9 1997/06/25 17:59:04 lloyd Exp $
 */
 
/*
 * An alternative version of this module is used if RSAREF is NOT needed.
 * This entire source file is under the control of the following conditional:
 */
#ifdef USERSAREF	/* [ */


#include "pgpConfig.h"

#include "pgpDebug.h"
#include "pgpRSAGlue.h"
#include "pgpKeyMisc.h"
#include "bn.h"
#include "pgpError.h"
#include "pgpRndom.h"
#include "pgpUsuals.h"


/*
 * If you're using a legally encumbered library this will be
 * printed in the program banner.
 */
char const banner_legalese[] = "\
Uses the RSAREF(tm) Toolkit, which is copyright RSA Data Security, Inc.\n\
Distributed by the Massachusetts Institute of Technology.\n";

/* Include files from RSAREF */
#include <global.h>
#include <rsaref.h>
#include <rsa.h>

/*
 * The functions we call in rsa.h are:
 *  RSAPublicEncrypt
 *  RSAPrivateEncrypt
 *  RSAPublicDecrypt
 *  RSAPrivateDecrypt
 */


/*
 * This returns TRUE if the key is too big, returning the
 * maximum number of bits that the library can accept.
 */
int
rsaKeyTooBig(RSApub const *pub, RSAsec const *sec)
{
	unsigned maxsize = MAX_RSA_MODULUS_BITS;
	if (pub) {
		if (bnBits(&pub->n) > maxsize)
			return maxsize;
		if (bnBits(&pub->e) > maxsize)
			return maxsize;
	}
	if (sec) {
		if (bnBits(&sec->d) > maxsize)
			return maxsize;
		if (bnBits(&sec->p) > MAX_RSA_PRIME_BITS)
			return maxsize;
		if (bnBits(&sec->q) > MAX_RSA_PRIME_BITS)
			return maxsize;
		if (bnBits(&sec->u) > MAX_RSA_PRIME_BITS)
			return maxsize;
	}
	/* Else OK */
	return 0;
}

/* Initialize RSAREF pubkey structure from a RSApub. */
static void
rpubk_init(R_RSA_PUBLIC_KEY *rpubk, RSApub const *pub)
{
	memset(rpubk, 0, sizeof (*rpubk));
	rpubk->bits = bnBits(&pub->n);
	bnExtractBigBytes(&pub->n, rpubk->modulus, 0, MAX_RSA_MODULUS_LEN);
	bnExtractBigBytes(&pub->e, rpubk->exponent, 0, MAX_RSA_MODULUS_LEN);
}

/* Initialize RSAREF privkey structure from a RSAsec. */
static void
rprivk_init(R_RSA_PRIVATE_KEY *rprivk, RSAsec const *sec)
{
	BigNum dmodp, dmodq, tmp;

	memset(rprivk, 0, sizeof (*rprivk));

	/* Calculate d mod p-1 and d mod q-1 */
	bnBegin(&dmodp);
	bnBegin(&dmodq);
	bnBegin(&tmp);
	bnCopy(&tmp, &sec->p);
	bnSubQ(&tmp, 1);
	bnMod(&dmodp, &sec->d, &tmp);
	bnCopy(&tmp, &sec->q);
	bnSubQ(&tmp, 1);
	bnMod(&dmodq, &sec->d, &tmp);

	/* Fill in structure */
	rprivk->bits = bnBits(&sec->n);
	bnExtractBigBytes(&sec->n, rprivk->modulus, 0, MAX_RSA_MODULUS_LEN);
	bnExtractBigBytes(&sec->e, rprivk->publicExponent, 0,
			  MAX_RSA_MODULUS_LEN);
	bnExtractBigBytes(&sec->d, rprivk->exponent, 0, MAX_RSA_MODULUS_LEN);
	/* RSAREF uses convention p > q, the opposite of ours */
	bnExtractBigBytes(&sec->q, rprivk->prime[0], 0, MAX_RSA_PRIME_LEN);
	bnExtractBigBytes(&sec->p, rprivk->prime[1], 0, MAX_RSA_PRIME_LEN);
	bnExtractBigBytes(&dmodq, rprivk->primeExponent[0], 0,
			  MAX_RSA_PRIME_LEN);
	bnExtractBigBytes(&dmodp, rprivk->primeExponent[1], 0,
			  MAX_RSA_PRIME_LEN);
	bnExtractBigBytes(&sec->u, rprivk->coefficient, 0, MAX_RSA_PRIME_LEN);

	bnEnd(&dmodp);
	bnEnd(&dmodq);
	bnEnd(&tmp);
}

/*
 * Encrypt a buffer holding a session key with an RSA public key
 */
int
rsaPublicEncrypt(BigNum *bn, PGPByte const *in, unsigned len,
	RSApub const *pub, PGPRandomContext const *rc)
{
	R_RSA_PUBLIC_KEY rpubk;
	R_RANDOM_STRUCT rrs;
	PGPByte buf[MAX_RSA_MODULUS_LEN];
	unsigned int bufsize;
	int err;

	/* Set the return number to 0 to start */
	(void)bnSetQ(bn, 0);

	/* Initialize RSAREF public key structure */
	rpubk_init(&rpubk, pub);

	/* Initialize RSAREF random numbers */
	R_RandomInit(&rrs);
	do {
		R_GetRandomBytesNeeded(&bufsize, &rrs);
		if (bufsize > sizeof(buf))
			bufsize = sizeof(buf);
		if (bufsize) {
			pgpRandomGetBytes(rc, buf, bufsize);
			R_RandomUpdate(&rrs, buf, bufsize);
		}
	} while (bufsize);

	/* Pad and encrypt */
	err = RSAPublicEncrypt(buf, &bufsize, (byte *)in, len, &rpubk, &rrs);
	R_RandomFinal(&rrs);

	if (err) {
		memset(buf, 0, sizeof(buf));
		memset(&rpubk, 0, sizeof(rpubk));
		if (err==RE_LEN)
			return kPGPError_PublicKeyTooSmall;
		return kPGPError_Generic;
	}

	/* Return to bn format */
	bnInsertBigBytes(bn, buf, 0, bufsize);
	memset(buf, 0, sizeof(buf));
	memset(&rpubk, 0, sizeof(rpubk));
	
	return 0;
}


/*
 * Do an RSA signing operation using the secret key
 */
int
rsaPrivateEncrypt(BigNum *bn, PGPByte const *in, unsigned len,
	RSAsec const *sec)
{
	R_RSA_PRIVATE_KEY rprivk;
	PGPByte buf[MAX_RSA_MODULUS_LEN];
	unsigned int bufsize;
	int err;

	/* SET the return number to 0 to start */
	(void)bnSetQ(bn, 0);

	/* Initialize RSAREF private key structure */
	rprivk_init(&rprivk, sec);

	err = RSAPrivateEncrypt(buf, &bufsize, (byte *)in, len, &rprivk);

	if (err) {
		memset(buf, 0, sizeof(buf));
		memset(&rprivk, 0, sizeof(rprivk));
		if (err==RE_LEN)
			return kPGPError_PublicKeyTooSmall;
		return kPGPError_Generic;
	}

	/* Return to bn format */
	bnInsertBigBytes(bn, buf, 0, bufsize);
	memset(buf, 0, sizeof(buf));
	memset(&rprivk, 0, sizeof(rprivk));
	
	return 0;
}

/*
 * These destroy (actually, replace with a decrypted version) the
 * input bignum bn.
 *
 * Performs an RSA signature check.  Returns a prefix of the unwrapped
 * data in the given buf.  Returns the length of the untruncated
 * data, which may exceed "len". Returns <0 on error.
 */
int
rsaPublicDecrypt(PGPByte *outbuf, unsigned len, BigNum *bn,
	RSApub const *pub)
{
	R_RSA_PUBLIC_KEY rpubk;
	PGPByte buf[MAX_RSA_MODULUS_LEN];
	unsigned int bufsize;
	int err;

	/* Initialize RSAREF public key structure */
	rpubk_init(&rpubk, pub);

	bufsize = (bnBits(bn) + 7) / 8;
	if (bufsize > sizeof(buf))
		bufsize = sizeof(buf);
	bnExtractBigBytes(bn, buf, 0, bufsize);

	err = RSAPublicDecrypt(buf, &bufsize, buf, bufsize, &rpubk);
	if (err) {
		memset(buf, 0, sizeof(buf));
		memset(&rpubk, 0, sizeof(rpubk));
		if (err==RE_LEN)
			return kPGPError_PublicKeyTooSmall;
		return kPGPError_Generic;
	}

	/* Copy result to buffer */
	if (len > bufsize)
		len = bufsize;
	memcpy(outbuf, buf, len);

	memset(buf, 0, sizeof(buf));
	memset(&rpubk, 0, sizeof(rpubk));
	
	return bufsize;
}

/*
 * Performs an RSA decryption.  Returns a prefix of the unwrapped
 * data in the given buf.  Returns the length of the untruncated
 * data, which may exceed "len". Returns <0 on error.
 */
int
rsaPrivateDecrypt(PGPByte *outbuf, unsigned len, BigNum *bn,
	RSAsec const *sec)
{
	R_RSA_PRIVATE_KEY rprivk;
	PGPByte buf[MAX_RSA_MODULUS_LEN];
	unsigned int bufsize;
	int err;

	/* Initialize RSAREF private key structure */
	rprivk_init(&rprivk, sec);

	bufsize = (bnBits(bn) + 7) / 8;
	if (bufsize > sizeof(buf))
		bufsize = sizeof(buf);
	bnExtractBigBytes(bn, buf, 0, bufsize);

	err = RSAPrivateDecrypt(buf, &bufsize, buf, bufsize, &rprivk);

	if (err) {
		memset(buf, 0, sizeof(buf));
		memset(&rprivk, 0, sizeof(rprivk));
		if (err==RE_LEN)
			return kPGPError_PublicKeyTooSmall;
		return kPGPError_Generic;
	}

	/* Copy result to buffer */
	if (len > bufsize)
		len = bufsize;
	memcpy(outbuf, buf, len);

	memset(buf, 0, sizeof(buf));
	memset(&rprivk, 0, sizeof(rprivk));
	
	return bufsize;
}


/*
 * Optional code to replace RSAREF's NN_ModExp with code from the
 * BigNum library, which is much faster.
 * Note that we are still using RSAREF, but we are just using a faster
 * modulo exponentiation routine.
 * If you comment out the following block of code, you get a (much slower)
 * pure RSAREF version.
 */
#ifdef USEMPILIB	/* [ */

typedef PGPUInt32 NN_DIGIT;

/*
 * Within the RSAREF NN module, values are stored in 32 bit units,
 * in little-endian order.  Unfortunately this does not map well onto
 * our bignum format and we have to do some shuffling.
 */
/* Convert an RSAREF NN number to a BigNum */
static void
nntobn(BigNum *bn, NN_DIGIT *nn, unsigned nnlen)
{
	PGPByte buf[MAX_RSA_MODULUS_LEN];
	PGPByte *bp = buf;
	unsigned i;

	pgpAssert (nnlen*4 <= MAX_RSA_MODULUS_LEN);
	for (i=0; i<nnlen; ++i) {
		PGPUInt32 v = nn[i];
		*bp++ = v         & 0xff;
		*bp++ = (v >>  8) & 0xff;
		*bp++ = (v >> 16) & 0xff;
		*bp++ = (v >> 24) & 0xff;
	}
	bnInsertLittleBytes(bn, buf, 0, nnlen*4);
}

/* Convert a BigNum to an RSAREF NN number */
static void
bntonn(BigNum *bn, NN_DIGIT *nn, unsigned nnlen)
{
	PGPByte buf[MAX_RSA_MODULUS_LEN];
	PGPByte *bp = buf;
	unsigned i;

	pgpAssert (nnlen*4 <= MAX_RSA_MODULUS_LEN);
	bnExtractLittleBytes(bn, buf, 0, nnlen*4);
	for (i=0; i<nnlen; ++i) {
		PGPUInt32 v = *bp++;
		v |= (*bp++) << 8;
		v |= (*bp++) << 16;
		v |= (*bp++) << 24;
		nn[i] = v;
	}
}

void
NN_ModExp (NN_DIGIT *a, NN_DIGIT *b, NN_DIGIT *c, unsigned cDigits,
	   NN_DIGIT *d, unsigned dDigits);

/*
 * Calculate b to the c mod d, result in a.
 */
void
NN_ModExp (NN_DIGIT *a, NN_DIGIT *b, NN_DIGIT *c, unsigned cDigits,
	   NN_DIGIT *d, unsigned dDigits)
{
	BigNum bnres, bnbase, bnexp, bnmod;

	bnBegin(&bnres);
	bnBegin(&bnbase);
	bnBegin(&bnexp);
	bnBegin(&bnmod);

	nntobn(&bnbase, b, dDigits);
	nntobn(&bnexp, c, cDigits);
	nntobn(&bnmod, d, dDigits);
	
	bnExpMod(&bnres, &bnbase, &bnexp, &bnmod);

	bntonn(&bnres, a, dDigits);

	bnEnd(&bnres);
	bnEnd(&bnbase);
	bnEnd(&bnexp);
	bnEnd(&bnmod);
}

#endif	/* ] USEMPILIB */

#endif	/* ] USERSAREF */
