Hi,
I’m trying to set up a multiparty scenario with iterative bootstrapping.
The design concept is as follow: I have different scenarios with 2, 4 or 6 parties.
Each party encrypts data and send it to a computational host where the calculations are made.
I’m struggeling at some points…
Is iterative bootstrapping even possible? I had a look into the examples, but there is another approach of bootstrapping implemented.
On the other hand, I’m getting “approximation error too high” while trying to decrypt my data.
Basically, before rotation, the decryption works, after that, it doesn’t.
Could somebody have a look on my code and tell me what’s wrong with it?
double eps = 0.0001;
// Initialize Public Key Containers
KeyPair<DCRTPoly> kp1; // Party 1
KeyPair<DCRTPoly> kp2; // Party 2
KeyPair<DCRTPoly> kp3; // Lead party - who finalizes interactive bootstrapping
KeyPair<DCRTPoly> kpMultiparty;
std::vector<int32_t> indices = {2};
////////////////////////////////////////////////////////////
// Perform Key Generation Operation
////////////////////////////////////////////////////////////
// Round 1 (party A)
kp1 = cryptoContext->KeyGen();
// Generate evalmult key part for A
auto evalMultKey = cryptoContext->KeySwitchGen(kp1.secretKey, kp1.secretKey);
// Generate evalsum key part for A
cryptoContext->EvalSumKeyGen(kp1.secretKey);
auto evalSumKeys = std::make_shared<std::map<usint, EvalKey<DCRTPoly>>>(
cryptoContext->GetEvalSumKeyMap(kp1.secretKey->GetKeyTag()));
auto evalAtIndexKeys = std::make_shared<std::map<usint, EvalKey<DCRTPoly>>>(
cryptoContext->GetEvalAutomorphismKeyMap(kp1.secretKey->GetKeyTag()));
// Round 2 (party B)
kp2 = cryptoContext->MultipartyKeyGen(kp1.publicKey);
auto evalMultKey2 = cryptoContext->MultiKeySwitchGen(kp2.secretKey, kp2.secretKey, evalMultKey);
auto evalMultAB = cryptoContext->MultiAddEvalKeys(evalMultKey, evalMultKey2, kp2.publicKey->GetKeyTag());
auto evalMultBAB = cryptoContext->MultiMultEvalKey(kp2.secretKey, evalMultAB, kp2.publicKey->GetKeyTag());
auto evalSumKeysB = cryptoContext->MultiEvalSumKeyGen(kp2.secretKey, evalSumKeys, kp2.publicKey->GetKeyTag());
auto evalSumKeysJoin = cryptoContext->MultiAddEvalSumKeys(evalSumKeys, evalSumKeysB, kp2.publicKey->GetKeyTag());
cryptoContext->InsertEvalSumKey(evalSumKeysJoin);
auto evalAtIndexKeysB =
cryptoContext->MultiEvalAtIndexKeyGen(kp2.secretKey, evalAtIndexKeys, indices, kp2.publicKey->GetKeyTag());
auto evalAtIndexKeysJoin =
cryptoContext->MultiAddEvalAutomorphismKeys(evalAtIndexKeys, evalAtIndexKeysB, kp2.publicKey->GetKeyTag());
cryptoContext->InsertEvalAutomorphismKey(evalAtIndexKeysJoin);
auto evalMultAAB = cryptoContext->MultiMultEvalKey(kp1.secretKey, evalMultAB, kp2.publicKey->GetKeyTag());
auto evalMultFinal = cryptoContext->MultiAddEvalMultKeys(evalMultAAB, evalMultBAB, evalMultAB->GetKeyTag());
cryptoContext->InsertEvalMultKey({evalMultFinal});
/////////////////////
// Round 3 (party C) - Lead Party (who encrypts and finalizes the bootstrapping protocol)
kp3 = cryptoContext->MultipartyKeyGen(kp2.publicKey);
auto evalMultKey3 = cryptoContext->MultiKeySwitchGen(kp3.secretKey, kp3.secretKey, evalMultKey);
auto evalMultABC = cryptoContext->MultiAddEvalKeys(evalMultAB, evalMultKey3, kp3.publicKey->GetKeyTag());
auto evalMultBABC = cryptoContext->MultiMultEvalKey(kp2.secretKey, evalMultABC, kp3.publicKey->GetKeyTag());
auto evalMultAABC = cryptoContext->MultiMultEvalKey(kp1.secretKey, evalMultABC, kp3.publicKey->GetKeyTag());
auto evalMultCABC = cryptoContext->MultiMultEvalKey(kp3.secretKey, evalMultABC, kp3.publicKey->GetKeyTag());
auto evalMultABABC = cryptoContext->MultiAddEvalMultKeys(evalMultBABC, evalMultAABC, evalMultBABC->GetKeyTag());
auto evalMultFinal2 = cryptoContext->MultiAddEvalMultKeys(evalMultABABC, evalMultCABC, evalMultCABC->GetKeyTag());
cryptoContext->InsertEvalMultKey({evalMultFinal2});
auto evalAtIndexKeysC =
cryptoContext->MultiEvalAtIndexKeyGen(kp3.secretKey, evalAtIndexKeys, indices, kp3.publicKey->GetKeyTag());
auto evalAtIndexKeysJoin2 =
cryptoContext->MultiAddEvalAutomorphismKeys(evalAtIndexKeys, evalAtIndexKeysC, kp3.publicKey->GetKeyTag());
cryptoContext->InsertEvalAutomorphismKey(evalAtIndexKeysJoin2);
auto evalSumKeysC = cryptoContext->MultiEvalSumKeyGen(kp3.secretKey, evalSumKeys, kp3.publicKey->GetKeyTag());
auto evalSumKeysJoin2 = cryptoContext->MultiAddEvalSumKeys(evalSumKeys, evalSumKeysC, kp3.publicKey->GetKeyTag());
cryptoContext->InsertEvalSumKey(evalSumKeysJoin2);
if (!kp1.good()) {
std::cout << "Key generation failed!" << std::endl;
exit(1);
}
if (!kp2.good()) {
std::cout << "Key generation failed!" << std::endl;
exit(1);
}
if (!kp3.good()) {
std::cout << "Key generation failed!" << std::endl;
exit(1);
}
cryptoContext->EvalBootstrapKeyGen(kp3.secretKey, batchSize);
cryptoContext->EvalRotateKeyGen(kp3.secretKey, {1,2,3,-1,-2,-3});
// END of Key Generation
std::vector<double> input1 = {14.0,47.89746762162162,9.182932, 97.5,53.902815050857185, 14.932224832748549,25.70880986063563, 42.76932656905464};
std::vector<double> input2 = {11.0,47.89746762162162,10.008757825825825, 189.16666666666666,53.784931171127575, 70.36906413174,13.117850483332402, 16.90789035155603};
Plaintext pt1 = cryptoContext->MakeCKKSPackedPlaintext(input1);
Plaintext pt2 = cryptoContext->MakeCKKSPackedPlaintext(input2);
usint encodedLength = input1.size();
auto ct1 = cryptoContext->Encrypt(kp3.publicKey, pt1);
auto ct2 = cryptoContext->Encrypt(kp3.publicKey, pt2);
auto cLat1Rad = cryptoContext->EvalMult(0.0174533, ct1); //Decryption works at this point
auto cLon1Rad = cryptoContext->EvalAtIndex(cLat1Rad, indices[0]); //After rotation, I'm getting approximation error too high
// INTERACTIVE BOOTSTRAPPING STARTS
auto ca1 = cryptoContext->IntMPBootAdjustScale(cLon1Rad);
// Leading party (party C) generates a Common Random Poly (crp) at max coefficient modulus (QNumPrime).
// a is sampled at random uniformly from R_{Q}
auto crp = cryptoContext->IntMPBootRandomElementGen(kp3.publicKey);
// Each party generates its own shares: maskedDecryptionShare and reEncryptionShare
// (h_{0,i}, h_{1,i}) = (masked decryption share, re-encryption share)
// we use a vector inseat of std::pair for Python API compatibility
vector<Ciphertext<DCRTPoly>> sharesPair0; // for Party A
vector<Ciphertext<DCRTPoly>> sharesPair1; // for Party B
vector<Ciphertext<DCRTPoly>> sharesPair2; // for Party C
// extract c1 - element-wise
auto c1 = ca1->Clone();
c1->GetElements().erase(c1->GetElements().begin());
// masked decryption on the client: c1 = a*s1
sharesPair0 = cryptoContext->IntMPBootDecrypt(kp1.secretKey, c1, crp);
sharesPair1 = cryptoContext->IntMPBootDecrypt(kp2.secretKey, c1, crp);
sharesPair2 = cryptoContext->IntMPBootDecrypt(kp3.secretKey, c1, crp);
vector<vector<Ciphertext<DCRTPoly>>> sharesPairVec;
sharesPairVec.push_back(sharesPair0);
sharesPairVec.push_back(sharesPair1);
sharesPairVec.push_back(sharesPair2);
// Party B finalizes the protocol by aggregating the shares and reEncrypting the results
auto aggregatedSharesPair = cryptoContext->IntMPBootAdd(sharesPairVec);
auto ciphertextOutput = cryptoContext->IntMPBootEncrypt(kp3.publicKey, aggregatedSharesPair, crp, ca1);
// INTERACTIVE BOOTSTRAPPING ENDS
// distributed decryption
auto ciphertextPartial1 = cryptoContext->MultipartyDecryptLead({ciphertextOutput}, kp1.secretKey);
auto ciphertextPartial2 = cryptoContext->MultipartyDecryptMain({ciphertextOutput}, kp2.secretKey);
auto ciphertextPartial3 = cryptoContext->MultipartyDecryptMain({ciphertextOutput}, kp3.secretKey);
vector<Ciphertext<DCRTPoly>> partialCiphertextVec;
partialCiphertextVec.push_back(ciphertextPartial1[0]);
partialCiphertextVec.push_back(ciphertextPartial2[0]);
partialCiphertextVec.push_back(ciphertextPartial3[0]);
Plaintext plaintextMultiparty;
cryptoContext->MultipartyDecryptFusion(partialCiphertextVec, &plaintextMultiparty);
plaintextMultiparty->SetLength(encodedLength);