// -*- mode: cpp; mode: fold -*-
// Description								/*{{{*/
/* ######################################################################

   GPG RSAREF module

   This module implements the RSA public key algorithm using the RSAREF
   library from RSA Labratories. It is designed to plug into GNU Privacy
   guard (arounds version 0.9.7).
   
   RSAREF is limited in key size and in the coding of the encrypted data.
   Everything that passes through RSAREF must be coded in PCKS #1. The 
   GPG module interface places no such restriction on the module itself,
   but fortunately it happens to pass PCKS #1 coded data in. It is 
   transparently decoded and recoded as necessary.
   
   This module isn't terribly great as RSAREF has no concept of secure 
   memory so the secret key and the symmetric cipher are written to 
   insecure memory in several places in this code and within the RSAREF
   lib. Memory that is touched by sensitive information is blanked out
   before being freed, so in effect this is as secure as PGP2.x is on 
   unix systems.

   I am a bit unclear on the use of g10m_* functions vs mpi_* functions,
   they may be mixed up improperly in this code..
      
   Compile with:
   gcc -Wall -fpic -shared -o rsaref rsaref.c /usr/lib/rsaref.a

   It is expected that NN_ModExp is removed from the RSAREF library, 
   a version is provided here. All testing was done on Debian GNU/Linxu 2.1

   This module is Copyright (c) 1999 Jason Gunthorpe <jgg@non-us.debian.org>
   and is placed into the public domain, do with it what you will. It
   comes with no warrenty express or implied.
   
   The basic structure of a GPG driver was taken from the RSA.c module
   by Werner Koch (c) 1997, 1998
   Ideas on how to correctly implement the RSAREF calls are from comments
   and code from PGP 2.6.3a by Philip Zimmermann
   
   ##################################################################### */
									/*}}}*/
// Include Files							/*{{{*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <rsaref/global.h>
#include <rsaref/rsa.h>
#include <rsaref/nn.h>
#include <rsaref/rsaref.h>
									/*}}}*/

// Library interface							/*{{{*/
// Return results from the exported functions
#define BAD_ALGO  4
#define BAD_KEY   7
#define BAD_SIGN  8

// 8 bit character type
typedef unsigned char byte;

// Hidden interface to the MPI library
struct mpi_struct {int hidden;};
typedef struct mpi_struct *MPI;

// MPI functions
extern byte *mpi_get_buffer(MPI a,unsigned *nbytes,int *sign );
extern void mpi_set_buffer(MPI a,const byte *buffer,unsigned nbytes,int sign);
extern void m_free(void *p);
extern void mpi_tdiv_r(MPI rem,MPI num,MPI den);
extern void mpi_sub_ui(MPI w,MPI u,unsigned long v);
extern void mpi_normalize(MPI a);

// GPG utility functions
extern void g10_log_fatal(const char *fmt,...);
extern void g10_log_mpidump(const char *text,MPI a);
extern MPI g10m_new(unsigned nbits);
extern MPI g10m_new_secure(unsigned nbits);
extern void g10m_release(MPI a);
extern unsigned g10m_get_nbits(MPI a);
extern unsigned g10m_get_size(MPI a);
extern int g10m_cmp(MPI u,MPI v);
extern void g10m_mul(MPI w,MPI u,MPI v);
extern void g10m_powm(MPI res,MPI base,MPI exp,MPI mod);

// Random functions
extern byte *get_random_bits(unsigned nbits,int level,int secure);

									/*}}}*/

// repack_mpi - Convert a MPI to a RSAREF compatible integer	        /*{{{*/
// ---------------------------------------------------------------------
/* Convert a GPG MPI into a RSAREF MPI. RSAREF uses a fixed length buffer
   with all bits being significant. The MPI value is decoded into a linear
   MSB stream and then shifted to the far right of the RSAREF buffer. It
   is assumed that the input buffer is zeroed. Max is the length of the
   buffer and is used for error checking. */
static unsigned repack_mpi(unsigned char *Out,MPI *In,unsigned Max)
{
   byte *Buffer;
   unsigned Size;
   
   Buffer = mpi_get_buffer(*In,&Size,0);
   if (Size > Max)
   {
      m_free(Buffer);
      g10_log_fatal("Key length is too large for RSAREF");
      return 0;
   }
   memcpy(Out+(Max-Size),Buffer,Size);
   memset(Buffer,0,Size);
   m_free(Buffer);
   return Size;
}
									/*}}}*/
// repack_rsaref_pub - Convert to a RSAREF public key structure		/*{{{*/
// ---------------------------------------------------------------------
/* Converts a GPG array of MPIs representing a public key into the RSAREF
   public key structure. The form of the MPI array is:
     pk[0] = n (modulus;
     pk[1] = e (exponent)
 */
static unsigned repack_rsaref_pub(R_RSA_PUBLIC_KEY *rpk,MPI *pk)
{
   unsigned ModSize;
   
   memset(rpk,0,sizeof(*rpk));
   if ((ModSize = repack_mpi(rpk->modulus,&pk[0],MAX_RSA_MODULUS_LEN)) == 0)
      return 0;
   if (repack_mpi(rpk->exponent,&pk[1],MAX_RSA_MODULUS_LEN) == 0)
      return 0;
   
   rpk->bits = ModSize*8;
   
   return ModSize;
}
									/*}}}*/
// repack_rsaref_sec - Convert to a RSAREF secret key structure		/*{{{*/
// ---------------------------------------------------------------------
/* Converts a GPG array of MPIs representing a secret key into the RSAREF
   secret key structure. It also computes the RSAREF needed DQ and DP 
   variables. The form of the MPI array is:
    sk[0] = n (modulus)
    sk[1] = e (public exponent)
    sk[2] = d (exponent)
    sk[3] = p (prime #1)
    sk[4] = q (prime #2)
    sk[5] = u (Coeffecient)
   dp = (p-1) % d 
   dq = (q-1) % d
 */
static unsigned repack_rsaref_sec(R_RSA_PRIVATE_KEY *rpk,MPI *sk)
{
   unsigned ModSize;
   MPI dq;
   MPI dp;
   
   memset(rpk,0,sizeof(*rpk));
   if ((ModSize = repack_mpi(rpk->modulus,&sk[0],MAX_RSA_MODULUS_LEN)) == 0)
      return 0;
   if (repack_mpi(rpk->publicExponent,&sk[1],MAX_RSA_MODULUS_LEN) == 0)
      return 0;
   if (repack_mpi(rpk->exponent,&sk[2],MAX_RSA_MODULUS_LEN) == 0)
      return 0;
   if (repack_mpi(rpk->prime[0],&sk[4],MAX_RSA_PRIME_LEN) == 0)
      return 0;
   if (repack_mpi(rpk->prime[1],&sk[3],MAX_RSA_PRIME_LEN) == 0)
      return 0;   
   if (repack_mpi(rpk->coefficient,&sk[5],MAX_RSA_PRIME_LEN) == 0)
      return 0;
   
   // Compute dp
   dp = g10m_new(ModSize);
   mpi_sub_ui(dp,sk[3],1);
   mpi_tdiv_r(dp,sk[2],dp);
   
   // Compute dq
   dq = g10m_new(ModSize);
   mpi_sub_ui(dq,sk[4],1);
   mpi_tdiv_r(dq,sk[2],dq);

   if (repack_mpi(rpk->primeExponent[0],&dq,MAX_RSA_PRIME_LEN) == 0)
   {
      m_free(dq);
      m_free(dp);
      return 0;
   }   
   if (repack_mpi(rpk->primeExponent[1],&dp,MAX_RSA_PRIME_LEN) == 0)
   {
      m_free(dq);
      m_free(dp);
      return 0;
   }
   
   m_free(dq);
   m_free(dp);
   
   rpk->bits = ModSize*8;
   return ModSize;
}
									/*}}}*/
// rsaref_remove_pad - Remove padding from a PCKS #1 coded value	/*{{{*/
// ---------------------------------------------------------------------
/* This routine removes the padding values according to PCKS #1, which 
   basically specifies a message of the form
     0T <RND> 0 <DATA>
   T is the type, <RND> is some arbitary non-zero values, 0 is the seperator
   and <DATA> is the payload. Only PCKS types 1 and 2 are used. The leading
   zero here is stripped off because of how GPG encodes MPIs (it is implicitly
   present) For signing purposes the padding is simply 0xFF but in theory
   it could be anything. The returned result is a byte array of the full
   MPI (of size Size) and Off is the index of the start of the payload */
static byte *rsaref_remove_pad(MPI data,unsigned *Size,unsigned *Off)
{
   byte *Buffer;
   unsigned Offset;

   // Get the bit buffer and be sure we have a valid type
   Buffer = mpi_get_buffer(data,Size,0);
   if (Buffer[0] != 2 && Buffer[0] != 1)
   {
      m_free(Buffer);
      return 0;
   }
   
   // Search for the 0
   for (Offset = 0; Offset != *Size && Buffer[Offset] != 0; Offset++);
   if (Buffer[Offset] != 0)
   {
      m_free(Buffer);
      return 0;
   }
   
   // Found it, return the offset and the buffer
   Offset++;
   *Off = Offset;
   return Buffer;
}
									/*}}}*/
// rsaref_add_pad - Add PCKS #1 padding to a datablock			/*{{{*/
// ---------------------------------------------------------------------
/* The input data is padded using 0xFF and placed in the MPI result. This
   is mostly the inverst of rsaref_remove_pad. Type should be 1 or 2 
   and indicates the sort of message this is (?) */
static void rsaref_add_pad(byte *Buffer,unsigned ModSize,unsigned Size,
			   MPI *Result,unsigned Type)
{   
    memmove(Buffer+(ModSize - Size),Buffer,Size);
    memset(Buffer,0xFF,(ModSize - Size));
    Buffer[0] = 0;
    Buffer[1] = Type;
    Buffer[(ModSize - Size-1)] = 0;
    *Result = g10m_new(ModSize);
    mpi_set_buffer(*Result,Buffer,ModSize,0);
}
									/*}}}*/
// NN_ModExp - Binds GPGs ModExp function into RSAlib			/*{{{*/
// ---------------------------------------------------------------------
/* Since the most common use of RSAREF is for PGP2.x it is frequently 
   compiled without NN_ModExp, this function, like the one in PGP, 
   compensates by using GPGs faster version. There surely must be a faster
   way to perform these conversions than this brutal set of transform
   calls :< */
void NN_ModExp(NN_DIGIT *a,NN_DIGIT *b, NN_DIGIT *c,unsigned int cDigits, 
	       NN_DIGIT *d, unsigned int dDigits)
{
   MPI rs,base,exp,mod;
   unsigned Size;
   byte *Buffer;
   byte Scratch[MAX_RSA_MODULUS_LEN];
   
   // Allocate the result space
   rs = g10m_new_secure(dDigits*2);
   
   /* Convert each in turn, using NN_Encode to get a bit string to send
      to mpi_set which then needs to have leading zeros removed */
   base = g10m_new_secure(dDigits);
   NN_Encode(Scratch,MAX_RSA_MODULUS_LEN,b,dDigits);
   mpi_set_buffer(base,Scratch,MAX_RSA_MODULUS_LEN,0);
   mpi_normalize(base);
   
   exp = g10m_new_secure(cDigits);
   NN_Encode(Scratch,MAX_RSA_MODULUS_LEN,c,cDigits);
   mpi_set_buffer(exp,Scratch,MAX_RSA_MODULUS_LEN,0);
   mpi_normalize(exp);
   
   mod = g10m_new_secure(dDigits);
   NN_Encode(Scratch,MAX_RSA_MODULUS_LEN,d,dDigits);
   mpi_set_buffer(mod,Scratch,MAX_RSA_MODULUS_LEN,0);
   mpi_normalize(mod);
   memset(Scratch,0,sizeof(Scratch));
   
   // Do the function
   g10m_powm(rs,base,exp,mod);
   
   // Drop memory
   g10m_release(base);
   g10m_release(exp);
   g10m_release(mod);

   // Convert it back
   Buffer = mpi_get_buffer(rs,&Size,0);
   g10m_release(rs);
   NN_Decode(a,dDigits,Buffer,Size);
   memset(Buffer,0,Size);
   m_free(Buffer);
}
									/*}}}*/

// do_check_secret_key - Verify that the secret key is valid		/*{{{*/
// ---------------------------------------------------------------------
/* Perform a verification step on the key. RSAREF does not provide a routine
   for this and I belive it is not strictly covered by their patent. */
static int do_check_secret_key(int algo, MPI *skey)
{
   int rc;
   MPI temp;
   
   if (algo != 1 && algo != 2 && algo != 3)
      return BAD_ALGO;

   /// p x q = n is what we are checking here
   temp = g10m_new(g10m_get_size(skey[3])*2);
   g10m_mul(temp,skey[3],skey[4]);
   rc = g10m_cmp(temp,skey[0]);
   g10m_release(temp);
   if (rc != 0)
      return BAD_KEY;
   return 0;
}
									/*}}}*/
// do_encrypt - Perform encryption using the public key			/*{{{*/
// ---------------------------------------------------------------------
/* This performs GPGs encrypt function, given a public key in pkey it 
   encrypts it so only the secret key can decode it. Regrettably the
   random padding generated by GPG must be thrown away and we need to use
   RSAREFs RNG, seeded from GPGs. */
static int do_encrypt(int algo,MPI *resarr,MPI data,MPI *pkey)
{
   R_RSA_PUBLIC_KEY rpk;
   R_RANDOM_STRUCT Random;
   byte *Buffer;
   unsigned Size;
   unsigned ModSize;
   unsigned int Off;
   int rc;
   
   if (algo != 1 && algo != 2)
      return BAD_ALGO;
   
   // Convert the public key
   if ((ModSize = repack_rsaref_pub(&rpk,pkey)) == 0)
      return BAD_KEY;
   
   // Remove the coding
   Buffer = rsaref_remove_pad(data,&Size,&Off);
   
   // Init the RSA RNG like PGP does by seeding it from the GPG RNG
   R_RandomInit(&Random);
   while (1)
   {
      unsigned BlkSize;
      byte *Rand;
      R_GetRandomBytesNeeded(&BlkSize,&Random);
      if (BlkSize == 0)
	 break;
      Rand = get_random_bits(BlkSize*8,1,1);
      R_RandomUpdate(&Random,Rand,BlkSize);
      m_free(Rand);
   }
   
   // Run the RSA method
   rc = RSAPublicEncrypt(Buffer,&Size,Buffer + Off,Size-Off,&rpk,&Random);
   R_RandomFinal(&Random);
   memset(&Random,0,sizeof(Random));
   if (rc != 0)
   {
      memset(Buffer,0,Size);
      m_free(Buffer);
      return BAD_KEY;
   }
   
   // Copy over the result into the result array
   resarr[0] = g10m_new(ModSize);
   mpi_set_buffer(resarr[0],Buffer,ModSize,0);
   memset(Buffer,0,Size);
   m_free(Buffer);
   return 0;
}
									/*}}}*/
// do_decrypt - Perform secret key decription				/*{{{*/
// ---------------------------------------------------------------------
/* This undoes do_encrypt. It takes a message encrypted with the public
   key and uses the secret key to unlock it. */
static int do_decrypt(int algo,MPI *result,MPI *data,MPI *skey)
{
   R_RSA_PRIVATE_KEY rpk;
   byte *Buffer;
   unsigned Size;
   unsigned ModSize;   
   int rc;
   
   if (algo != 1 && algo != 2)
      return BAD_ALGO;
   
   
   // Convert the secret key
   if ((ModSize = repack_rsaref_sec(&rpk,skey)) == 0)
      return BAD_KEY;
   
   // Do the decryption
   Buffer = mpi_get_buffer(data[0],&Size,0);
   rc = RSAPrivateDecrypt(Buffer,&Size,Buffer,Size,&rpk);
   memset(&rpk,0,sizeof(rpk));
   if (rc != 0)
   {
      memset(Buffer,0,Size);
      m_free(Buffer);
      return BAD_KEY;
   }
   
   /* Re-insert the padding that RSAREF removed so that GPG can properly
      parse the reply */
   *result = g10m_new_secure(ModSize);
   rsaref_add_pad(Buffer,ModSize,Size,result,2);
   memset(Buffer,0,Size);
   m_free(Buffer);
   return 0;
}
									/*}}}*/
// do_sign - Perform a signing operation				/*{{{*/
// ---------------------------------------------------------------------
/* This routine uses the secret key to encrypt a chunk of data so that
   it can be decrypted by using the public key. The data itself is is
   required to have FF PCKS padding, which means it is only good for
   signatures. */
static int do_sign(int algo,MPI *resarr,MPI data,MPI *skey)
{
   R_RSA_PRIVATE_KEY rpk;
   byte *Buffer;
   unsigned int Off;
   unsigned Size;
   unsigned ModSize;   
   int rc;

   if (algo != 1 && algo != 3)
      return BAD_ALGO;
   
   // Convert the secret key
   if ((ModSize = repack_rsaref_sec(&rpk,skey)) == 0)
      return BAD_KEY;

   // Remove GPGs padding and then encrypt
   Buffer = rsaref_remove_pad(data,&Size,&Off);
   rc = RSAPrivateEncrypt(Buffer,&Size,Buffer+Off,Size-Off,&rpk);
   memset(&rpk,0,sizeof(rpk));
   if (rc != 0)
   {
      memset(Buffer,0,Size);
      m_free(Buffer);
      return BAD_KEY;
   }
   
   // Copy over the result into the result array
   resarr[0] = g10m_new(ModSize);
   mpi_set_buffer(resarr[0],Buffer,ModSize,0);
   memset(Buffer,0,Size);
   m_free(Buffer);   
   return 0;
}
									/*}}}*/
// do_verify - Perform a verification operation				/*{{{*/
// ---------------------------------------------------------------------
/* This is the most specific routine here, it takes a chunk of data encrypted
   with the secret key and compares its unencrypted form with the given 
   expected result. Why it is not a more general public key decrypt routine
   is beyond me.. This only works if the incoming hash used 0xFF for the
   padding value. I don't know what the compare function is for, so it is
   unused */
static int do_verify(int algo,MPI hash,MPI *data,MPI *pkey,
		     int (*cmp)(void *opaque, MPI tmp),void *opaquev)
{
   MPI result;
   int rc;
   R_RSA_PUBLIC_KEY rpk;
   byte *Buffer;
   unsigned Size;
   unsigned ModSize;
   
   if (algo != 1 && algo != 3)
      return BAD_ALGO;
   
   // Convert the key
   if ((ModSize = repack_rsaref_pub(&rpk,pkey)) == 0)
      return BAD_KEY;
   
   // Do the RSA step. RSAREF can do this using the same input and output.
   Buffer = mpi_get_buffer(data[0],&Size,0);
   rc = RSAPublicDecrypt(Buffer,&Size,Buffer,Size,&rpk);
   if (rc != 0)
   {
      memset(Buffer,0,Size);
      m_free(Buffer);
      return BAD_KEY;
   }

   // Copy over the result..
   rsaref_add_pad(Buffer,ModSize,Size,&result,1);
   memset(Buffer,0,Size);
   m_free(Buffer);
   
   // And compare
   rc = g10m_cmp(result,hash)?BAD_SIGN:0;
   g10m_release(result);
   return rc;
}
									/*}}}*/
// do_get_nbits - Return the number of bits in the key			/*{{{*/
// ---------------------------------------------------------------------
/* */
static unsigned do_get_nbits(int algo, MPI *pkey)
{
   if (algo != 1 && algo != 2 && algo != 3)
      return BAD_ALGO;
    return g10m_get_nbits(pkey[0]);
}
									/*}}}*/
// do_get_info - Return information about this module			/*{{{*/
// ---------------------------------------------------------------------
/* This returns descriptive strings and information about the module,
   particularly the entry points. Why it doesn't use a structure I don't 
   know.. This algorithm calls itself RSAREF to distingust from the RSA
   algorithm */
typedef int (*GenerateProto)(int algo,unsigned nbits,MPI *skey,MPI **retfactors);
typedef int (*CheckSecKeyProto)(int algo,MPI *skey);
typedef int (*EncryptProto)(int algo,MPI *resarr,MPI data,MPI *pkey);
typedef int (*DecryptProto)(int algo,MPI *result,MPI *data,MPI *skey);
typedef int (*SignProto)(int algo,MPI *resarr,MPI data,MPI *skey);
typedef int (*VerifyProto)(int algo,MPI hash,MPI *data,MPI *pkey,
			   int (*)(void *,MPI),void *);
typedef unsigned (*GetNBitsProto)(int algo,MPI *pkey);

static const char *do_get_info(int algo,
	      int *npkey,int *nskey, int *nenc, int *nsig, int *usage,
	      GenerateProto *generate,CheckSecKeyProto *checkseckey,
	      EncryptProto *encrypt,DecryptProto *decrypt,SignProto *sign,
	      VerifyProto *verify,GetNBitsProto *getnbits)
{
   // Fill in the result pointers..
   *npkey = 2;
   *nskey = 6;
   *nenc = 1;
   *nsig = 1;
   
   *generate = 0; 
   *checkseckey = do_check_secret_key;
   *encrypt = do_encrypt;
   *decrypt = do_decrypt;
   *sign = do_sign;
   *verify = do_verify;
   *getnbits = do_get_nbits;
   
   /* Return the usage flag and the description string for the various
      algorithms */
   switch (algo) 
   {
      case 1:
      *usage = 2|1; 
      return "RSAREF";
      
      case 2: 
      *usage = 2; 
      return "RSAREF-E";
      
      case 3:
      *usage = 1;
      return "RSAREF-S";
   }
   
   *usage = 0;
   return NULL;
}
									/*}}}*/
// gnupgext_enum_func - Module entry point				/*{{{*/
// ---------------------------------------------------------------------
/* This routine is used by GPG to enumerate the capabilities of the module */
const char * const gnupgext_version = "RSAREF " __DATE__;
void *gnupgext_enum_func(int what,int *sequence,int *class,int *version)
{
   int i;
   // Table of  responses, makes it simple to get the right seq number
   static struct 
   {
      int class;
      int version;
      int  value;
      void (*func)(void);
   } table[] = {{30,1,0,(void(*)(void))do_get_info},
                {31,1,1,0}, // RSA
                {31,1,2,0}, // RSA encrypt only
                {31,1,3,0}}; // RSA sign only

   
   // Look for a matching class starting at the given sequence number
   for (i = *sequence; i < sizeof(table)/sizeof(table[0]) && i >= 0; i++)
   {
      // Not a matching class
      if (what == 0 || table[i].class != what)
	 continue;
      
      *sequence = i + 1;
      *class = table[i].class;
      *version  = table[i].version;
      
      // See if we should return an integer or the function pointer
      switch (*class)
      {
	 case 11:
	 case 21:
	 case 31:
	 return &table[i].value;
      }
      
      return table[i].func;
   } 

   *sequence = i + 1;
   return 0;
}
									/*}}}*/
