6

I am writing a program that uses Paillier Cryptosystem to encrypt a matrix. To encrypt a 50 x 50 matrix it takes about 12 seconds! That's painfully long considering I intend to encrypt matrices of sizes 5000 x 5000 and above. Profiling the program on Xcode I figured out that this paillier_get_rand_devurandom() is the culprit.

This is the call trace snapshot :

enter image description here

Here's the source code of this particular Paillier C library function

    void paillier_get_rand_devurandom( void* buf, int len )
    {
          paillier_get_rand_file(buf, len, "/dev/urandom");
    }  


    void paillier_get_rand_file( void* buf, int len, char* file )
   {
         FILE* fp;
         void* p;

         fp = fopen(file, "r");

         p = buf;
         while( len )
         {
             size_t s;
             s = fread(p, 1, len, fp);
             p += s;
             len -= s;
         }

        fclose(fp);
    }

And in case,

    <http://www.en.wikipedia.org/wiki/Paillier_cryptosystem>
    Paillier Cryptosytem C library : <http://acsc.cs.utexas.edu/libpaillier/> 

I have read that random number generation using dev/random is slow whereas with dev/urandom its much faster. In my case both are equally slow. Can this random number generation be made faster?

EDIT: Here's an example

    #include <stdio.h>
    #include <stdlib.h>
    #include <gmp.h>
    #include<paillier.h>


   int main(void)
   {

       paillier_pubkey_t* pub;//The public key
   paillier_prvkey_t* prv;//The private key 


       paillier_keygen(1024, &pub, &prv,paillier_get_rand_devurandom);


      paillier_ciphertext_t* ca;
      paillier_ciphertext_t* cb;
      paillier_ciphertext_t* res;


      paillier_plaintext_t* a;
      paillier_plaintext_t* b;
      paillier_plaintext_t* sum;



     a=paillier_plaintext_from_ui(45);
     b=paillier_plaintext_from_ui(100);


     //This is the encryption function 
     ca=paillier_enc(0, pub, a, paillier_get_rand_devurandom);
     cb=paillier_enc(0, pub, b, paillier_get_rand_devurandom);


     res=paillier_create_enc_zero();


     paillier_mul(pub, res,ca, cb);

     sum=paillier_dec(0, pub, prv, res);


     gmp_printf("The sum is : %Zd\n",sum);


     return 0;

   }

And this is the encryption function signature

    /*
Encrypt the given plaintext with the given public key using
randomness from get_rand for blinding. If res is not null, its
contents will be overwritten with the result. Otherwise, a new
paillier_ciphertext_t will be allocated and returned.
    */
    paillier_ciphertext_t* paillier_enc( paillier_ciphertext_t* res,
                                           paillier_pubkey_t* pub,
                        paillier_plaintext_t* pt,
                         paillier_get_rand_t get_rand );

Sorry this question is only getting longer

 The actual scale_encrypt_T()

 void scale_encrypt_T(char *scaledTfile)
{
    ...
    ...

//Printing scaled and then encrypted T[][] in a file 
for(i=0;i<n;i++)
{
    for(j=0;j<n;j++)
    {
        void *buf2;

        //T[][] is the 50 x 50 matrix 
        temp=(int)(T[i][j]*pow(10,6));  //Scale factor q = 10 to the power of 6

        p0=paillier_plaintext_from_ui(temp);

        //c0 is the cipher text
         /***So this is where its taking so long***/
        c0=paillier_enc(0, pub, p0, paillier_get_rand_devurandom);


        buf2=paillier_ciphertext_to_bytes(PAILLIER_BITS_TO_BYTES(pub->bits)*2, c0);

        fwrite(buf2, 1, PAILLIER_BITS_TO_BYTES(pub->bits)*2, fpt);
        free(buf2);

    }

}
  • How long is each read from /dev/urandom? How many of them are you doing? Is your program otherwise working? Is your program reading from /dev/urandom for a purpose other than initial key generation? What purpose? Can you supply a [self-contained compilable example](http://sscce.org)? – Eric Postpischil May 29 '13 at 19:26
  • You should only use /dev/random or /dev/urandom for seeding or for producing cryptographic keys. – Jim Balter May 29 '13 at 19:39
  • @Eric dev/urandom is called for each encryption – segmentation_fault May 29 '13 at 19:43
  • @Jim Balter Well, the function signature asks for the random number generator function as one of its arguments. – segmentation_fault May 29 '13 at 19:46
  • @segmentation_fault: It is important to provide specific numerical answers. In the 12 seconds the program takes, what number of reads are performed from /dev/urandom? How many bytes are read each time (or, if they vary, what is the total number of bytes read)? There is some concern that your program is reading more bytes from /dev/urandom than it should be reading. What is “each encryption”? Each entry of a 50-by-50 matrix, so you are doing 2,500 encryptions? And generating keys for each? Or once for the encryption of a 50-by-50 matrix? – Eric Postpischil May 29 '13 at 19:56
  • @Eric Sorry your 1st question escaped my notice. In the 12s, 2500 reads are performed. Comes to 4.8 ms for each entry. About bytes, being a newbie, I'm uncertain. Yes there are 2500 encryptions. The keys are generated only once in the beginning of the program. Thanks – segmentation_fault May 29 '13 at 20:07
  • I downloaded and built gmp-5.1.2, libpaillier-0.8, and your code above (with default compilation options) and executed it on a MacPro4,1 with OS X 10.8.3. The program completed in .17 seconds. – Eric Postpischil May 29 '13 at 20:11
  • Oh thanks! Sorry for the trouble. So according to you where can the problem lie? – segmentation_fault May 29 '13 at 20:15
  • Given that the call trace you posted shows a routine named “scale_encrypt_T” but the code you posted does not contain that text, and neither do the GMP or Paillier sources, I would hypothesize one problem is that you have not shown us the actual code that exhibits the problem. – Eric Postpischil May 29 '13 at 20:26
  • @Eric Apologies. Now I have included the actual code in the question. – segmentation_fault May 29 '13 at 20:46
  • 1
    I ran that program of yours such that there would be 2500 calls to paillier_enc - it takes 3.8ms per call on my weak AMD fanless box running Linux. If I make the FILE* fp static and only open it once I can reliably shave off another 0.3ms per call! I am wondering if OS X is slow at system calls and can do better if you avoid the repeated open and close of /dev/urandom. – parry May 29 '13 at 21:11
  • @parry Oh thanks for the effort! What was taking 12s has now reduced to 0.286s i.e. 0.1144ms from 4.8ms for each entry! I am amazed. Thanks again. – segmentation_fault May 30 '13 at 10:03
  • Even though this is already solved, perhaps you or others would be willing to make this library use the RDRAND instruction when available - the performance savings is huge. – Thomas M. DuBuisson May 30 '13 at 16:15

1 Answers1

1

Supposing that n is 50, your code is doing 2500 separate encryptions. Each encryption may require some random data.

It will be more efficient to do one encryption by encrypting the entire array in one call to paillier_enc. I am not familiar with the libpaillier API, but it looks like you would do this by arranging for all the data of the 50-by-50 array to be in contiguous memory, then use paillier_plaintext_from_bytes to package the entire array into a paillier_plaintext_t object, then use pallier_enc with that object.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312