Skip to content

Incorrect Results from extractDigits #519

@wahyudierwin

Description

@wahyudierwin

I am trying to perform digit extraction homomorphically, and fortunately, HElib provides a function called extractDigits. However, I am getting incorrect results. Below is the code I'm using:

#include <cstdlib>
#include <ctime>

#include <helib/helib.h>
#include <helib/debugging.h>

#include <random>
#include <map> 
#include <NTL/ZZ_pE.h>
#include <NTL/mat_ZZ_pE.h>
#include <helib/Ptxt.h>


void print_decrypted(const helib::Ctxt& ctxt, const helib::Context& context, const helib::SecKey& sk)
{
  const helib::EncryptedArray& ea = context.getEA();
  unsigned long ord_p = context.getOrdP();

  long nSlots = ea.size();

  std::vector<NTL::ZZX> decrypted(nSlots);
  ea.decrypt(ctxt, sk, decrypted);

  for(int i = 0; i < nSlots; i++)
  {
    helib::printZZX(std::cout, decrypted[i], ord_p);
  }
  std::cout << std::endl;
}

int main(int argc, char* argv[])
{
    // Plaintext prime modulus
  unsigned long p = 7;
  
  // Cyclotomic polynomial - defines phi(m)
  unsigned long m = 678;

  // Hensel lifting (default = 1)
  unsigned long r = 3;
  // Number of bits of the modulus chain
  unsigned long bits = 1000;
  
  // Number of columns of Key-Switching matrix (default = 2 or 3)
  unsigned long c = 2;

  std::cout << "Initialising context object..." << std::endl;
  // Initialize context
  // This object will hold information about the algebra created from the
  // previously set parameters
  helib::Context context = helib::ContextBuilder<helib::BGV>()
                               .m(m)
                               .p(p)
                               .r(r)
                               .bits(bits)
                               .c(c)
                               .build();

  // Print the context
  context.printout();
  std::cout << "-=-=-=-=-=-=-=-=-=-=-=-=-" << std::endl;
  
  // Secret key management
  std::cout << "Creating secret key..." << std::endl;
  // Create a secret key associated with the context
  helib::SecKey secret_key(context);
  // Generate the secret key
  secret_key.GenSecKey();
  std::cout << "Generating key-switching matrices..." << std::endl;
  // Compute key-switching matrices that we need
  helib::addSome1DMatrices(secret_key);

  // Public key management
  // Set the secret key (upcast: SecKey is a subclass of PubKey)
  const helib::PubKey& public_key = secret_key;

  //generate random data
  std::random_device rd;
  std::mt19937 eng(rd());
  std::uniform_int_distribution<unsigned long> distr_u;
  std::uniform_int_distribution<long> distr_i;

  // get EncryptedArray
  const helib::EncryptedArray& ea = context.getEA();
  
  // get number of slots
  long nslots = ea.size();
  unsigned long input_range = context.getPPowR();

  long input_x;
  std::vector<long> x_vec(nslots);

  // Generate input
  for (int i = 0; i < nslots; i++){
    input_x = distr_u(eng) % input_range;
    x_vec[i] = input_x;
		
    if (i == 0) x_vec[i] = input_range - 1;
    if (i == 1) x_vec[i] = input_range/2;
    if (i == 2) x_vec[i] = input_range/2 + 1;
  }

  // encrypt
  helib::Ctxt ctxt_x(public_key);
  ea.encrypt(ctxt_x, public_key, x_vec);

  std::cout << "Input plaintext:\n";
  for (int i = 0; i < nslots; i++){
    std::cout << x_vec[i] << "\t";
  }
  std::cout << std::endl;

  // test extract digits
  std::vector<helib::Ctxt> ctxt_x_p;
  helib::extractDigits(ctxt_x_p, ctxt_x, r);

  // print extracted digits
  for (int i=0 ; i<ctxt_x_p.size() ; i++){
    print_decrypted(ctxt_x_p[i], context, secret_key);
  }

  return 0;
}

From the code, p is 7 and r is 3, which means the maximum input for each slot should be p^r = 7^3 = 343. However, after running the code, I get the following output:

Input plaintext:
342     171     172     11      26      64      119     70      34      226     247     154     289     96      210  241
[342][3][340][340][341][1][][][342][2][2][][2][341][][3]
[][3][46][2][46][2][3][3][47][46][][1][48][][2][48]
[][3][4][][1][1][2][1][1][5][5][3][6][2][4][5]

I expected that 342 when represented in base 7 would be 666, but the results are different from the expectation.

Questions:

  • Could there be an issue with how extractDigits is applied?
  • Is there a bug or limitation in HElib's implementation of extractDigits?

Any comments or suggestions are highly appreciated. Thank you!

Added
I also tried another parameters, p = 3, r = 4, m = 679 it gives incorrect results:

Input plaintext:
80	40	41	69	6	3	24	61	7	21	10	22	
[80][1][80][][][][][1][1][][1][1]
[][1][26][26][26][1][26][26][26][1][][1]
[][1][8][8][1][][][1][1][8][1][8]
[][1][2][][][][1][2][][1][][1]

However, if p = 2, it produces correct results.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions