intel / pailliercryptolib

Intel Paillier Cryptosystem Library is an open-source library which provides accelerated performance of a partial homomorphic encryption (HE), named Paillier cryptosystem, by utilizing Intel® IPP-Crypto on AVX512IFMA instructions. Intel Paillier Cryptosystem Library is certified for ISO compliance.
Apache License 2.0
70 stars 18 forks source link

Why deserialization of CipherText is PlainText? #81

Open lzjluzijie opened 5 months ago

lzjluzijie commented 5 months ago

https://github.com/intel/pailliercryptolib/blob/fdc21350302117103452968ababc2f9676f0d383/test/test_serialization.cpp#L96

If I change that PlainText to CipherText, there would have segmentation fault.

I think the test be something like this:

void testSerialize() {
  const uint32_t num_values = 100;
  auto keys = ipcl::generateKeypair(768);
  auto pk = keys.pub_key;
  auto sk = keys.priv_key;

  std::random_device dev;
  std::mt19937 rng(dev());
  std::uniform_int_distribution<std::mt19937::result_type> dist(0, UINT_MAX);
  std::vector<uint32_t> exp_value(num_values);
  for (int i = 1; i < num_values; i++) {
    exp_value[i] = dist(rng);
  }

  ipcl::PlainText pt = ipcl::PlainText(exp_value);
  ipcl::CipherText ct = pk.encrypt(pt);

  std::ostringstream os;
  ipcl::serializer::serialize(os, ct);
  ipcl::CipherText ct_after;
  std::istringstream is(os.str());
  ipcl::serializer::deserialize(is, ct_after);

  ipcl::PlainText pt_after = sk.decrypt(ct_after);
  for (int i = 0; i < num_values; i++) {
    std::vector<uint32_t> v = pt.getElementVec(i);
    std::vector<uint32_t> v_after = pt_after.getElementVec(i);

    if (v[0] != v_after[0]) {
      std::cerr << "Error: " << v[0] << " != " << v_after[0] << std::endl;
    }
  }
}

but it failed with segmentation fault

==10631== Invalid read of size 8
==10631==    at 0x4862245: ipcl::PublicKey::create(BigNumber const&, int, bool) (in /opt/intel/ipcl1/lib/ipcl/libipcl.so.2.0.0)
==10631==    by 0x4862553: ipcl::PublicKey::create(BigNumber const&, int, BigNumber const&, int) (in /opt/intel/ipcl1/lib/ipcl/libipcl.so.2.0.0)
==10631==    by 0x458354: load<cereal::PortableBinaryInputArchive> (pub_key.hpp:161)
==10631==    by 0x458354: member_load<cereal::PortableBinaryInputArchive, ipcl::PublicKey> (access.hpp:287)
==10631==    by 0x458354: processImpl<ipcl::PublicKey> (cereal.hpp:1058)
==10631==    by 0x458354: process<ipcl::PublicKey&> (cereal.hpp:853)
==10631==    by 0x458354: operator()<ipcl::PublicKey&> (cereal.hpp:730)
==10631==    by 0x458354: serialize<cereal::PortableBinaryInputArchive, ipcl::PublicKey&> (portable_binary.hpp:291)
==10631==    by 0x458354: processImpl<cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:925)
==10631==    by 0x458354: process<cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:853)
==10631==    by 0x458354: process<cereal::base_class<ipcl::BaseText>, cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:862)
==10631==    by 0x458354: operator()<cereal::base_class<ipcl::BaseText>, cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:730)
==10631==    by 0x458354: serialize<cereal::PortableBinaryInputArchive> (ciphertext.hpp:73)
==10631==    by 0x458354: member_serialize<cereal::PortableBinaryInputArchive, ipcl::CipherText> (access.hpp:275)
==10631==    by 0x458354: processImpl<ipcl::CipherText> (cereal.hpp:1038)
==10631==    by 0x458354: process<ipcl::CipherText&> (cereal.hpp:853)
==10631==    by 0x458354: operator()<ipcl::CipherText&> (cereal.hpp:730)
==10631==    by 0x458354: void ipcl::serializer::deserialize<ipcl::CipherText>(std::istream&, ipcl::CipherText&) (serialize.hpp:34)
==10631==    by 0x44D535: testSerialize() (test.cpp:437)
==10631==    by 0x40F57D: main (main.cpp:8)
==10631==  Address 0x18 is not stack'd, malloc'd or (recently) free'd
==10631==
==10631==
==10631== Process terminating with default action of signal 11 (SIGSEGV)
==10631==  Access not within mapped region at address 0x18
==10631==    at 0x4862245: ipcl::PublicKey::create(BigNumber const&, int, bool) (in /opt/intel/ipcl1/lib/ipcl/libipcl.so.2.0.0)
==10631==    by 0x4862553: ipcl::PublicKey::create(BigNumber const&, int, BigNumber const&, int) (in /opt/intel/ipcl1/lib/ipcl/libipcl.so.2.0.0)
==10631==    by 0x458354: load<cereal::PortableBinaryInputArchive> (pub_key.hpp:161)
==10631==    by 0x458354: member_load<cereal::PortableBinaryInputArchive, ipcl::PublicKey> (access.hpp:287)
==10631==    by 0x458354: processImpl<ipcl::PublicKey> (cereal.hpp:1058)
==10631==    by 0x458354: process<ipcl::PublicKey&> (cereal.hpp:853)
==10631==    by 0x458354: operator()<ipcl::PublicKey&> (cereal.hpp:730)
==10631==    by 0x458354: serialize<cereal::PortableBinaryInputArchive, ipcl::PublicKey&> (portable_binary.hpp:291)
==10631==    by 0x458354: processImpl<cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:925)
==10631==    by 0x458354: process<cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:853)
==10631==    by 0x458354: process<cereal::base_class<ipcl::BaseText>, cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:862)
==10631==    by 0x458354: operator()<cereal::base_class<ipcl::BaseText>, cereal::NameValuePair<ipcl::PublicKey&> > (cereal.hpp:730)
==10631==    by 0x458354: serialize<cereal::PortableBinaryInputArchive> (ciphertext.hpp:73)
==10631==    by 0x458354: member_serialize<cereal::PortableBinaryInputArchive, ipcl::CipherText> (access.hpp:275)
==10631==    by 0x458354: processImpl<ipcl::CipherText> (cereal.hpp:1038)
==10631==    by 0x458354: process<ipcl::CipherText&> (cereal.hpp:853)
==10631==    by 0x458354: operator()<ipcl::CipherText&> (cereal.hpp:730)
==10631==    by 0x458354: void ipcl::serializer::deserialize<ipcl::CipherText>(std::istream&, ipcl::CipherText&) (serialize.hpp:34)
==10631==    by 0x44D535: testSerialize() (test.cpp:437)
==10631==    by 0x40F57D: main (main.cpp:8)

the line test.cpp:437 is ipcl::PlainText pt_after = sk.decrypt(ct_after);. Did I make any mistake? Thank you!

lzjluzijie commented 5 months ago

OK, I added a dummy values and init ct_after using ipcl::CipherText(pk, values), and the test passed. But is it the right way to use serialization?

void testSerialize() {
  const uint32_t num_values = 100;
  auto keys = ipcl::generateKeypair(768);
  auto pk = keys.pub_key;
  auto sk = keys.priv_key;

  std::random_device dev;
  std::mt19937 rng(dev());
  std::uniform_int_distribution<std::mt19937::result_type> dist(0, UINT_MAX);
  std::vector<uint32_t> exp_value(num_values);
  for (int i = 1; i < num_values; i++) {
    exp_value[i] = dist(rng);
  }

  ipcl::PlainText pt = ipcl::PlainText(exp_value);
  ipcl::CipherText ct = pk.encrypt(pt);

  std::ostringstream os;
  ipcl::serializer::serialize(os, ct);

  std::vector<uint32_t> values(num_values);
  ipcl::CipherText ct_after = ipcl::CipherText(pk, values);
  std::istringstream is(os.str());
  ipcl::serializer::deserialize(is, ct_after);

  ipcl::PlainText pt_after = sk.decrypt(ct_after);
  for (int i = 0; i < num_values; i++) {
    std::vector<uint32_t> v = pt.getElementVec(i);
    std::vector<uint32_t> v_after = pt_after.getElementVec(i);
    std::cout << v[0] << " " << v_after[0] << std::endl;

    if (v[0] != v_after[0]) {
      std::cerr << "Error: " << v[0] << " != " << v_after[0] << std::endl;
      exit(1);
    }
  }
}
justalittlenoob commented 5 months ago

OK, I added a dummy values and init ct_after using ipcl::CipherText(pk, values), and the test passed. But is it the right way to use serialization?

void testSerialize() {
  const uint32_t num_values = 100;
  auto keys = ipcl::generateKeypair(768);
  auto pk = keys.pub_key;
  auto sk = keys.priv_key;

  std::random_device dev;
  std::mt19937 rng(dev());
  std::uniform_int_distribution<std::mt19937::result_type> dist(0, UINT_MAX);
  std::vector<uint32_t> exp_value(num_values);
  for (int i = 1; i < num_values; i++) {
    exp_value[i] = dist(rng);
  }

  ipcl::PlainText pt = ipcl::PlainText(exp_value);
  ipcl::CipherText ct = pk.encrypt(pt);

  std::ostringstream os;
  ipcl::serializer::serialize(os, ct);

  std::vector<uint32_t> values(num_values);
  ipcl::CipherText ct_after = ipcl::CipherText(pk, values);
  std::istringstream is(os.str());
  ipcl::serializer::deserialize(is, ct_after);

  ipcl::PlainText pt_after = sk.decrypt(ct_after);
  for (int i = 0; i < num_values; i++) {
    std::vector<uint32_t> v = pt.getElementVec(i);
    std::vector<uint32_t> v_after = pt_after.getElementVec(i);
    std::cout << v[0] << " " << v_after[0] << std::endl;

    if (v[0] != v_after[0]) {
      std::cerr << "Error: " << v[0] << " != " << v_after[0] << std::endl;
      exit(1);
    }
  }
}

I think this is correct.
There is ciphertext serialization test, you can refer to it. https://github.com/intel/pailliercryptolib/blob/fdc21350302117103452968ababc2f9676f0d383/test/test_serialization.cpp#L80

lzjluzijie commented 5 months ago

OK, I added a dummy values and init ct_after using ipcl::CipherText(pk, values), and the test passed. But is it the right way to use serialization?

void testSerialize() {
  const uint32_t num_values = 100;
  auto keys = ipcl::generateKeypair(768);
  auto pk = keys.pub_key;
  auto sk = keys.priv_key;

  std::random_device dev;
  std::mt19937 rng(dev());
  std::uniform_int_distribution<std::mt19937::result_type> dist(0, UINT_MAX);
  std::vector<uint32_t> exp_value(num_values);
  for (int i = 1; i < num_values; i++) {
    exp_value[i] = dist(rng);
  }

  ipcl::PlainText pt = ipcl::PlainText(exp_value);
  ipcl::CipherText ct = pk.encrypt(pt);

  std::ostringstream os;
  ipcl::serializer::serialize(os, ct);

  std::vector<uint32_t> values(num_values);
  ipcl::CipherText ct_after = ipcl::CipherText(pk, values);
  std::istringstream is(os.str());
  ipcl::serializer::deserialize(is, ct_after);

  ipcl::PlainText pt_after = sk.decrypt(ct_after);
  for (int i = 0; i < num_values; i++) {
    std::vector<uint32_t> v = pt.getElementVec(i);
    std::vector<uint32_t> v_after = pt_after.getElementVec(i);
    std::cout << v[0] << " " << v_after[0] << std::endl;

    if (v[0] != v_after[0]) {
      std::cerr << "Error: " << v[0] << " != " << v_after[0] << std::endl;
      exit(1);
    }
  }
}

I think this is correct. There is ciphertext serialization test, you can refer to it.

https://github.com/intel/pailliercryptolib/blob/fdc21350302117103452968ababc2f9676f0d383/test/test_serialization.cpp#L80

But why ct_after has type PlainText? https://github.com/intel/pailliercryptolib/blob/fdc21350302117103452968ababc2f9676f0d383/test/test_serialization.cpp#L96

justalittlenoob commented 5 months ago

This is a good question. To be honest, it is possible to use types PlainText or CipherText in this place, because they both inherit from BaseText. The real place for serialization is in BaseText.
For the convenience of initialization, we chosen to use PlainText. Of course, you can change this line to something like:
std::vector<uint32_t> zero(num_values, 0); ipcl::CipherText ct_after(keys.pub_key, zero);
the results will be the same. If you think this is easier to understand.

lzjluzijie commented 5 months ago

Thank you. So the initialization ipcl::CipherText ct_after(keys.pub_key, zero) is not encryption using pub_key?

justalittlenoob commented 5 months ago

Thank you. So the initialization ipcl::CipherText ct_after(keys.pub_key, zero) is not encryption using pub_key?

If you initialize a ciphertext directly, then the 2nd parameter should be the corresponding ciphertext (encrypted).
PlainText and CipherText are essentially containers. Store content through a vector. It does not care whether the stored content is encrypted or unencrypted.