homenc / HElib

HElib is an open-source software library that implements homomorphic encryption. It supports the BGV scheme with bootstrapping and the Approximate Number CKKS scheme. HElib also includes optimizations for efficient homomorphic evaluation, focusing on effective use of ciphertext packing techniques and on the Gentry-Halevi-Smart optimizations.
https://homenc.github.io/HElib
Other
3.11k stars 761 forks source link

Implementing serialization for `helib::Ctxt` #353

Closed giancarloGiuffra closed 4 years ago

giancarloGiuffra commented 4 years ago

Your Contact: github profile Your environment (OS/HW): macOS Catalina 10.15.3, 2,6 GHz Intel Core i5, 8 GB 1600 MHz DDR3, Intel Iris 1536 MB

Detailed Description: Hi everyone, I am not sure if this is the right place to ask this, but I had no other way to reach the HElib community. I am trying to implement an idea for electronic voting using HElib. The first step I am tackling is serializing some classes. I have started with the helib::Context class and decided to use the Boost Serialization library. I have encountered a problem in my attempt to serialization and thought someone here could give me a tip on solving it, given your knowledge of the library.

I have actually implemented the serialization. Unfortunately, the usage of buildModChain to setup the primes in the modulus chain of the context breaks my serialization process in a way I have not being able to understand so far. I think posting some code will make everything more clear. My implementation for serializing and deserializing is the following:

template<class Archive>
inline void save_construct_data(Archive & archive, const helib::Context * context, const unsigned int version){
  archive << context->zMStar.getM();
  archive << context->zMStar.getP();
  archive << context->alMod.getR();
  archive << context->smallPrimes;
  archive << context->ctxtPrimes;
  archive << context->specialPrimes;
  archive << context->digits;
}

template<class Archive>
inline void load_construct_data(Archive & archive, helib::Context * context, const unsigned int version){
  unsigned long m;
  unsigned long p;
  unsigned long r;
  helib::IndexSet smallPrimes;
  helib::IndexSet ctxtPrimes;
  helib::IndexSet specialPrimes;
  std::vector<helib::IndexSet> digits;
  archive >> m;
  archive >> p;
  archive >> r;
  archive >> smallPrimes;
  archive >> ctxtPrimes;
  archive >> specialPrimes;
  archive >> digits;
  ::new(context)helib::Context(m, p, r);
  //restore smallPrimes
  for(long prime = smallPrimes.first(); prime <= smallPrimes.last(); prime = smallPrimes.next(prime)){
    context->AddSmallPrime(prime);
  }
  //restore ctxtPrimes
  for(long prime = ctxtPrimes.first(); prime <= ctxtPrimes.last(); prime = ctxtPrimes.next(prime))
    context->AddCtxtPrime(prime);
  //restore specialPrimes
  for(long prime = specialPrimes.first(); prime <= specialPrimes.last(); prime = specialPrimes.next(prime))
    context->AddSpecialPrime(prime);
  //restore digits
  context->digits = digits;
  //last operation made in buildModchain
  context->setModSizeTable();
}

The test that calls the above implementation is the following:

BOOST_AUTO_TEST_CASE(serialization_context)
{
  // Plaintext prime modulus
  unsigned long p = 4999;
  // Cyclotomic polynomial - defines phi(m)
  unsigned long m = 32109;
  // Hensel lifting (default = 1)
  unsigned long r = 1;
  // Number of bits of the modulus chain
  unsigned long bits = 300;
  // Number of columns of Key-Switching matix (default = 2 or 3)
  unsigned long c = 2;

  helib::Context * original_context_ptr = new helib::Context(m, p, r);
  // Modify the context, adding primes to the modulus chain
  buildModChain(*original_context_ptr, bits, c);

  std::string filename = "context.serialized";

  std::ofstream os(filename);
  {
    boost::archive::text_oarchive oarchive(os);
    BOOST_TEST_MESSAGE("(m,p,r): " << original_context_ptr->zMStar.getM() << "," << original_context_ptr->zMStar.getP() << "," << original_context_ptr->alMod.getR());
    oarchive << original_context_ptr;
  }

  helib::Context * restored_context_ptr = new helib::Context(2, 3, 1);
  BOOST_TEST_MESSAGE("allocated memory for restored context, address " << restored_context_ptr);
  {
    std::ifstream ifs(filename);
    boost::archive::text_iarchive iarchive(ifs);
    iarchive >> restored_context_ptr;
    BOOST_TEST_MESSAGE("(m,p,r): " << restored_context_ptr->zMStar.getM() << "," << restored_context_ptr->zMStar.getP() << "," << restored_context_ptr->alMod.getR());
  }

  BOOST_TEST((*restored_context_ptr == *original_context_ptr));
}

If I run the above test commenting the line buildModChain(*original_context_ptr, bits, c);, the test passes. Instead if I run it as is, the following runtime error occurs:

memory access violation at address: 0x00000000: no mapping at fault address

Doing some analysis, the error occurs inside the deserialization function when the line context->AddSmallPrime(prime); is executed, which is the first access to the pointer after the call to the placement new operator. I was hoping someone here could have an idea of why buildModChain has such effect.

Thank you for any help. I apologize for using the issues in the repo to make such a question but it seemed the most effective way to reach the HElib community. I have posted an stackoverflow question here. Thanks.

giancarloGiuffra commented 4 years ago

It was a problem with my implementation. I was saving only the indices and was missing the actual primes in the modulus chain.