Hello, I’m trying to use the MultiPrecisionSignCipher function, but I’m encountering some problems. I’ve noticed some discussions suggesting that bootstrapping places the ciphertext in the last layer, making multiplication impossible. However, if I have a fresh ciphertext, and I want to use the MultiPrecisionSignCipher function, what should the ciphertext input be? I want to use the resulting sign function output in further calculations, such as multiplying it with another ciphertext. Should I use RLWE ciphertexts for this? How can I do this using standard CKKS encryption?
std::vector<lbcrypto::Poly> MultiPrecisionSignCipher(std::vector<lbcrypto::Poly> &ctxtBFV, lbcrypto::CryptoContextCKKSRNS::ContextType &cc,
//BigInteger PInput, BigInteger PDigit, BigInteger Q, BigInteger Bigq, uint64_t scaleTHI, uint64_t scaleStepTHI, size_t order)
BigInteger QBFVInit, BigInteger PInput, BigInteger PDigit, BigInteger Q, BigInteger Bigq, uint64_t scaleTHI, uint64_t scaleStepTHI, size_t order, uint32_t numSlots, uint32_t ringDim, uint32_t dcrtBits,
std::shared_ptr<lbcrypto::M4DCRTParams> &ep, lbcrypto::KeyPair<lbcrypto::DCRTPoly> &keyPair, uint32_t numSlotsCKKS, uint32_t depth, uint32_t levelsAvailableBeforeBootstrap
)
{
//auto cc = ctxtBFV->GetCryptoContext();
auto a = PInput.ConvertToInt<int64_t>();
auto b = PDigit.ConvertToInt<int64_t>();
auto funcMod = [b](int64_t x) -> int64_t {
return (x % b);
};
auto funcStep = [a, b](int64_t x) -> int64_t {
return (x % a) >= (b / 2);
};
std::vector<int64_t> coeffintMod;
std::vector<std::complex<double>> coeffcompMod;
std::vector<std::complex<double>> coeffcompStep;
bool binaryLUT = (PDigit.ConvertToInt() == 2) && (order == 1);
if (binaryLUT) {
coeffintMod = {funcMod(1), funcMod(0) - funcMod(1)};
}
else {
coeffcompMod = GetHermiteTrigCoefficients(funcMod, PDigit.ConvertToInt(), order, scaleTHI); // divided by 2
coeffcompStep = GetHermiteTrigCoefficients(funcStep, PDigit.ConvertToInt(), order,
scaleStepTHI); // divided by 2
}
SchemeletRLWEMP::ModSwitch(ctxtBFV, Q, QBFVInit);
uint32_t QBFVBits = Q.GetMSB() - 1;
/* 8. Set up the sign loop parameters. */
std::vector<int64_t> coeffint;
std::vector<std::complex<double>> coeffcomp;
if (binaryLUT)
coeffint = coeffintMod;
else
coeffcomp = coeffcompMod;
const bool checkeq2 = PDigit.ConvertToInt() == 2;
const bool checkgt2 = PDigit.ConvertToInt() > 2;
const uint32_t pDigitBits = PDigit.GetMSB() - 1;
BigInteger QNew;
BigInteger pOrig = PInput;
BigInteger Qorig = Q; // remember original Q so we can restore before returning
bool step = false;
bool go = QBFVBits > dcrtBits;
size_t levelsToDrop = 0;
uint32_t postScalingBits = 0;
/* 9. Start the sign loop. For arbitrary digit size, pNew > 2, the last iteration needs
* to evaluate step pNew not mod pNew.
* Currently this only works when log(pNew) divides log(p).
*/
while (go) {
auto encryptedDigit = ctxtBFV;
/* 9.1. Apply mod Bigq to extract the digit and convert it from RLWE to CKKS. */
encryptedDigit[0].SwitchModulus(Bigq, 1, 0, 0);
encryptedDigit[1].SwitchModulus(Bigq, 1, 0, 0);
auto ctxt = SchemeletRLWEMP::ConvertRLWEToCKKS(*cc, encryptedDigit, keyPair.publicKey, Bigq, numSlotsCKKS,
depth - (levelsAvailableBeforeBootstrap > 0));
/* 9.2 Bootstrap the digit.*/
Ciphertext<DCRTPoly> ctxtAfterFBT;
if (binaryLUT)
ctxtAfterFBT = cc->EvalFBT(ctxt, coeffint, pDigitBits, ep->GetModulus(), scaleTHI * (1 << postScalingBits),
levelsToDrop, order);
else
ctxtAfterFBT = cc->EvalFBT(ctxt, coeffcomp, pDigitBits, ep->GetModulus(), scaleTHI * (1 << postScalingBits),
levelsToDrop, order);
/* 9.3 Convert the result back to RLWE and update the
* plaintext and ciphertext modulus of the ciphertext for the next iteration.
*/
auto polys = SchemeletRLWEMP::ConvertCKKSToRLWE(ctxtAfterFBT, Q);
if (!step) {
/* 9.4 If not in the last iteration, subtract the digit from the ciphertext. */
ctxtBFV[0] = ctxtBFV[0] - polys[0];
ctxtBFV[1] = ctxtBFV[1] - polys[1];
/* 9.5 Do modulus switching from Q to QNew for the RLWE ciphertext. */
QNew = Q >> pDigitBits;
ctxtBFV[0] = ctxtBFV[0].MultiplyAndRound(QNew, Q);
ctxtBFV[0].SwitchModulus(QNew, 1, 0, 0);
ctxtBFV[1] = ctxtBFV[1].MultiplyAndRound(QNew, Q);
ctxtBFV[1].SwitchModulus(QNew, 1, 0, 0);
Q >>= pDigitBits;
PInput >>= pDigitBits;
QBFVBits -= pDigitBits;
postScalingBits += pDigitBits;
}
else {
/* 9.6 If in the last iteration, return the digit. */
ctxtBFV[0] = std::move(polys[0]);
ctxtBFV[1] = std::move(polys[1]);
}
/* 9.7 If in the last iteration, decrypt and assess correctness. */
go = QBFVBits > dcrtBits;
if (step || (checkeq2 && !go)) {
std::vector<int64_t> one_input = {1};
if (one_input.size() < numSlots)
one_input = Fill<int64_t>(one_input, numSlots);
auto ctxtone = SchemeletRLWEMP::EncryptCoeff(one_input, Q, PInput, keyPair.secretKey, ep);
ctxtBFV = {ctxtone[0] - ctxtBFV[0], ctxtone[1] - ctxtBFV[1]};
auto computed =
SchemeletRLWEMP::DecryptCoeff(ctxtBFV, Q, PInput, keyPair.secretKey, ep, numSlotsCKKS, numSlots);
std::cerr << "First 8 elements of the obtained sign: [";
std::copy_n(computed.begin(), 8, std::ostream_iterator<int64_t>(std::cerr, " "));
std::cerr << "]" << std::endl;
std::cout << Q << std::endl;
std::cout << PInput << std::endl;
}
/* 9.8 Determine whether it is the last iteration and if not, update the parameters for the next iteration. */
if (checkgt2 && !go && !step) {
if (!binaryLUT)
coeffcomp = coeffcompStep;
scaleTHI = scaleStepTHI;
step = true;
go = true;
if (coeffcompMod.size() > 4 && GetMultiplicativeDepthByCoeffVector(coeffcompMod, true) >
GetMultiplicativeDepthByCoeffVector(coeffcompStep, true)) {
levelsToDrop = GetMultiplicativeDepthByCoeffVector(coeffcompMod, true) -
GetMultiplicativeDepthByCoeffVector(coeffcompStep, true);
}
}
}
return ctxtBFV;
}