// -*- 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 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 #include #include #include #include #include #include #include /*}}}*/ // 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 0 T is the type, is some arbitary non-zero values, 0 is the seperator and 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; } /*}}}*/