HElib
HElib copied to clipboard
Context and keys read from files cannot be used.
Hi~ I tried to write FHEcontext (ct), FHEPubKey (pk), and FHESecKey (sk) to files (or binary strings), then load them to use, but the result is not quite reasonable. This is what happened. I constructed ct, sk, and pk, and use this pk to encrypt some data and produced a ciphertext. Then I write the corresponding sk to a file (or string), and read the file to get it back as sk2. Of course I load ct first and created a new FHESecKey object with this loaded ct. However, when I use this sk2 to perform decryption, I'll get the error message: DoubleCRT::Op: incompatible objects, which means the contexts of sk and ciphertext do not match. When I bypass this check, the decryption can be done correctly. (But is I bypass this check, then wrong keys might be applied to ciphertexts, which is a problem.)
Here is my code: ` //{ // 1. Write ASCII and bin files. ofstream asciiFile(asciiFile1); ofstream binFile(binFile1, ios::binary); assert(asciiFile.is_open());
std::unique_ptr<FHEcontext> contextOrigin(new FHEcontext(m, p, r));
buildModChain(*contextOrigin, L, c); // Set the modulus chain
if (!noPrint) {
cout << "Test to write out ASCII and Binary Files.\n";
contextOrigin->zMStar.printout(); // Printout context params
cout << "\tSecurity Level: " << contextOrigin->securityLevel() << endl;
}
std::unique_ptr<FHESecKey> secKeyOrigin(new FHESecKey(*contextOrigin));
FHEPubKey* pubKeyOrigin = (FHEPubKey*)secKeyOrigin.get();
secKeyOrigin->GenSecKey(w);
addSome1DMatrices(*secKeyOrigin);
addFrbMatrices(*secKeyOrigin);
// ASCII
if (!noPrint)
cout << "\tWriting ASCII1 file " << asciiFile1 << endl;
writeContextBase(asciiFile, *contextOrigin);
asciiFile << *contextOrigin << endl << endl;
asciiFile << *pubKeyOrigin << endl << endl;
asciiFile << *secKeyOrigin << endl << endl;
// Bin
if (!noPrint)
cout << "\tWriting Binary file " << binFile1<< endl;
writeContextBaseBinary(binFile, *contextOrigin);
writeContextBinary(binFile, *contextOrigin);
writePubKeyBinary(binFile, *pubKeyOrigin);
writeSecKeyBinary(binFile, *secKeyOrigin);
asciiFile.close();
binFile.close();
cout << "GOOD\n";
//} { // 4. Read in binary and perform operation. if (!noPrint) cout << "Test reading in Binary files and performing an operation between two ctxts\n";
ifstream inFile(binFile1, ios::binary);
// Read in context,
std::unique_ptr<FHEcontext> context = buildContextFromBinary(inFile);
readContextBinary(inFile, *context);
// Read in PubKey.
std::unique_ptr<FHESecKey> secKey(new FHESecKey(*context));
FHEPubKey* pubKey = (FHEPubKey*) secKey.get();
readPubKeyBinary(inFile, *pubKey);
readSecKeyBinary(inFile, *secKey);
inFile.close();
//again
ifstream inFile2(binFile1, ios::binary);
// Read in context,
std::unique_ptr<FHEcontext> context2 = buildContextFromBinary(inFile2);
readContextBinary(inFile2, *context2);
// Read in PubKey.
std::unique_ptr<FHESecKey> secKey2(new FHESecKey(*context2));
FHEPubKey* pubKey2 = (FHEPubKey*)secKey2.get();
readPubKeyBinary(inFile2, *pubKey2);
readSecKeyBinary(inFile2, *secKey2);
inFile.close();
//again
ifstream inFile3(binFile1, ios::binary);
// Read in context,
std::unique_ptr<FHEcontext> context3 = buildContextFromBinary(inFile3);
readContextBinary(inFile3, *context3);
// Read in PubKey.
std::unique_ptr<FHESecKey> secKey3(new FHESecKey(*context3));
FHEPubKey* pubKey3 = (FHEPubKey*)secKey3.get();
readPubKeyBinary(inFile3, *pubKey3);
readSecKeyBinary(inFile3, *secKey3);
inFile.close();
cout << (*context2 == *context3) << endl;
cout << (*secKey2 == *secKey3) << endl;
cout << (*pubKey2 == *pubKey3) << endl;
// Get the ea
const EncryptedArray& ea = *context3->ea;
// Setup some ptxts and ctxts.
Ctxt c1(*pubKey3), c2(*pubKey3);
PlaintextArray p1(ea), p2(ea);
random(ea, p1);
random(ea, p2);
ea.encrypt(c1, *pubKey3, p1);
ea.encrypt(c2, *pubKey3, p2);
// Operation multiply and add.
mul(ea, p1, p2);
c1 *= c2;
// Decrypt and Compare.
PlaintextArray pp1(ea);
ea.decrypt(c1, *secKey2, pp1);
if(!equals(ea, p1, pp1)) {
cout << "BAD\n";
exit(EXIT_FAILURE);
}
cout << "GOOD\n";
if(cleanup) {
if (!noPrint)
cout << "Clean up. Deleting created files." << endl;
cleanupFiles(asciiFile1, asciiFile2, binFile1);
}
} { // 5. Read in binary from opposite little endian and print ASCII and compare bool littleEndian = isLittleEndian();
string otherEndianFileIn
= sampleFilePrefix + (littleEndian? "_BE.bin" : "_LE.bin");
string otherEndianASCII
= sampleFilePrefix + (littleEndian? "_BE.txt" : "_LE.txt");
if (!noPrint)
cout << "Test to read in" << (littleEndian? " BE ":" LE ")
<< "binary file and write it out as ASCII" << endl;
if(sampleFilePrefix.empty()) {
if (!noPrint)
cout << "\tSample prefix not provided, test not done." << endl;
} else {
if (!noPrint)
cout << "\tSample file used: " << otherEndianFileIn << endl;
ifstream inFile(otherEndianFileIn, ios::binary);
if(!inFile.is_open()) {
cout << "BAD boo!\n";
if (!noPrint)
cout << " file " << otherEndianFileIn
<< " could not be opened.\n";
exit(EXIT_FAILURE);
}
ofstream outFile(otherEndianFileOut);
// Read in context,
std::unique_ptr<FHEcontext> context = buildContextFromBinary(inFile);
readContextBinary(inFile, *context);
// Read in SecKey and PubKey.
std::unique_ptr<FHESecKey> secKey(new FHESecKey(*context));
FHEPubKey* pubKey = (FHEPubKey*) secKey.get();
readPubKeyBinary(inFile, *pubKey);
readSecKeyBinary(inFile, *secKey);
inFile.close();
// ASCII
if (!noPrint)
cout << "\tWriting other endian file." << endl;
writeContextBase(outFile, *context);
outFile << *context << endl << endl;
outFile << *pubKey << endl << endl;
outFile << *secKey << endl << endl;
outFile.close();
// Compare byte-wise the two ASCII files
if (!noPrint)
cout << "Comparing the two ASCII files\n";
long differ = compareFiles(otherEndianASCII, otherEndianFileOut);
if(differ != 0) {
cout << "BAD\n";
exit(EXIT_FAILURE);
}
cout << "GOOD\n";
if(cleanup) {
if (!noPrint)
cout << "Clean up. Deleting created files." << endl;
cleanupFiles(otherEndianFileOut);
}
}
` Actually it is a modified Test_Bin_IO. In the original test, when performing encryption and decryption, both pk and sk are loaded from file, and pk is created use the loaded sk, which kinda misses the point of the test.
Look forward to your help~ Guys
I did some test on the context check before ciphertext operation, and find that the checking procedures compare the address or reference of the FHEcontext objects, but not the content. This will lead to an ensured operation failure while the FHEcontext instants of the operands are different even if the values held are the same. Is this a bug or intended?
Is this a bug or intended?
A little bit of both :)
It was written as a quick and dirty hack, it really needs to be fixed by now but I never found the time to fix it. Probably the best way to fix it is to give each context and each key a "name" (tied somehow to its contents) and check that the names match.
In the current implementation there is also no empty Ctxt constructor and each Ctxt is initialized against a given HE Context. This makes the current comparison of addresses not so bad actually. I am also not entirely sure how 2 different Contexts would behave in parallel, since NTL, for example, has some statically set global parameters (current modulus).