From 4ec49ec56dc1d70b88e6f1132340e76ed513d875 Mon Sep 17 00:00:00 2001 From: dsuponitskiy Date: Mon, 7 Oct 2024 09:53:59 -0400 Subject: [PATCH 01/15] Unnecessary data copy (#857) * Fix unnecessary data copies by correctly using std::move * Eliminated more unnecessary data copies * performance tuning * Added move semantics * Code clean up --------- Co-authored-by: Lei Chen Co-authored-by: Dmitriy Suponitskiy Co-authored-by: Carlo Pascoe --- .../include/lattice/hal/dcrtpoly-interface.h | 2 +- src/pke/lib/cryptocontext.cpp | 112 ++++++------------ src/pke/lib/encoding/ckkspackedencoding.cpp | 8 +- src/pke/lib/keyswitch/keyswitch-bv.cpp | 6 +- src/pke/lib/keyswitch/keyswitch-hybrid.cpp | 16 +-- src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp | 32 ++--- .../lib/scheme/ckksrns/ckksrns-leveledshe.cpp | 11 +- .../ckksrns/ckksrns-schemeswitching.cpp | 28 ++--- src/pke/lib/schemebase/base-multiparty.cpp | 7 +- src/pke/lib/schemerns/rns-leveledshe.cpp | 11 +- 10 files changed, 96 insertions(+), 137 deletions(-) diff --git a/src/core/include/lattice/hal/dcrtpoly-interface.h b/src/core/include/lattice/hal/dcrtpoly-interface.h index 77c3e483b..ba3343b59 100644 --- a/src/core/include/lattice/hal/dcrtpoly-interface.h +++ b/src/core/include/lattice/hal/dcrtpoly-interface.h @@ -326,7 +326,7 @@ class DCRTPolyInterface : public ILElement { * @param element The element to store */ void SetElementAtIndex(usint index, TowerType&& element) { - return this->GetDerived().SetElementAtIndex(index, element); + return this->GetDerived().SetElementAtIndex(index, std::move(element)); } /*********************************************************************** diff --git a/src/pke/lib/cryptocontext.cpp b/src/pke/lib/cryptocontext.cpp index 1451a4ed0..2d22aff51 100644 --- a/src/pke/lib/cryptocontext.cpp +++ b/src/pke/lib/cryptocontext.cpp @@ -376,8 +376,7 @@ Ciphertext CryptoContextImpl::EvalSum(ConstCiphertext ValidateCiphertext(ciphertext); auto evalSumKeys = CryptoContextImpl::GetEvalAutomorphismKeyMap(ciphertext->GetKeyTag()); - auto rv = GetScheme()->EvalSum(ciphertext, batchSize, evalSumKeys); - return rv; + return GetScheme()->EvalSum(ciphertext, batchSize, evalSumKeys); } template @@ -386,8 +385,7 @@ Ciphertext CryptoContextImpl::EvalSumRows(ConstCiphertextEvalSumRows(ciphertext, numRows, evalSumKeys, subringDim); - return rv; + return GetScheme()->EvalSumRows(ciphertext, numRows, evalSumKeys, subringDim); } template @@ -397,8 +395,7 @@ Ciphertext CryptoContextImpl::EvalSumCols( ValidateCiphertext(ciphertext); auto evalSumKeys = CryptoContextImpl::GetEvalAutomorphismKeyMap(ciphertext->GetKeyTag()); - auto rv = GetScheme()->EvalSumCols(ciphertext, numCols, evalSumKeys, evalSumKeysRight); - return rv; + return GetScheme()->EvalSumCols(ciphertext, numCols, evalSumKeys, evalSumKeysRight); } template @@ -406,17 +403,13 @@ Ciphertext CryptoContextImpl::EvalAtIndex(ConstCiphertextClone(); - return rv; + return ciphertext->Clone(); } auto evalAutomorphismKeys = CryptoContextImpl::GetEvalAutomorphismKeyMap(ciphertext->GetKeyTag()); - - auto rv = GetScheme()->EvalAtIndex(ciphertext, index, evalAutomorphismKeys); - return rv; + return GetScheme()->EvalAtIndex(ciphertext, index, evalAutomorphismKeys); } template @@ -425,10 +418,7 @@ Ciphertext CryptoContextImpl::EvalMerge( ValidateCiphertext(ciphertextVector[0]); auto evalAutomorphismKeys = CryptoContextImpl::GetEvalAutomorphismKeyMap(ciphertextVector[0]->GetKeyTag()); - - auto rv = GetScheme()->EvalMerge(ciphertextVector, evalAutomorphismKeys); - - return rv; + return GetScheme()->EvalMerge(ciphertextVector, evalAutomorphismKeys); } template @@ -436,15 +426,11 @@ Ciphertext CryptoContextImpl::EvalInnerProduct(ConstCiphertext ConstCiphertext ct2, usint batchSize) const { ValidateCiphertext(ct1); if (ct2 == nullptr || ct1->GetKeyTag() != ct2->GetKeyTag()) - OPENFHE_THROW( - "Information passed to EvalInnerProduct was not generated " - "with this crypto context"); + OPENFHE_THROW("Information was not generated with this crypto context"); auto evalSumKeys = CryptoContextImpl::GetEvalAutomorphismKeyMap(ct1->GetKeyTag()); auto ek = CryptoContextImpl::GetEvalMultKeyVector(ct1->GetKeyTag()); - - auto rv = GetScheme()->EvalInnerProduct(ct1, ct2, batchSize, evalSumKeys, ek[0]); - return rv; + return GetScheme()->EvalInnerProduct(ct1, ct2, batchSize, evalSumKeys, ek[0]); } template @@ -452,14 +438,10 @@ Ciphertext CryptoContextImpl::EvalInnerProduct(ConstCiphertext usint batchSize) const { ValidateCiphertext(ct1); if (ct2 == nullptr) - OPENFHE_THROW( - "Information passed to EvalInnerProduct was not generated " - "with this crypto context"); + OPENFHE_THROW("Information was not generated with this crypto context"); auto evalSumKeys = CryptoContextImpl::GetEvalAutomorphismKeyMap(ct1->GetKeyTag()); - - auto rv = GetScheme()->EvalInnerProduct(ct1, ct2, batchSize, evalSumKeys); - return rv; + return GetScheme()->EvalInnerProduct(ct1, ct2, batchSize, evalSumKeys); } template @@ -477,9 +459,9 @@ template DecryptResult CryptoContextImpl::Decrypt(ConstCiphertext ciphertext, const PrivateKey privateKey, Plaintext* plaintext) { if (ciphertext == nullptr) - OPENFHE_THROW("ciphertext passed to Decrypt is empty"); + OPENFHE_THROW("ciphertext is empty"); if (plaintext == nullptr) - OPENFHE_THROW("plaintext passed to Decrypt is empty"); + OPENFHE_THROW("plaintext is empty"); ValidateKey(privateKey); // determine which type of plaintext that you need to decrypt into @@ -582,13 +564,11 @@ template <> DecryptResult CryptoContextImpl::Decrypt(ConstCiphertext ciphertext, const PrivateKey privateKey, Plaintext* plaintext) { if (ciphertext == nullptr) - OPENFHE_THROW("ciphertext passed to Decrypt is empty"); + OPENFHE_THROW("ciphertext is empty"); if (plaintext == nullptr) - OPENFHE_THROW("plaintext passed to Decrypt is empty"); + OPENFHE_THROW("plaintext is empty"); if (privateKey == nullptr || Mismatched(privateKey->GetCryptoContext())) - OPENFHE_THROW( - "Information passed to Decrypt was not generated with " - "this crypto context"); + OPENFHE_THROW("Information was not generated with this crypto context"); // determine which type of plaintext that you need to decrypt into // Plaintext decrypted = @@ -643,9 +623,7 @@ DecryptResult CryptoContextImpl::MultipartyDecryptFusion( for (size_t i = 0; i < last_ciphertext; i++) { ValidateCiphertext(partialCiphertextVec[i]); if (partialCiphertextVec[i]->GetEncodingType() != partialCiphertextVec[0]->GetEncodingType()) - OPENFHE_THROW( - "Ciphertexts passed to MultipartyDecryptFusion have " - "mismatched encoding types"); + OPENFHE_THROW("Ciphertexts have mismatched encoding types"); } // determine which type of plaintext that you need to decrypt into @@ -730,14 +708,13 @@ std::unordered_map CryptoContextImpl::ShareKeys(co auto ring_dimension = elementParams->GetRingDimension(); // condition for inverse in lagrange coeff to exist. - for (usint k = 0; k < vecSize; k++) { - auto modq_k = elementParams->GetParams()[k]->GetModulus(); + for (size_t i = 0; i < vecSize; ++i) { + auto modq_k = elementParams->GetParams()[i]->GetModulus(); if (N >= modq_k) OPENFHE_THROW("Number of parties N needs to be less than DCRTPoly moduli"); } // secret sharing - const usint num_of_shares = N - 1; std::unordered_map SecretShares; if (shareType == "additive") { @@ -745,6 +722,7 @@ std::unordered_map CryptoContextImpl::ShareKeys(co typename DCRTPoly::DugType dug; DCRTPoly rsum(dug, elementParams, Format::EVALUATION); + const uint32_t num_of_shares = N - 1; std::vector SecretSharesVec; SecretSharesVec.reserve(num_of_shares); SecretSharesVec.push_back(rsum); @@ -755,66 +733,54 @@ std::unordered_map CryptoContextImpl::ShareKeys(co } SecretSharesVec.push_back(sk->GetPrivateElement() - rsum); - usint ctr = 0; - for (size_t i = 1; i <= N; i++) { + for (size_t i = 1, ctr = 0; i <= N; ++i) { if (i != index) { - SecretShares[i] = SecretSharesVec[ctr]; - ctr++; + SecretShares[i] = SecretSharesVec[ctr++]; } } } else if (shareType == "shamir") { // vector to store columnwise randomly generated coefficients for polynomial f from Z_q for every secret key entry - std::vector fs; - fs.reserve(threshold); - // set constant term of polynomial f_i to s_i - DCRTPoly ske = sk->GetPrivateElement(); - // set the secret element in coefficient format - ske.SetFormat(Format::COEFFICIENT); + std::vector fs{sk->GetPrivateElement()}; + fs.back().SetFormat(Format::COEFFICIENT); - fs.push_back(std::move(ske)); // generate random coefficients + fs.reserve(threshold); typename DCRTPoly::DugType dug; - for (size_t i = 1; i < threshold; i++) { - fs.push_back(DCRTPoly(dug, elementParams, Format::COEFFICIENT)); + for (size_t i = 1; i < threshold; ++i) { + fs.emplace_back(dug, elementParams, Format::COEFFICIENT); } // evaluate the polynomial at the index of the parties 1 to N - - for (size_t i = 1; i <= N; i++) { + for (size_t i = 1; i <= N; ++i) { if (i != index) { DCRTPoly feval(elementParams, Format::COEFFICIENT, true); for (size_t k = 0; k < vecSize; k++) { auto modq_k = elementParams->GetParams()[k]->GetModulus(); - NativeVector powtempvec(ring_dimension, modq_k); NativePoly powtemppoly(elementParams->GetParams()[k], Format::COEFFICIENT); NativePoly fevalpoly(elementParams->GetParams()[k], Format::COEFFICIENT, true); NativeInteger powtemp(1); for (size_t t = 1; t < threshold; t++) { - powtemp = powtemp.ModMul(i, modq_k); - - for (size_t d = 0; d < ring_dimension; d++) { - powtempvec.at(d) = powtemp; - } + NativeVector powtempvec(ring_dimension, modq_k, (powtemp = powtemp.ModMul(i, modq_k))); - powtemppoly.SetValues(powtempvec, Format::COEFFICIENT); + powtemppoly.SetValues(std::move(powtempvec), Format::COEFFICIENT); - auto fst = fs[t].GetElementAtIndex(k); + auto& fst = fs[t].GetElementAtIndex(k); - for (size_t l = 0; l < ring_dimension; l++) { - fevalpoly.at(l) += powtemppoly.at(l).ModMul(fst.at(l), modq_k); + for (size_t i = 0; i < ring_dimension; ++i) { + fevalpoly[i] += powtemppoly[i].ModMul(fst[i], modq_k); } } fevalpoly += fs[0].GetElementAtIndex(k); fevalpoly.SetFormat(Format::COEFFICIENT); - feval.SetElementAtIndex(k, fevalpoly); + feval.SetElementAtIndex(k, std::move(fevalpoly)); } // assign fi - SecretShares[i] = feval; + SecretShares.emplace(i, std::move(feval)); } } } @@ -864,7 +830,7 @@ void CryptoContextImpl::RecoverSharedKey(PrivateKey& sk, for (uint32_t i = 0; i < threshold; ++i) { sum_of_elems += sk_shares[client_indexes[i]]; } - sk->SetPrivateElement(sum_of_elems); + sk->SetPrivateElement(std::move(sum_of_elems)); } else if (shareType == "shamir") { // use lagrange interpolation to recover the secret @@ -888,7 +854,7 @@ void CryptoContextImpl::RecoverSharedKey(PrivateKey& sk, } } multpoly.SetFormat(Format::EVALUATION); - Lagrange_coeffs[j].SetElementAtIndex(k, multpoly); + Lagrange_coeffs[j].SetElementAtIndex(k, std::move(multpoly)); } Lagrange_coeffs[j].SetFormat(Format::COEFFICIENT); } @@ -901,10 +867,10 @@ void CryptoContextImpl::RecoverSharedKey(PrivateKey& sk, const auto& share = sk_shares[client_indexes[i]].GetAllElements()[k]; lagrange_sum_of_elems_poly += coeff.TimesNoCheck(share); } - lagrange_sum_of_elems.SetElementAtIndex(k, lagrange_sum_of_elems_poly); + lagrange_sum_of_elems.SetElementAtIndex(k, std::move(lagrange_sum_of_elems_poly)); } lagrange_sum_of_elems.SetFormat(Format::EVALUATION); - sk->SetPrivateElement(lagrange_sum_of_elems); + sk->SetPrivateElement(std::move(lagrange_sum_of_elems)); } } diff --git a/src/pke/lib/encoding/ckkspackedencoding.cpp b/src/pke/lib/encoding/ckkspackedencoding.cpp index b646af390..b8bd4b576 100644 --- a/src/pke/lib/encoding/ckkspackedencoding.cpp +++ b/src/pke/lib/encoding/ckkspackedencoding.cpp @@ -205,8 +205,8 @@ bool CKKSPackedEncoding::Encode() { NativeVector nativeVec(ringDim, nativeParams[i]->GetModulus()); FitToNativeVector(temp, Max128BitValue(), &nativeVec); NativePoly element = this->GetElement().GetElementAtIndex(i); - element.SetValues(nativeVec, Format::COEFFICIENT); // output was in coefficient format - this->encodedVectorDCRT.SetElementAtIndex(i, element); + element.SetValues(std::move(nativeVec), Format::COEFFICIENT); // output was in coefficient format + this->encodedVectorDCRT.SetElementAtIndex(i, std::move(element)); } usint numTowers = nativeParams.size(); @@ -366,8 +366,8 @@ bool CKKSPackedEncoding::Encode() { NativeVector nativeVec(ringDim, nativeParams[i]->GetModulus()); FitToNativeVector(temp, Max64BitValue(), &nativeVec); NativePoly element = this->GetElement().GetElementAtIndex(i); - element.SetValues(nativeVec, Format::COEFFICIENT); // output was in coefficient format - this->encodedVectorDCRT.SetElementAtIndex(i, element); + element.SetValues(std::move(nativeVec), Format::COEFFICIENT); // output was in coefficient format + this->encodedVectorDCRT.SetElementAtIndex(i, std::move(element)); } usint numTowers = nativeParams.size(); diff --git a/src/pke/lib/keyswitch/keyswitch-bv.cpp b/src/pke/lib/keyswitch/keyswitch-bv.cpp index a5055865d..5a7d92356 100644 --- a/src/pke/lib/keyswitch/keyswitch-bv.cpp +++ b/src/pke/lib/keyswitch/keyswitch-bv.cpp @@ -233,11 +233,9 @@ EvalKey KeySwitchBV::KeySwitchGenInternal(const PrivateKey o av.reserve(sOld.GetNumOfElements() * digitSize); bv.reserve(sOld.GetNumOfElements() * digitSize); for (usint i = 0; i < sOld.GetNumOfElements(); i++) { - std::vector sOldDecomposed = sOld.GetElementAtIndex(i).PowersOfBase(digitSize); - - for (size_t k = 0; k < sOldDecomposed.size(); k++) { + for (auto&& sOldDecomposed : sOld.GetElementAtIndex(i).PowersOfBase(digitSize)) { DCRTPoly filtered(elementParams, Format::EVALUATION, true); - filtered.SetElementAtIndex(i, sOldDecomposed[k]); + filtered.SetElementAtIndex(i, std::move(sOldDecomposed)); DCRTPoly u = (cryptoParams->GetSecretKeyDist() == GAUSSIAN) ? DCRTPoly(dgg, elementParams, Format::EVALUATION) : diff --git a/src/pke/lib/keyswitch/keyswitch-hybrid.cpp b/src/pke/lib/keyswitch/keyswitch-hybrid.cpp index 6df1fd37e..fb7cd35c8 100644 --- a/src/pke/lib/keyswitch/keyswitch-hybrid.cpp +++ b/src/pke/lib/keyswitch/keyswitch-hybrid.cpp @@ -244,13 +244,13 @@ Ciphertext KeySwitchHYBRID::KeySwitchExt(ConstCiphertext cip if ((addFirst) || (k > 0)) { auto cMult = cv[k].TimesNoCheck(cryptoParams->GetPModq()); for (usint i = 0; i < sizeQl; i++) { - resultElements[k].SetElementAtIndex(i, cMult.GetElementAtIndex(i)); + resultElements[k].SetElementAtIndex(i, std::move(cMult.GetElementAtIndex(i))); } } } Ciphertext result = ciphertext->CloneZero(); - result->SetElements(resultElements); + result->SetElements(std::move(resultElements)); return result; } @@ -287,7 +287,7 @@ Ciphertext KeySwitchHYBRID::KeySwitchDown(ConstCiphertext ci cryptoParams->GettInvModpPrecon(), t, cryptoParams->GettModqPrecon()); Ciphertext result = ciphertext->CloneZero(); - result->SetElements({ct0, ct1}); + result->SetElements(std::vector{std::move(ct0), std::move(ct1)}); return result; } @@ -386,11 +386,11 @@ std::shared_ptr> KeySwitchHYBRID::EvalKeySwitchPrecomputeC uint32_t sizePartQl = partsCt[part].GetNumOfElements(); partsCtCompl[part] = partCtClone.ApproxSwitchCRTBasis( - cryptoParams->GetParamsPartQ(part), cryptoParams->GetParamsComplPartQ(sizeQl - 1, part), - cryptoParams->GetPartQlHatInvModq(part, sizePartQl - 1), - cryptoParams->GetPartQlHatInvModqPrecon(part, sizePartQl - 1), - cryptoParams->GetPartQlHatModp(sizeQl - 1, part), - cryptoParams->GetmodComplPartqBarrettMu(sizeQl - 1, part)); + cryptoParams->GetParamsPartQ(part), cryptoParams->GetParamsComplPartQ(sizeQl - 1, part), + cryptoParams->GetPartQlHatInvModq(part, sizePartQl - 1), + cryptoParams->GetPartQlHatInvModqPrecon(part, sizePartQl - 1), + cryptoParams->GetPartQlHatModp(sizeQl - 1, part), + cryptoParams->GetmodComplPartqBarrettMu(sizeQl - 1, part)); partsCtCompl[part].SetFormat(Format::EVALUATION); diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp index 69cdd6901..d1cd57700 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp @@ -487,8 +487,8 @@ Ciphertext FHECKKSRNS::EvalBootstrap(ConstCiphertext ciphert ctxtDCRT[i] = temp; } - raised->SetElements(ctxtDCRT); raised->SetLevel(L0 - ctxtDCRT[0].GetNumOfElements()); + raised->SetElements(std::move(ctxtDCRT)); #ifdef BOOTSTRAPTIMING std::cerr << "\nNumber of levels at the beginning of bootstrapping: " @@ -1616,7 +1616,7 @@ Ciphertext FHECKKSRNS::EvalLinearTransform(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); result = inner; } else { @@ -1636,7 +1636,7 @@ Ciphertext FHECKKSRNS::EvalLinearTransform(const std::vectorKeySwitchDown(result); auto elements = result->GetElements(); elements[0] += first; - result->SetElements(elements); + result->SetElements(std::move(elements)); return result; } @@ -1756,7 +1756,7 @@ Ciphertext FHECKKSRNS::EvalCoeffsToSlots(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); outer = inner; } else { @@ -1774,7 +1774,7 @@ Ciphertext FHECKKSRNS::EvalCoeffsToSlots(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); EvalAddExtInPlace(outer, inner); } } @@ -1819,7 +1819,7 @@ Ciphertext FHECKKSRNS::EvalCoeffsToSlots(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); outer = inner; } else { @@ -1837,7 +1837,7 @@ Ciphertext FHECKKSRNS::EvalCoeffsToSlots(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); EvalAddExtInPlace(outer, inner); } } @@ -1969,7 +1969,7 @@ Ciphertext FHECKKSRNS::EvalSlotsToCoeffs(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); outer = inner; } else { @@ -1987,7 +1987,7 @@ Ciphertext FHECKKSRNS::EvalSlotsToCoeffs(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); EvalAddExtInPlace(outer, inner); } } @@ -2032,7 +2032,7 @@ Ciphertext FHECKKSRNS::EvalSlotsToCoeffs(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); outer = inner; } else { @@ -2050,7 +2050,7 @@ Ciphertext FHECKKSRNS::EvalSlotsToCoeffs(const std::vectorKeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); EvalAddExtInPlace(outer, inner); } } @@ -2264,8 +2264,8 @@ Plaintext FHECKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, co NativeVector nativeVec(N, nativeParams[i]->GetModulus()); FitToNativeVector(N, temp, Max128BitValue(), &nativeVec); NativePoly element = plainElement.GetElementAtIndex(i); - element.SetValues(nativeVec, Format::COEFFICIENT); - plainElement.SetElementAtIndex(i, element); + element.SetValues(std::move(nativeVec), Format::COEFFICIENT); + plainElement.SetElementAtIndex(i, std::move(element)); } usint numTowers = nativeParams.size(); @@ -2407,8 +2407,8 @@ Plaintext FHECKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, co NativeVector nativeVec(N, nativeParams[i]->GetModulus()); FitToNativeVector(N, temp, Max64BitValue(), &nativeVec); NativePoly element = plainElement.GetElementAtIndex(i); - element.SetValues(nativeVec, Format::COEFFICIENT); - plainElement.SetElementAtIndex(i, element); + element.SetValues(std::move(nativeVec), Format::COEFFICIENT); + plainElement.SetElementAtIndex(i, std::move(element)); } usint numTowers = nativeParams.size(); @@ -2503,7 +2503,7 @@ EvalKey FHECKKSRNS::ConjugateKeyGen(const PrivateKey private DCRTPoly sPermuted = s.AutomorphismTransform(index, vec); - privateKeyPermuted->SetPrivateElement(sPermuted); + privateKeyPermuted->SetPrivateElement(std::move(sPermuted)); privateKeyPermuted->SetKeyTag(privateKey->GetKeyTag()); return algo->KeySwitchGen(privateKey, privateKeyPermuted); diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp index e1d12ac34..c12108ade 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp @@ -472,7 +472,7 @@ Ciphertext LeveledSHECKKSRNS::EvalFastRotationExt(ConstCiphertextGetElements()[0].TimesNoCheck(cryptoParams->GetPModq()); for (usint i = 0; i < sizeQl; i++) { - psiC0.SetElementAtIndex(i, cMult.GetElementAtIndex(i)); + psiC0.SetElementAtIndex(i, std::move(cMult.GetElementAtIndex(i))); } (*cTilda)[0] += psiC0; } @@ -493,12 +493,13 @@ Ciphertext LeveledSHECKKSRNS::EvalFastRotationExt(ConstCiphertext LeveledSHECKKSRNS::MultByInteger(ConstCiphertext ciphertext, uint64_t integer) const { const std::vector& cv = ciphertext->GetElements(); - std::vector resultDCRT(cv.size()); - for (usint i = 0; i < cv.size(); i++) - resultDCRT[i] = cv[i].Times(NativeInteger(integer)); + std::vector resultDCRT; + resultDCRT.reserve(cv.size()); + for (const auto& elem : cv) + resultDCRT.push_back(elem.Times(NativeInteger(integer))); Ciphertext result = ciphertext->CloneZero(); - result->SetElements(resultDCRT); + result->SetElements(std::move(resultDCRT)); return result; } diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-schemeswitching.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-schemeswitching.cpp index 2a949ba4b..901e9325a 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-schemeswitching.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-schemeswitching.cpp @@ -269,8 +269,8 @@ Plaintext SWITCHCKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, NativeVector nativeVec(N, nativeParams[i]->GetModulus()); FitToNativeVector(N, temp, Max128BitValue(), &nativeVec); NativePoly element = plainElement.GetElementAtIndex(i); - element.SetValues(nativeVec, Format::COEFFICIENT); - plainElement.SetElementAtIndex(i, element); + element.SetValues(std::move(nativeVec), Format::COEFFICIENT); + plainElement.SetElementAtIndex(i, std::move(element)); } usint numTowers = nativeParams.size(); @@ -415,8 +415,8 @@ Plaintext SWITCHCKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, NativeVector nativeVec(N, nativeParams[i]->GetModulus()); FitToNativeVector(N, temp, Max64BitValue(), &nativeVec); NativePoly element = plainElement.GetElementAtIndex(i); - element.SetValues(nativeVec, Format::COEFFICIENT); - plainElement.SetElementAtIndex(i, element); + element.SetValues(std::move(nativeVec), Format::COEFFICIENT); + plainElement.SetElementAtIndex(i, std::move(element)); } usint numTowers = nativeParams.size(); @@ -511,7 +511,7 @@ EvalKey SWITCHCKKSRNS::ConjugateKeyGen(const PrivateKey priv DCRTPoly sPermuted = s.AutomorphismTransform(index, vec); - privateKeyPermuted->SetPrivateElement(sPermuted); + privateKeyPermuted->SetPrivateElement(std::move(sPermuted)); privateKeyPermuted->SetKeyTag(privateKey->GetKeyTag()); return algo->KeySwitchGen(privateKey, privateKeyPermuted); @@ -615,7 +615,7 @@ EvalKey switchingKeyGenRLWE( skelementsPlain[j] = skelementsPlain.GetModulus() - 1; } } - skelements.SetElementAtIndex(i, skelementsPlain); + skelements.SetElementAtIndex(i, std::move(skelementsPlain)); } skelements.SetFormat(Format::EVALUATION); @@ -648,7 +648,7 @@ void ModSwitch(ConstCiphertext ctxt, Ciphertext& ctxtKS, Nat ref.SetFormat(Format::EVALUATION); } - ctxtKS->SetElements(resultElements); + ctxtKS->SetElements(std::move(resultElements)); } EvalKey switchingKeyGen(const PrivateKey& ckksSKto, const PrivateKey& ckksSKfrom) { @@ -670,7 +670,7 @@ EvalKey switchingKeyGen(const PrivateKey& ckksSKto, const Pr else skElementsPlain[j] = skElementsPlain.GetModulus() - 1; } - skElements.SetElementAtIndex(i, skElementsPlain); + skElements.SetElementAtIndex(i, std::move(skElementsPlain)); } skElements.SetFormat(Format::EVALUATION); @@ -720,8 +720,8 @@ EvalKey switchingKeyGenRLWEcc(const PrivateKey& ckksSKto, co skElementsPlainLWE[j] = skElementsPlain.GetModulus() - 1; } } - skElements.SetElementAtIndex(i, skElementsPlain); - skElements2.SetElementAtIndex(i, skElementsPlainLWE); + skElements.SetElementAtIndex(i, std::move(skElementsPlain)); + skElements2.SetElementAtIndex(i, std::move(skElementsPlainLWE)); } skElements.SetFormat(Format::EVALUATION); @@ -998,7 +998,7 @@ Ciphertext SWITCHCKKSRNS::EvalLTWithPrecomputeSwitch(const CryptoConte first = cc.KeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); result = inner; } else { @@ -1018,7 +1018,7 @@ Ciphertext SWITCHCKKSRNS::EvalLTWithPrecomputeSwitch(const CryptoConte result = cc.KeySwitchDown(result); auto elements = result->GetElements(); elements[0] += first; - result->SetElements(elements); + result->SetElements(std::move(elements)); return result; } @@ -1104,7 +1104,7 @@ Ciphertext SWITCHCKKSRNS::EvalLTRectWithPrecomputeSwitch( first = cc.KeySwitchDownFirstElement(inner); auto elements = inner->GetElements(); elements[0].SetValuesToZero(); - inner->SetElements(elements); + inner->SetElements(std::move(elements)); result = inner; } else { @@ -1123,7 +1123,7 @@ Ciphertext SWITCHCKKSRNS::EvalLTRectWithPrecomputeSwitch( result = cc.KeySwitchDown(result); auto elements = result->GetElements(); elements[0] += first; - result->SetElements(elements); + result->SetElements(std::move(elements)); // A represents the diagonals, which lose the information whether the initial matrix is tall or wide if (wide) { diff --git a/src/pke/lib/schemebase/base-multiparty.cpp b/src/pke/lib/schemebase/base-multiparty.cpp index d4cd4e194..aef2d0ee4 100644 --- a/src/pke/lib/schemebase/base-multiparty.cpp +++ b/src/pke/lib/schemebase/base-multiparty.cpp @@ -160,7 +160,7 @@ std::shared_ptr>> MultipartyBase::Mult PrecomputeAutoMap(N, index, &vec); Element sPermuted = s.AutomorphismTransform(index, vec); - privateKeyPermuted->SetPrivateElement(sPermuted); + privateKeyPermuted->SetPrivateElement(std::move(sPermuted)); // verify if the key indexList[i] exists in the evalKeyMap auto evalKeyIterator = evalKeyMap->find(indexList[i]); @@ -185,9 +185,8 @@ std::shared_ptr>> MultipartyBase::Mult std::vector autoIndices(indexList.size()); for (size_t i = 0; i < indexList.size(); i++) { - autoIndices[i] = (isCKKS(cc->getSchemeId())) ? - FindAutomorphismIndex2nComplex(indexList[i], M) : - FindAutomorphismIndex2n(indexList[i], M); + autoIndices[i] = (isCKKS(cc->getSchemeId())) ? FindAutomorphismIndex2nComplex(indexList[i], M) : + FindAutomorphismIndex2n(indexList[i], M); } return MultiEvalAutomorphismKeyGen(privateKey, evalKeyMap, autoIndices); diff --git a/src/pke/lib/schemerns/rns-leveledshe.cpp b/src/pke/lib/schemerns/rns-leveledshe.cpp index c9aedea1d..85ed30e7b 100644 --- a/src/pke/lib/schemerns/rns-leveledshe.cpp +++ b/src/pke/lib/schemerns/rns-leveledshe.cpp @@ -478,22 +478,17 @@ void LeveledSHERNS::AdjustForAddOrSubInPlace(Ciphertext& ciphertext1, crtPowSF = CKKSPackedEncoding::CRTMult(crtPowSF, crtSF, moduli); } - ptxt = ptxt.Times(crtPowSF); - if (ptxtIndex == 1) { - ciphertext1->SetElements({ptxt}); + ciphertext1->SetElements(std::vector{ptxt.Times(crtPowSF)}); ciphertext1->SetNoiseScaleDeg(ctxtDepth); } else { - ciphertext2->SetElements({ptxt}); + ciphertext2->SetElements(std::vector{ptxt.Times(crtPowSF)}); ciphertext2->SetNoiseScaleDeg(ctxtDepth); } } else if (ptxtDepth > ctxtDepth) { - OPENFHE_THROW( - "LPAlgorithmSHERNS::AdjustForAddOrSubInPlace " - "- plaintext cannot be encoded at a larger depth than that " - "of the ciphertext."); + OPENFHE_THROW("plaintext cannot be encoded at a larger depth than that of the ciphertext."); } } else if (cryptoParams->GetScalingTechnique() != NORESCALE) { From cf579ba9055ce066603764d5f1148b206787122a Mon Sep 17 00:00:00 2001 From: dsuponitskiy Date: Mon, 7 Oct 2024 09:59:15 -0400 Subject: [PATCH 02/15] Fixed an if condition (#875) Co-authored-by: Dmitriy Suponitskiy --- src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp index c12108ade..d25ee6f92 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-leveledshe.cpp @@ -390,7 +390,7 @@ std::vector LeveledSHECKKSRNS::GetElementForEvalMult(ConstCip std::vector factors(numTowers); - if (large_abs > bound) { + if (large_abs >= bound) { for (usint i = 0; i < numTowers; i++) { DoubleInteger reduced = large % moduli[i].ConvertToInt(); From 9ae159f8f2022922ab18bcf20270aefabc0fb3f9 Mon Sep 17 00:00:00 2001 From: pascoec <123595534+pascoec@users.noreply.github.com> Date: Fri, 18 Oct 2024 13:30:21 -0400 Subject: [PATCH 03/15] Binfhe extended (#873) * added move constructor for RLWECiphertextImpl * add initial extended functionality to EvalBinGate and Bootstrapping * additional extended functionality; bug fix for pk encryption; performance optimizations * unit tests for extended functionality * bug fix for BinFHEScheme::BootstrapFunc() * update to binfhe-paramsets benchmark * update to operator<<(std::ostream& s, BINFHE_OUTPUT f); * limit use of FRESH BINFHE_OUTPUT * limit use of FRESH and BOOTSTRAPPED BINFHE_OUTPUT * changed default BINFHE_OUTPUT for secret key encryption to SMALL_DIM --- benchmark/src/binfhe-ap.cpp | 6 +- benchmark/src/binfhe-ginx.cpp | 6 +- benchmark/src/binfhe-lmkcdey.cpp | 6 +- benchmark/src/binfhe-paramsets.cpp | 86 ++++----- src/binfhe/examples/boolean-ap.cpp | 2 +- src/binfhe/examples/boolean-lmkcdey.cpp | 2 +- ...-serial-binary-dynamic-large-precision.cpp | 2 +- ...an-serial-json-dynamic-large-precision.cpp | 2 +- src/binfhe/examples/boolean.cpp | 2 +- src/binfhe/examples/eval-decomp.cpp | 2 +- src/binfhe/examples/eval-flooring.cpp | 2 +- src/binfhe/examples/eval-function.cpp | 2 +- src/binfhe/examples/eval-sign.cpp | 2 +- src/binfhe/examples/pke/boolean-pke.cpp | 2 +- src/binfhe/include/binfhe-base-scheme.h | 6 +- src/binfhe/include/binfhe-constants.h | 4 +- src/binfhe/include/binfhecontext.h | 12 +- src/binfhe/include/lwe-keypair.h | 3 +- src/binfhe/include/lwe-keyswitchkey.h | 8 +- src/binfhe/include/lwe-publickey.h | 4 +- src/binfhe/include/rlwe-ciphertext.h | 2 + src/binfhe/lib/binfhe-base-scheme.cpp | 182 +++++++++--------- src/binfhe/lib/binfhe-constants-impl.cpp | 6 + src/binfhe/lib/binfhecontext.cpp | 18 +- src/binfhe/lib/lwe-pke.cpp | 16 +- src/binfhe/unittest/UnitTestFHEW.cpp | 12 +- src/binfhe/unittest/UnitTestFHEWExtended.cpp | 153 +++++++++++++++ src/binfhe/unittest/UnitTestFHEWPKESerial.cpp | 4 +- src/binfhe/unittest/UnitTestFHEWSerial.cpp | 12 +- src/binfhe/unittest/UnitTestFunc.cpp | 12 +- src/pke/examples/scheme-switching.cpp | 28 ++- src/pke/extras/scheme-switching-timing.cpp | 5 +- .../utckksrns/UnitTestSchemeSwitch.cpp | 4 +- 33 files changed, 389 insertions(+), 226 deletions(-) create mode 100644 src/binfhe/unittest/UnitTestFHEWExtended.cpp diff --git a/benchmark/src/binfhe-ap.cpp b/benchmark/src/binfhe-ap.cpp index c96db6815..344ccc7d9 100644 --- a/benchmark/src/binfhe-ap.cpp +++ b/benchmark/src/binfhe-ap.cpp @@ -72,7 +72,7 @@ void FHEW_ENCRYPT(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); for (auto _ : state) { - LWECiphertext ct1 = cc.Encrypt(sk, 1, FRESH); + LWECiphertext ct1 = cc.Encrypt(sk, 1, SMALL_DIM); } } @@ -87,7 +87,7 @@ void FHEW_NOT(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); - LWECiphertext ct1 = cc.Encrypt(sk, 1, FRESH); + LWECiphertext ct1 = cc.Encrypt(sk, 1, SMALL_DIM); for (auto _ : state) { LWECiphertext ct11 = cc.EvalNOT(ct1); @@ -163,7 +163,7 @@ void FHEW_KEYSWITCH(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); LWEPrivateKey skN = cc.KeyGenN(); - auto ctQN1 = cc.Encrypt(skN, 1, FRESH); + auto ctQN1 = cc.Encrypt(skN, 1, SMALL_DIM); auto keySwitchHint = cc.KeySwitchGen(sk, skN); for (auto _ : state) { diff --git a/benchmark/src/binfhe-ginx.cpp b/benchmark/src/binfhe-ginx.cpp index b2f06003b..f5237de67 100644 --- a/benchmark/src/binfhe-ginx.cpp +++ b/benchmark/src/binfhe-ginx.cpp @@ -73,7 +73,7 @@ void FHEW_ENCRYPT(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); for (auto _ : state) { - LWECiphertext ct1 = cc.Encrypt(sk, 1, FRESH); + LWECiphertext ct1 = cc.Encrypt(sk, 1, SMALL_DIM); } } @@ -87,7 +87,7 @@ void FHEW_NOT(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); - LWECiphertext ct1 = cc.Encrypt(sk, 1, FRESH); + LWECiphertext ct1 = cc.Encrypt(sk, 1, SMALL_DIM); for (auto _ : state) { LWECiphertext ct11 = cc.EvalNOT(ct1); @@ -150,7 +150,7 @@ void FHEW_KEYSWITCH(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); LWEPrivateKey skN = cc.KeyGenN(); - auto ctQN1 = cc.Encrypt(skN, 1, FRESH); + auto ctQN1 = cc.Encrypt(skN, 1, SMALL_DIM); auto keySwitchHint = cc.KeySwitchGen(sk, skN); for (auto _ : state) { diff --git a/benchmark/src/binfhe-lmkcdey.cpp b/benchmark/src/binfhe-lmkcdey.cpp index 3a01aed45..2fdef364c 100644 --- a/benchmark/src/binfhe-lmkcdey.cpp +++ b/benchmark/src/binfhe-lmkcdey.cpp @@ -73,7 +73,7 @@ void FHEW_ENCRYPT(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); for (auto _ : state) { - LWECiphertext ct1 = cc.Encrypt(sk, 1, FRESH); + LWECiphertext ct1 = cc.Encrypt(sk, 1, SMALL_DIM); } } @@ -87,7 +87,7 @@ void FHEW_NOT(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); - LWECiphertext ct1 = cc.Encrypt(sk, 1, FRESH); + LWECiphertext ct1 = cc.Encrypt(sk, 1, SMALL_DIM); for (auto _ : state) { LWECiphertext ct11 = cc.EvalNOT(ct1); @@ -150,7 +150,7 @@ void FHEW_KEYSWITCH(benchmark::State& state, ParamSet param_set) { LWEPrivateKey sk = cc.KeyGen(); LWEPrivateKey skN = cc.KeyGenN(); - auto ctQN1 = cc.Encrypt(skN, 1, FRESH); + auto ctQN1 = cc.Encrypt(skN, 1, SMALL_DIM); auto keySwitchHint = cc.KeySwitchGen(sk, skN); for (auto _ : state) { diff --git a/benchmark/src/binfhe-paramsets.cpp b/benchmark/src/binfhe-paramsets.cpp index 85d54a320..049420d23 100644 --- a/benchmark/src/binfhe-paramsets.cpp +++ b/benchmark/src/binfhe-paramsets.cpp @@ -96,53 +96,53 @@ using namespace lbcrypto; } // clang-format off -BENCHMARK_CAPTURE(FHEW_BINGATE2, TOY_2_GINX_OR, TOY, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, MEDIUM_2_GINX_OR, MEDIUM, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128_2_AP_OR, STD128_AP, AP, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128_2_GINX_OR, STD128, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128_3_GINX_OR, STD128_3, GINX, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128_4_GINX_OR, STD128_4, GINX, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128Q_2_GINX_OR, STD128Q, GINX, OR)->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(FHEW_BINGATE2, TOY_2_GINX_OR, TOY, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, MEDIUM_2_GINX_OR, MEDIUM, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128_2_AP_OR, STD128_AP, AP, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128_2_GINX_OR, STD128, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128_3_GINX_OR, STD128_3, GINX, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128_4_GINX_OR, STD128_4, GINX, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128Q_2_GINX_OR, STD128Q, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); #if NATIVEINT >= 64 -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128Q_3_GINX_OR, STD128Q_3, GINX, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128Q_4_GINX_OR, STD128Q_4, GINX, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192_2_GINX_OR, STD192, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192_3_GINX_OR, STD192_3, GINX, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192_4_GINX_OR, STD192_4, GINX, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192Q_2_GINX_OR, STD192Q, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192Q_3_GINX_OR, STD192Q_3, GINX, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192Q_4_GINX_OR, STD192Q_4, GINX, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256_2_GINX_OR, STD256, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256_3_GINX_OR, STD256_3, GINX, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256_4_GINX_OR, STD256_4, GINX, OR4)->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128Q_3_GINX_OR, STD128Q_3, GINX, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128Q_4_GINX_OR, STD128Q_4, GINX, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192_2_GINX_OR, STD192, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192_3_GINX_OR, STD192_3, GINX, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192_4_GINX_OR, STD192_4, GINX, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192Q_2_GINX_OR, STD192Q, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192Q_3_GINX_OR, STD192Q_3, GINX, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192Q_4_GINX_OR, STD192Q_4, GINX, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256_2_GINX_OR, STD256, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256_3_GINX_OR, STD256_3, GINX, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256_4_GINX_OR, STD256_4, GINX, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); #endif -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256Q_2_GINX_OR, STD256Q, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256Q_3_GINX_OR, STD256Q_3, GINX, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256Q_4_GINX_OR, STD256Q_4, GINX, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128_2_LMKCDEY_OR, STD128_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128_3_LMKCDEY_OR, STD128_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128_4_LMKCDEY_OR, STD128_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128Q_2_LMKCDEY_OR, STD128Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128Q_3_LMKCDEY_OR, STD128Q_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256Q_2_GINX_OR, STD256Q, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256Q_3_GINX_OR, STD256Q_3, GINX, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256Q_4_GINX_OR, STD256Q_4, GINX, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128_2_LMKCDEY_OR, STD128_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128_3_LMKCDEY_OR, STD128_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128_4_LMKCDEY_OR, STD128_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD128Q_2_LMKCDEY_OR, STD128Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD128Q_3_LMKCDEY_OR, STD128Q_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); #if NATIVEINT >= 64 -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128Q_4_LMKCDEY_OR, STD128Q_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192_2_LMKCDEY_OR, STD192_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192_3_LMKCDEY_OR, STD192_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192_4_LMKCDEY_OR, STD192_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192Q_2_LMKCDEY_OR, STD192Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192Q_3_LMKCDEY_OR, STD192Q_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192Q_4_LMKCDEY_OR, STD192Q_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256_2_LMKCDEY_OR, STD256_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256_3_LMKCDEY_OR, STD256_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256_4_LMKCDEY_OR, STD256_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256Q_2_LMKCDEY_OR, STD256Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256Q_3_LMKCDEY_OR, STD256Q_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256Q_4_LMKCDEY_OR, STD256Q_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD128Q_4_LMKCDEY_OR, STD128Q_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192_2_LMKCDEY_OR, STD192_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192_3_LMKCDEY_OR, STD192_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192_4_LMKCDEY_OR, STD192_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD192Q_2_LMKCDEY_OR, STD192Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD192Q_3_LMKCDEY_OR, STD192Q_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD192Q_4_LMKCDEY_OR, STD192Q_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256_2_LMKCDEY_OR, STD256_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256_3_LMKCDEY_OR, STD256_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256_4_LMKCDEY_OR, STD256_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, STD256Q_2_LMKCDEY_OR, STD256Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE3, STD256Q_3_LMKCDEY_OR, STD256Q_3_LMKCDEY, LMKCDEY, OR3)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE4, STD256Q_4_LMKCDEY_OR, STD256Q_4_LMKCDEY, LMKCDEY, OR4)->Unit(benchmark::kMillisecond)->MinTime(5.0); #endif -BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128_2_GINX_OR, LPF_STD128, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128Q_2_GINX_OR, LPF_STD128Q, GINX, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128_2_LMKCDEY_OR, LPF_STD128_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); -BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128Q_2_LMKCDEY_OR, LPF_STD128Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond); +BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128_2_GINX_OR, LPF_STD128, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128Q_2_GINX_OR, LPF_STD128Q, GINX, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128_2_LMKCDEY_OR, LPF_STD128_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); +BENCHMARK_CAPTURE(FHEW_BINGATE2, LPF_STD128Q_2_LMKCDEY_OR, LPF_STD128Q_LMKCDEY, LMKCDEY, OR)->Unit(benchmark::kMillisecond)->MinTime(5.0); // clang-format on BENCHMARK_MAIN(); diff --git a/src/binfhe/examples/boolean-ap.cpp b/src/binfhe/examples/boolean-ap.cpp index c9b964c59..8f60a5309 100644 --- a/src/binfhe/examples/boolean-ap.cpp +++ b/src/binfhe/examples/boolean-ap.cpp @@ -68,7 +68,7 @@ int main() { // Encrypt two ciphertexts representing Boolean True (1) // By default, freshly encrypted ciphertexts are bootstrapped. // If you wish to get a fresh encryption without bootstrapping, write - // auto ct1 = cc.Encrypt(sk, 1, FRESH); + // auto ct1 = cc.Encrypt(sk, 1, LARGE_DIM); auto ct1 = cc.Encrypt(sk, 1); auto ct2 = cc.Encrypt(sk, 1); diff --git a/src/binfhe/examples/boolean-lmkcdey.cpp b/src/binfhe/examples/boolean-lmkcdey.cpp index 6c5932856..6fac639b9 100644 --- a/src/binfhe/examples/boolean-lmkcdey.cpp +++ b/src/binfhe/examples/boolean-lmkcdey.cpp @@ -62,7 +62,7 @@ int main() { // Encrypt two ciphertexts representing Boolean True (1) // By default, freshly encrypted ciphertexts are bootstrapped. // If you wish to get a fresh encryption without bootstrapping, write - // auto ct1 = cc.Encrypt(sk, 1, FRESH); + // auto ct1 = cc.Encrypt(sk, 1, LARGE_DIM); auto ct1 = cc.Encrypt(sk, 1); auto ct2 = cc.Encrypt(sk, 1); diff --git a/src/binfhe/examples/boolean-serial-binary-dynamic-large-precision.cpp b/src/binfhe/examples/boolean-serial-binary-dynamic-large-precision.cpp index 2027ec3f4..ab0022a1e 100644 --- a/src/binfhe/examples/boolean-serial-binary-dynamic-large-precision.cpp +++ b/src/binfhe/examples/boolean-serial-binary-dynamic-large-precision.cpp @@ -198,7 +198,7 @@ int main() { for (int i = 0; i < 8; i++) { // We first encrypt with large Q - auto ct1 = cc.Encrypt(sk, p / 2 + i - 3, FRESH, p, Q); + auto ct1 = cc.Encrypt(sk, p / 2 + i - 3, LARGE_DIM, p, Q); // Get the MSB ct1 = cc.EvalSign(ct1); diff --git a/src/binfhe/examples/boolean-serial-json-dynamic-large-precision.cpp b/src/binfhe/examples/boolean-serial-json-dynamic-large-precision.cpp index dbf7fe6da..37b876958 100644 --- a/src/binfhe/examples/boolean-serial-json-dynamic-large-precision.cpp +++ b/src/binfhe/examples/boolean-serial-json-dynamic-large-precision.cpp @@ -198,7 +198,7 @@ int main() { for (int i = 0; i < 8; i++) { // We first encrypt with large Q - auto ct1 = cc.Encrypt(sk, p / 2 + i - 3, FRESH, p, Q); + auto ct1 = cc.Encrypt(sk, p / 2 + i - 3, LARGE_DIM, p, Q); // Get the MSB ct1 = cc.EvalSign(ct1); diff --git a/src/binfhe/examples/boolean.cpp b/src/binfhe/examples/boolean.cpp index 6708e18c0..079a4d03f 100644 --- a/src/binfhe/examples/boolean.cpp +++ b/src/binfhe/examples/boolean.cpp @@ -65,7 +65,7 @@ int main() { // Encrypt two ciphertexts representing Boolean True (1). // By default, freshly encrypted ciphertexts are bootstrapped. // If you wish to get a fresh encryption without bootstrapping, write - // auto ct1 = cc.Encrypt(sk, 1, FRESH); + // auto ct1 = cc.Encrypt(sk, 1, LARGE_DIM); auto ct1 = cc.Encrypt(sk, 1); auto ct2 = cc.Encrypt(sk, 1); diff --git a/src/binfhe/examples/eval-decomp.cpp b/src/binfhe/examples/eval-decomp.cpp index 984bc44b6..fb2b9a22e 100644 --- a/src/binfhe/examples/eval-decomp.cpp +++ b/src/binfhe/examples/eval-decomp.cpp @@ -70,7 +70,7 @@ int main() { std::cout << "Completed the key generation." << std::endl; // Sample Program: Step 3: Encryption - auto ct1 = cc.Encrypt(sk, P / 2 + 1, FRESH, P, Q); + auto ct1 = cc.Encrypt(sk, P / 2 + 1, LARGE_DIM, P, Q); std::cout << "Encrypted value: " << P / 2 + 1 << std::endl; // Sample Program: Step 4: Evaluation diff --git a/src/binfhe/examples/eval-flooring.cpp b/src/binfhe/examples/eval-flooring.cpp index 45d8fd429..f4a45c77e 100644 --- a/src/binfhe/examples/eval-flooring.cpp +++ b/src/binfhe/examples/eval-flooring.cpp @@ -66,7 +66,7 @@ int main() { uint32_t input = 6; std::cout << "Homomorphically round down the input by " << bits << " bits." << std::endl; - auto ct1 = cc.Encrypt(sk, input % p, FRESH, p); + auto ct1 = cc.Encrypt(sk, input % p, LARGE_DIM, p); // Sample Program: Step 4: Evaluation auto ctRounded = cc.EvalFloor(ct1, bits); diff --git a/src/binfhe/examples/eval-function.cpp b/src/binfhe/examples/eval-function.cpp index 03354f4a1..1e4f3265e 100644 --- a/src/binfhe/examples/eval-function.cpp +++ b/src/binfhe/examples/eval-function.cpp @@ -72,7 +72,7 @@ int main() { // Sample Program: Step 4: evalute f(x) homomorphically and decrypt // Note that we check for all the possible plaintexts. for (int i = 0; i < p; i++) { - auto ct1 = cc.Encrypt(sk, i % p, FRESH, p); + auto ct1 = cc.Encrypt(sk, i % p, LARGE_DIM, p); auto ct_cube = cc.EvalFunc(ct1, lut); diff --git a/src/binfhe/examples/eval-sign.cpp b/src/binfhe/examples/eval-sign.cpp index 3498e7e8e..0171ba5ce 100644 --- a/src/binfhe/examples/eval-sign.cpp +++ b/src/binfhe/examples/eval-sign.cpp @@ -73,7 +73,7 @@ int main() { // Note that we check for 8 different numbers for (int i = 0; i < 8; i++) { // We first encrypt with large Q - auto ct1 = cc.Encrypt(sk, p / 2 + i - 3, FRESH, p, Q); + auto ct1 = cc.Encrypt(sk, p / 2 + i - 3, LARGE_DIM, p, Q); // Get the MSB ct1 = cc.EvalSign(ct1); diff --git a/src/binfhe/examples/pke/boolean-pke.cpp b/src/binfhe/examples/pke/boolean-pke.cpp index 8c4196ccc..2f7da74f9 100644 --- a/src/binfhe/examples/pke/boolean-pke.cpp +++ b/src/binfhe/examples/pke/boolean-pke.cpp @@ -79,7 +79,7 @@ int main() { // Encrypt two ciphertexts representing Boolean True (1). // By default, freshly encrypted ciphertexts are bootstrapped. // If you wish to get a fresh encryption without bootstrapping, write - // auto ct1 = cc.Encrypt(sk, 1, FRESH); + // auto ct1 = cc.Encrypt(sk, 1, LARGE_DIM); auto ct1 = cc.Encrypt(cc.GetPublicKey(), 1); auto ct2 = cc.Encrypt(cc.GetPublicKey(), 1); diff --git a/src/binfhe/include/binfhe-base-scheme.h b/src/binfhe/include/binfhe-base-scheme.h index 1dddd71ca..df3ac368c 100644 --- a/src/binfhe/include/binfhe-base-scheme.h +++ b/src/binfhe/include/binfhe-base-scheme.h @@ -99,7 +99,7 @@ class BinFHEScheme { * @return a shared pointer to the resulting ciphertext */ LWECiphertext EvalBinGate(const std::shared_ptr& params, BINGATE gate, const RingGSWBTKey& EK, - ConstLWECiphertext& ct1, ConstLWECiphertext& ct2) const; + ConstLWECiphertext& ct1, ConstLWECiphertext& ct2, bool extended = false) const; /** * Evaluates a binary gate on a vector of ciphertexts (calls bootstrapping as a subroutine). @@ -112,7 +112,7 @@ class BinFHEScheme { * @return a shared pointer to the resulting ciphertext */ LWECiphertext EvalBinGate(const std::shared_ptr& params, BINGATE gate, const RingGSWBTKey& EK, - const std::vector& ctvector) const; + const std::vector& ctvector, bool extended = false) const; /** * Evaluates NOT gate @@ -132,7 +132,7 @@ class BinFHEScheme { * @return a shared pointer to the resulting ciphertext */ LWECiphertext Bootstrap(const std::shared_ptr& params, const RingGSWBTKey& EK, - ConstLWECiphertext& ct) const; + ConstLWECiphertext& ct, bool extended = false) const; /** * Evaluate an arbitrary function diff --git a/src/binfhe/include/binfhe-constants.h b/src/binfhe/include/binfhe-constants.h index fbd002839..7a5226e0d 100644 --- a/src/binfhe/include/binfhe-constants.h +++ b/src/binfhe/include/binfhe-constants.h @@ -102,8 +102,8 @@ std::ostream& operator<<(std::ostream& s, BINFHE_PARAMSET f); */ enum BINFHE_OUTPUT { INVALID_OUTPUT = 0, - FRESH, // a fresh encryption - BOOTSTRAPPED, // a freshly encrypted ciphertext is bootstrapped + FRESH, // a fresh encryption (deprecated) + BOOTSTRAPPED, // a freshly encrypted ciphertext is bootstrapped (deprecated) LARGE_DIM, // a fresh encryption with dimension N SMALL_DIM, // a freshly encrypted ciphertext of dimension N and modulus Q switched to n and q }; diff --git a/src/binfhe/include/binfhecontext.h b/src/binfhe/include/binfhecontext.h index 5e393c114..420353e11 100644 --- a/src/binfhe/include/binfhecontext.h +++ b/src/binfhe/include/binfhecontext.h @@ -206,13 +206,13 @@ class BinFHEContext : public Serializable { * * @param sk the secret key * @param m the plaintext - * @param output FRESH to generate fresh ciphertext, BOOTSTRAPPED to - * generate a refreshed ciphertext (default) + * @param output SMALL_DIM to generate fresh ciphertext (default), LARGE_DIM to + * generate a refreshed ciphertext * @param p plaintext modulus * @param mod the ciphertext modulus to encrypt with; by default m_q in params * @return a shared pointer to the ciphertext */ - LWECiphertext Encrypt(ConstLWEPrivateKey& sk, LWEPlaintext m, BINFHE_OUTPUT output = BOOTSTRAPPED, + LWECiphertext Encrypt(ConstLWEPrivateKey& sk, LWEPlaintext m, BINFHE_OUTPUT output = SMALL_DIM, LWEPlaintextModulus p = 4, const NativeInteger& mod = 0) const; /** @@ -302,7 +302,7 @@ class BinFHEContext : public Serializable { * @param ct2 second ciphertext * @return a shared pointer to the resulting ciphertext */ - LWECiphertext EvalBinGate(BINGATE gate, ConstLWECiphertext& ct1, ConstLWECiphertext& ct2) const; + LWECiphertext EvalBinGate(BINGATE gate, ConstLWECiphertext& ct1, ConstLWECiphertext& ct2, bool extended = false) const; /** * Evaluates a binary gate on vector of ciphertexts (calls bootstrapping as a subroutine) @@ -311,7 +311,7 @@ class BinFHEContext : public Serializable { * @param ctvector vector of ciphertexts * @return a shared pointer to the resulting ciphertext */ - LWECiphertext EvalBinGate(BINGATE gate, const std::vector& ctvector) const; + LWECiphertext EvalBinGate(BINGATE gate, const std::vector& ctvector, bool extended = false) const; /** * Bootstraps a ciphertext (without peforming any operation) @@ -319,7 +319,7 @@ class BinFHEContext : public Serializable { * @param ct ciphertext to be bootstrapped * @return a shared pointer to the resulting ciphertext */ - LWECiphertext Bootstrap(ConstLWECiphertext& ct) const; + LWECiphertext Bootstrap(ConstLWECiphertext& ct, bool extended = false) const; /** * Evaluate an arbitrary function diff --git a/src/binfhe/include/lwe-keypair.h b/src/binfhe/include/lwe-keypair.h index a34f8a87b..e928bdb60 100644 --- a/src/binfhe/include/lwe-keypair.h +++ b/src/binfhe/include/lwe-keypair.h @@ -54,7 +54,8 @@ class LWEKeyPairImpl { LWEPublicKey publicKey{nullptr}; LWEPrivateKey secretKey{nullptr}; - LWEKeyPairImpl(LWEPublicKey Av, LWEPrivateKey s) noexcept : publicKey(std::move(Av)), secretKey(std::move(s)) {} + LWEKeyPairImpl(const LWEPublicKey& Av, const LWEPrivateKey& s) : publicKey(Av), secretKey(s) {} + LWEKeyPairImpl(LWEPublicKey&& Av, LWEPrivateKey&& s) noexcept : publicKey(std::move(Av)), secretKey(std::move(s)) {} bool good() { return publicKey && secretKey; diff --git a/src/binfhe/include/lwe-keyswitchkey.h b/src/binfhe/include/lwe-keyswitchkey.h index 526260f10..b3692ff8a 100644 --- a/src/binfhe/include/lwe-keyswitchkey.h +++ b/src/binfhe/include/lwe-keyswitchkey.h @@ -50,10 +50,14 @@ class LWESwitchingKeyImpl : public Serializable { public: LWESwitchingKeyImpl() = default; - explicit LWESwitchingKeyImpl(const std::vector>>& keyA, - const std::vector>>& keyB) + LWESwitchingKeyImpl(const std::vector>>& keyA, + const std::vector>>& keyB) : m_keyA(keyA), m_keyB(keyB) {} + LWESwitchingKeyImpl(std::vector>>&& keyA, + std::vector>>&& keyB) + : m_keyA(std::move(keyA)), m_keyB(std::move(keyB)) {} + LWESwitchingKeyImpl(const LWESwitchingKeyImpl& rhs) : m_keyA(rhs.m_keyA), m_keyB(rhs.m_keyB) {} LWESwitchingKeyImpl(LWESwitchingKeyImpl&& rhs) noexcept diff --git a/src/binfhe/include/lwe-publickey.h b/src/binfhe/include/lwe-publickey.h index fd37c3e6c..ce82b425f 100644 --- a/src/binfhe/include/lwe-publickey.h +++ b/src/binfhe/include/lwe-publickey.h @@ -49,7 +49,9 @@ class LWEPublicKeyImpl : public Serializable { public: LWEPublicKeyImpl() = default; - explicit LWEPublicKeyImpl(const std::vector& A, const NativeVector& v) : m_A(A), m_v(v) {} + LWEPublicKeyImpl(const std::vector& A, const NativeVector& v) : m_A(A), m_v(v) {} + + LWEPublicKeyImpl(std::vector&& A, NativeVector&& v) noexcept : m_A(std::move(A)), m_v(std::move(v)) {} LWEPublicKeyImpl(LWEPublicKeyImpl&& rhs) noexcept : m_A(std::move(rhs.m_A)), m_v(std::move(rhs.m_v)) {} diff --git a/src/binfhe/include/rlwe-ciphertext.h b/src/binfhe/include/rlwe-ciphertext.h index 580dba401..c3946cdd5 100644 --- a/src/binfhe/include/rlwe-ciphertext.h +++ b/src/binfhe/include/rlwe-ciphertext.h @@ -65,6 +65,8 @@ class RLWECiphertextImpl : public Serializable { explicit RLWECiphertextImpl(const std::vector& elements) : m_elements(elements) {} + explicit RLWECiphertextImpl(std::vector&& elements) noexcept : m_elements(std::move(elements)) {} + RLWECiphertextImpl(const RLWECiphertextImpl& rhs) : m_elements(rhs.m_elements) {} RLWECiphertextImpl(RLWECiphertextImpl&& rhs) noexcept : m_elements(std::move(rhs.m_elements)) {} diff --git a/src/binfhe/lib/binfhe-base-scheme.cpp b/src/binfhe/lib/binfhe-base-scheme.cpp index b6118db87..13f5d809f 100644 --- a/src/binfhe/lib/binfhe-base-scheme.cpp +++ b/src/binfhe/lib/binfhe-base-scheme.cpp @@ -59,7 +59,7 @@ RingGSWBTKey BinFHEScheme::KeyGen(const std::shared_ptr& par const auto& RGSWParams = params->GetRingGSWParams(); const auto& polyParams = RGSWParams->GetPolyParams(); NativePoly skNPoly(polyParams); - skNPoly.SetValues(skN->GetElement(), Format::COEFFICIENT); + skNPoly.SetValues(std::move(skN->GetElement()), Format::COEFFICIENT); skNPoly.SetFormat(Format::EVALUATION); ek.BSkey = ACCscheme->KeyGenAcc(RGSWParams, skNPoly, LWEsk); @@ -70,137 +70,135 @@ RingGSWBTKey BinFHEScheme::KeyGen(const std::shared_ptr& par // Full evaluation as described in https://eprint.iacr.org/2020/086 LWECiphertext BinFHEScheme::EvalBinGate(const std::shared_ptr& params, BINGATE gate, const RingGSWBTKey& EK, ConstLWECiphertext& ct1, - ConstLWECiphertext& ct2) const { + ConstLWECiphertext& ct2, bool extended) const { if (ct1 == ct2) OPENFHE_THROW("Input ciphertexts should be independant"); - LWECiphertext ctprep = std::make_shared(*ct1); + const auto& LWEParams = params->GetLWEParams(); + NativeInteger Q{LWEParams->GetQ()}; + + // input cts expected with SMALL_DIM + auto cct1 = (Q == ct1->GetModulus()) ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ct1) : std::make_shared(*ct1); + const auto cct2 = (Q == ct2->GetModulus()) ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ct2) : ct2; + // the additive homomorphic operation for XOR/NXOR is different from the other gates we compute // 2*(ct1 + ct2) mod 4 for XOR, 0 -> 0, 2 -> 1 // XOR_FAST and XNOR_FAST are included for backwards compatibility; they map to XOR and XNOR if ((gate == XOR) || (gate == XNOR) || (gate == XOR_FAST) || (gate == XNOR_FAST)) { - LWEscheme->EvalAddEq(ctprep, ct2); - LWEscheme->EvalAddEq(ctprep, ctprep); + LWEscheme->EvalAddEq(cct1, cct2); + LWEscheme->EvalAddEq(cct1, cct1); } else { // for all other gates, we simply compute (ct1 + ct2) mod 4 // for AND: 0,1 -> 0 and 2,3 -> 1 // for OR: 1,2 -> 1 and 3,0 -> 0 - LWEscheme->EvalAddEq(ctprep, ct2); + LWEscheme->EvalAddEq(cct1, cct2); } - auto acc{BootstrapGateCore(params, gate, EK.BSkey, ctprep)}; - // the accumulator result is encrypted w.r.t. the transposed secret key // we can transpose "a" to get an encryption under the original secret key - std::vector& accVec{acc->GetElements()}; + auto accVec{BootstrapGateCore(params, gate, EK.BSkey, cct1)->GetElements()}; accVec[0] = accVec[0].Transpose(); accVec[0].SetFormat(Format::COEFFICIENT); accVec[1].SetFormat(Format::COEFFICIENT); - // we add Q/8 to "b" to to map back to Q/4 (i.e., mod 2) arithmetic. - const auto& LWEParams = params->GetLWEParams(); - NativeInteger Q{LWEParams->GetQ()}; + // hardcoded for p = 4 + // we add Q/8 to "b" to map back to Q/4 (i.e., mod 2) arithmetic. NativeInteger b{(Q >> 3) + 1}; b.ModAddFastEq(accVec[1][0], Q); - auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), std::move(b)); - // Modulus switching to a middle step Q' - auto ctMS = LWEscheme->ModSwitch(LWEParams->GetqKS(), ctExt); - // Key switching - auto ctKS = LWEscheme->KeySwitch(LWEParams, EK.KSkey, ctMS); - // Modulus switching - return LWEscheme->ModSwitch(ct1->GetModulus(), ctKS); + auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), b); + + if (extended) + return ctExt; + return LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctExt); } // Full evaluation as described in https://eprint.iacr.org/2020/086 LWECiphertext BinFHEScheme::EvalBinGate(const std::shared_ptr& params, BINGATE gate, - const RingGSWBTKey& EK, const std::vector& ctvector) const { + const RingGSWBTKey& EK, const std::vector& ctvector, bool extended) const { + // check if the ciphertexts are all independent - for (size_t i = 0; i < ctvector.size(); i++) { - for (size_t j = i + 1; j < ctvector.size(); j++) { - if (ctvector[j] == ctvector[i]) { + uint32_t length = ctvector.size(); + for (uint32_t i = 0; i < length; ++i) { + for (uint32_t j = i + 1; j < length; ++j) { + if (ctvector[i] == ctvector[j]) { OPENFHE_THROW("Input ciphertexts should be independent"); } } } - NativeInteger p = ctvector[0]->GetptModulus(); + if ((gate == AND3) || (gate == OR3) || (gate == AND4) || (gate == OR4) || (gate == MAJORITY)) { + const auto& LWEParams = params->GetLWEParams(); + NativeInteger Q{LWEParams->GetQ()}; - LWECiphertext ctprep = std::make_shared(*ctvector[0]); - ctprep->SetptModulus(p); - if ((gate == MAJORITY) || (gate == AND3) || (gate == OR3) || (gate == AND4) || (gate == OR4)) { - // we simply compute sum(ctvector[i]) mod p - for (size_t i = 1; i < ctvector.size(); i++) { - LWEscheme->EvalAddEq(ctprep, ctvector[i]); + // input cts expected with SMALL_DIM + auto ct = (Q == ctvector[0]->GetModulus()) ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctvector[0]) : std::make_shared(*ctvector[0]); + for (uint32_t i = 1; i < length; ++i) { + LWEscheme->EvalAddEq(ct, (Q == ctvector[i]->GetModulus()) ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctvector[i]) : ctvector[i]); } - auto acc = BootstrapGateCore(params, gate, EK.BSkey, ctprep); - std::vector& accVec = acc->GetElements(); + auto p = ctvector[0]->GetptModulus(); + ct->SetptModulus(p); + // the accumulator result is encrypted w.r.t. the transposed secret key // we can transpose "a" to get an encryption under the original secret key + auto accVec{BootstrapGateCore(params, gate, EK.BSkey, ct)->GetElements()}; accVec[0] = accVec[0].Transpose(); accVec[0].SetFormat(Format::COEFFICIENT); accVec[1].SetFormat(Format::COEFFICIENT); - // we add Q/8 to "b" to to map back to Q/4 (i.e., mod 2) arithmetic. - auto& LWEParams = params->GetLWEParams(); - NativeInteger Q = LWEParams->GetQ(); - NativeInteger b = Q / NativeInteger(2 * p) + 1; + NativeInteger b = Q / (p * 2) + 1; b.ModAddFastEq(accVec[1][0], Q); - auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), std::move(b)); - // Modulus switching to a middle step Q' - auto ctMS = LWEscheme->ModSwitch(LWEParams->GetqKS(), ctExt); - // Key switching - auto ctKS = LWEscheme->KeySwitch(LWEParams, EK.KSkey, ctMS); - // Modulus switching - return LWEscheme->ModSwitch(ctvector[0]->GetModulus(), ctKS); + auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), b); + + if (!extended) + ctExt = LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctExt); + + ctExt->SetptModulus(p); + return ctExt; } else if (gate == CMUX) { - if (ctvector.size() != 3) + if (length != 3) OPENFHE_THROW("CMUX gate implemented for ciphertext vectors of size 3"); - auto ccNOT = EvalNOT(params, ctvector[2]); - auto ctNAND1 = EvalBinGate(params, NAND, EK, ctvector[0], ccNOT); - auto ctNAND2 = EvalBinGate(params, NAND, EK, ctvector[1], ctvector[2]); - auto ctCMUX = EvalBinGate(params, NAND, EK, ctNAND1, ctNAND2); - return ctCMUX; + auto&& ctNAND1 = EvalBinGate(params, NAND, EK, ctvector[0], EvalNOT(params, ctvector[2])); + auto&& ctNAND2 = EvalBinGate(params, NAND, EK, ctvector[1], ctvector[2]); + return EvalBinGate(params, NAND, EK, ctNAND1, ctNAND2); } else { OPENFHE_THROW("This gate is not implemented for vector of ciphertexts at this time"); } } + // Full evaluation as described in https://eprint.iacr.org/2020/086 LWECiphertext BinFHEScheme::Bootstrap(const std::shared_ptr& params, const RingGSWBTKey& EK, - ConstLWECiphertext& ct) const { - NativeInteger p = ct->GetptModulus(); - LWECiphertext ctprep{std::make_shared(*ct)}; - // ctprep = ct + q/4 - LWEscheme->EvalAddConstEq(ctprep, (ct->GetModulus() >> 2)); + ConstLWECiphertext& ct, bool extended) const { - auto acc{BootstrapGateCore(params, AND, EK.BSkey, ctprep)}; + const auto& LWEParams = params->GetLWEParams(); + NativeInteger Q{LWEParams->GetQ()}; + // input ct expected with SMALL_DIM + auto cct = (Q == ct->GetModulus()) ? LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ct) : std::make_shared(*ct); + LWEscheme->EvalAddConstEq(cct, (ct->GetModulus() >> 2)); // the accumulator result is encrypted w.r.t. the transposed secret key // we can transpose "a" to get an encryption under the original secret key - std::vector& accVec{acc->GetElements()}; + auto accVec{BootstrapGateCore(params, AND, EK.BSkey, cct)->GetElements()}; accVec[0] = accVec[0].Transpose(); accVec[0].SetFormat(Format::COEFFICIENT); accVec[1].SetFormat(Format::COEFFICIENT); - // we add Q/8 to "b" to to map back to Q/4 (i.e., mod 2) arithmetic. - const auto& LWEParams = params->GetLWEParams(); - NativeInteger Q{LWEParams->GetQ()}; - NativeInteger b = Q / NativeInteger(2 * p) + 1; + NativeInteger b{Q / (ct->GetptModulus() * 2) + 1}; b.ModAddFastEq(accVec[1][0], Q); - auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), std::move(b)); - // Modulus switching to a middle step Q' - auto ctMS = LWEscheme->ModSwitch(LWEParams->GetqKS(), ctExt); - // Key switching - auto ctKS = LWEscheme->KeySwitch(LWEParams, EK.KSkey, ctMS); - // Modulus switching - return LWEscheme->ModSwitch(ct->GetModulus(), ctKS); + auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), b); + + if (!extended) + ctExt = LWEscheme->SwitchCTtoqn(LWEParams, EK.KSkey, ctExt); + + ctExt->SetptModulus(ct->GetptModulus()); + return ctExt; } // Evaluation of the NOT operation; no key material is needed @@ -252,9 +250,9 @@ LWECiphertext BinFHEScheme::EvalFunc(const std::shared_ptr& NativeInteger dq{q << 1}; // raise the modulus of ct1 : q -> 2q ct1->GetA().SetModulus(dq); - auto ct2 = std::make_shared(*ct1); LWEscheme->EvalAddConstEq(ct2, beta); + // this is 1/4q_small or -1/4q_small mod q auto f0 = [](NativeInteger x, NativeInteger q, NativeInteger Q) -> NativeInteger { if (x < (q >> 1)) @@ -418,8 +416,6 @@ LWECiphertext BinFHEScheme::EvalSign(const std::shared_ptr& return cttmp; } -////// - // Evaluate Ciphertext Decomposition std::vector BinFHEScheme::EvalDecomp(const std::shared_ptr& params, const std::map& EKs, ConstLWECiphertext& ct, @@ -492,38 +488,43 @@ RLWECiphertext BinFHEScheme::BootstrapGateCore(const std::shared_ptrGetLWEParams(); - auto& RGSWParams = params->GetRingGSWParams(); - auto polyParams = RGSWParams->GetPolyParams(); - - // Specifies the range [q1,q2) that will be used for mapping - NativeInteger p = ct->GetptModulus(); + // Specifies the range [lb, ub) that will be used for mapping NativeInteger q = ct->GetModulus(); - uint32_t qHalf = q.ConvertToInt() >> 1; + auto qHalf = q.ConvertToInt() >> 1; + auto& RGSWParams = params->GetRingGSWParams(); NativeInteger q1 = RGSWParams->GetGateConst()[static_cast(gate)]; NativeInteger q2 = q1.ModAddFast(NativeInteger(qHalf), q); + bool swap = q1 >= q2; + auto lb = swap? q2 : q1; + auto ub = swap? q1 : q2; + // depending on whether the value is the range, it will be set // to either Q/8 or -Q/8 to match binary arithmetic + auto& LWEParams = params->GetLWEParams(); NativeInteger Q = LWEParams->GetQ(); - NativeInteger Q2p = Q / NativeInteger(2 * p) + 1; + NativeInteger Q2p = Q / (ct->GetptModulus() * 2) + 1; NativeInteger Q2pNeg = Q - Q2p; - uint32_t N = LWEParams->GetN(); + auto lv = swap? Q2p : Q2pNeg; + auto uv = swap? Q2pNeg : Q2p; + + const uint32_t N = LWEParams->GetN(); NativeVector m(N, Q); // Since q | (2*N), we deal with a sparse embedding of Z_Q[x]/(X^{q/2}+1) to // Z_Q[x]/(X^N+1) - uint32_t factor = (2 * N / q.ConvertToInt()); - const NativeInteger& b = ct->GetB(); - for (size_t j = 0; j < qHalf; ++j) { - NativeInteger temp = b.ModSub(j, q); - if (q1 < q2) - m[j * factor] = ((temp >= q1) && (temp < q2)) ? Q2pNeg : Q2p; - else - m[j * factor] = ((temp >= q2) && (temp < q1)) ? Q2p : Q2pNeg; + const uint32_t factor = (N / qHalf); + + NativeInteger b = ct->GetB(); + + for (uint32_t i = 0; i < N; i += factor) { + m[i] = ((b >= lb) && (b < ub)) ? lv : uv; + b.ModSubFastEq(1, q); } + std::vector res(2); + auto& polyParams = RGSWParams->GetPolyParams(); // no need to do NTT as all coefficients of this poly are zero res[0] = NativePoly(polyParams, Format::EVALUATION, true); res[1] = NativePoly(polyParams, Format::COEFFICIENT, false); @@ -585,16 +586,15 @@ RLWECiphertext BinFHEScheme::BootstrapFuncCore(const std::shared_ptr LWECiphertext BinFHEScheme::BootstrapFunc(const std::shared_ptr& params, const RingGSWBTKey& EK, ConstLWECiphertext& ct, const Func f, const NativeInteger& fmod) const { - auto acc = BootstrapFuncCore(params, EK.BSkey, ct, f, fmod); - - std::vector& accVec = acc->GetElements(); // the accumulator result is encrypted w.r.t. the transposed secret key // we can transpose "a" to get an encryption under the original secret key + auto accVec{BootstrapFuncCore(params, EK.BSkey, ct, f, fmod)->GetElements()}; accVec[0] = accVec[0].Transpose(); accVec[0].SetFormat(Format::COEFFICIENT); accVec[1].SetFormat(Format::COEFFICIENT); - auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), std::move(accVec[1][0])); + auto ctExt = std::make_shared(std::move(accVec[0].GetValues()), accVec[1][0]); + auto& LWEParams = params->GetLWEParams(); // Modulus switching to a middle step Q' auto ctMS = LWEscheme->ModSwitch(LWEParams->GetqKS(), ctExt); diff --git a/src/binfhe/lib/binfhe-constants-impl.cpp b/src/binfhe/lib/binfhe-constants-impl.cpp index 34de51c62..ee9a56242 100644 --- a/src/binfhe/lib/binfhe-constants-impl.cpp +++ b/src/binfhe/lib/binfhe-constants-impl.cpp @@ -184,6 +184,12 @@ std::ostream& operator<<(std::ostream& s, BINFHE_OUTPUT f) { case BOOTSTRAPPED: s << "BOOTSTRAPPED"; break; + case LARGE_DIM: + s << "LARGE_DIM"; + break; + case SMALL_DIM: + s << "SMALL_DIM"; + break; default: s << "UNKNOWN"; break; diff --git a/src/binfhe/lib/binfhecontext.cpp b/src/binfhe/lib/binfhecontext.cpp index 40624df37..778b42ef3 100644 --- a/src/binfhe/lib/binfhecontext.cpp +++ b/src/binfhe/lib/binfhecontext.cpp @@ -232,8 +232,10 @@ LWECiphertext BinFHEContext::Encrypt(ConstLWEPublicKey& pk, LWEPlaintext m, BINF // Switch from ct of modulus Q and dimension N to smaller q and n // This is done by default while calling Encrypt but the output could // be set to LARGE_DIM to skip this switching - if (output == SMALL_DIM) - return SwitchCTtoqn(m_BTKey.KSkey, ct); + if (output == SMALL_DIM) { + ct = SwitchCTtoqn(m_BTKey.KSkey, ct); + ct->SetptModulus(p); + } return ct; } @@ -275,16 +277,16 @@ void BinFHEContext::BTKeyGen(ConstLWEPrivateKey& sk, KEYGEN_MODE keygenMode) { } } -LWECiphertext BinFHEContext::EvalBinGate(const BINGATE gate, ConstLWECiphertext& ct1, ConstLWECiphertext& ct2) const { - return m_binfhescheme->EvalBinGate(m_params, gate, m_BTKey, ct1, ct2); +LWECiphertext BinFHEContext::EvalBinGate(const BINGATE gate, ConstLWECiphertext& ct1, ConstLWECiphertext& ct2, bool extended) const { + return m_binfhescheme->EvalBinGate(m_params, gate, m_BTKey, ct1, ct2, extended); } -LWECiphertext BinFHEContext::EvalBinGate(const BINGATE gate, const std::vector& ctvector) const { - return m_binfhescheme->EvalBinGate(m_params, gate, m_BTKey, ctvector); +LWECiphertext BinFHEContext::EvalBinGate(const BINGATE gate, const std::vector& ctvector, bool extended) const { + return m_binfhescheme->EvalBinGate(m_params, gate, m_BTKey, ctvector, extended); } -LWECiphertext BinFHEContext::Bootstrap(ConstLWECiphertext& ct) const { - return m_binfhescheme->Bootstrap(m_params, m_BTKey, ct); +LWECiphertext BinFHEContext::Bootstrap(ConstLWECiphertext& ct, bool extended) const { + return m_binfhescheme->Bootstrap(m_params, m_BTKey, ct, extended); } LWECiphertext BinFHEContext::EvalNOT(ConstLWECiphertext& ct) const { diff --git a/src/binfhe/lib/lwe-pke.cpp b/src/binfhe/lib/lwe-pke.cpp index 5730726ef..5af69ecef 100644 --- a/src/binfhe/lib/lwe-pke.cpp +++ b/src/binfhe/lib/lwe-pke.cpp @@ -107,8 +107,8 @@ LWECiphertext LWEEncryptionScheme::Encrypt(const std::shared_ptrGetElement(); - uint32_t n = s.GetLength(); + NativeVector s = sk->GetElement(); + const uint32_t n = s.GetLength(); s.SwitchModulus(mod); NativeInteger b = (m % p) * (mod / p) + params->GetDgg().GenerateInteger(mod); @@ -118,7 +118,7 @@ LWECiphertext LWEEncryptionScheme::Encrypt(const std::shared_ptr(std::move(a), std::move(b)); + auto ct = std::make_shared(std::move(a), b); ct->SetptModulus(p); return ct; } @@ -255,7 +255,7 @@ LWECiphertext LWEEncryptionScheme::ModSwitch(NativeInteger q, ConstLWECiphertext auto n = ctQ->GetLength(); auto Q = ctQ->GetModulus(); NativeVector a(n, q); - for (size_t i = 0; i < n; ++i) + for (uint32_t i = 0; i < n; ++i) a[i] = RoundqQ(ctQ->GetA()[i], q, Q); return std::make_shared(std::move(a), RoundqQ(ctQ->GetB(), q, Q)); } @@ -368,7 +368,7 @@ LWECiphertext LWEEncryptionScheme::KeySwitch(const std::shared_ptr(std::move(a), std::move(b)); + return std::make_shared(std::move(a), b); } // noiseless LWE embedding @@ -377,9 +377,7 @@ LWECiphertext LWEEncryptionScheme::KeySwitch(const std::shared_ptr& params, LWEPlaintext m) const { NativeInteger q(params->Getq()); - NativeInteger b(m * (q >> 2)); - NativeVector a(params->Getn(), q); - return std::make_shared(std::move(a), std::move(b)); + return std::make_shared(NativeVector(params->Getn(), q), (q >> 2)*m); } }; // namespace lbcrypto diff --git a/src/binfhe/unittest/UnitTestFHEW.cpp b/src/binfhe/unittest/UnitTestFHEW.cpp index 2b092b7d8..e92477b45 100644 --- a/src/binfhe/unittest/UnitTestFHEW.cpp +++ b/src/binfhe/unittest/UnitTestFHEW.cpp @@ -243,8 +243,8 @@ class UTGENERAL_FHEW : public ::testing::TestWithParam auto sk = cc.KeyGen(); auto skN = cc.KeyGenN(); - auto ctQN1 = cc.Encrypt(skN, 1, FRESH, 4, Q); - auto ctQN0 = cc.Encrypt(skN, 0, FRESH, 4, Q); + auto ctQN1 = cc.Encrypt(skN, 1, SMALL_DIM, 4, Q); + auto ctQN0 = cc.Encrypt(skN, 0, SMALL_DIM, 4, Q); NativeVector newSK = sk->GetElement(); newSK.SwitchModulus(Q); @@ -297,8 +297,8 @@ class UTGENERAL_FHEW : public ::testing::TestWithParam newSK.SwitchModulus(Q); auto skQ = std::make_shared(newSK); - auto ctQ1 = cc.Encrypt(skQ, 1, FRESH, 4, Q); - auto ctQ0 = cc.Encrypt(skQ, 0, FRESH, 4, Q); + auto ctQ1 = cc.Encrypt(skQ, 1, SMALL_DIM, 4, Q); + auto ctQ0 = cc.Encrypt(skQ, 0, SMALL_DIM, 4, Q); // switches the modulus from Q to q auto ct1 = cc.GetLWEScheme()->ModSwitch(cc.GetParams()->GetLWEParams()->Getq(), ctQ1); @@ -339,8 +339,8 @@ class UTGENERAL_FHEW : public ::testing::TestWithParam auto sk = cc.KeyGen(); - auto ct1 = cc.Encrypt(sk, 1, FRESH); - auto ct0 = cc.Encrypt(sk, 0, FRESH); + auto ct1 = cc.Encrypt(sk, 1); + auto ct0 = cc.Encrypt(sk, 0); auto ct1Not = cc.EvalNOT(ct1); auto ct0Not = cc.EvalNOT(ct0); diff --git a/src/binfhe/unittest/UnitTestFHEWExtended.cpp b/src/binfhe/unittest/UnitTestFHEWExtended.cpp new file mode 100644 index 000000000..65dfe79dd --- /dev/null +++ b/src/binfhe/unittest/UnitTestFHEWExtended.cpp @@ -0,0 +1,153 @@ +//================================================================================== +// BSD 2-Clause License +// +// Copyright (c) 2014-2024, NJIT, Duality Technologies Inc. and other contributors +// +// All rights reserved. +// +// Author TPOC: contact@openfhe.org +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//================================================================================== + +#include "binfhecontext.h" +#include "gtest/gtest.h" + +using namespace lbcrypto; + +TEST(UNITTestFHEWExtended, EvalBinGate2) { + auto cc = BinFHEContext(); + cc.GenerateBinFHEContext(TOY, GINX); + + auto sk = cc.KeyGen(); + cc.BTKeyGen(sk, PUB_ENCRYPT); + + auto pk = cc.GetPublicKey(); + auto Q = cc.GetParams()->GetLWEParams()->GetQ(); + + auto ct_small = cc.Encrypt(pk, 1, SMALL_DIM, 4); + EXPECT_NE(Q, ct_small->GetModulus()); + + auto ct_large = cc.Encrypt(pk, 1, LARGE_DIM, 4); + EXPECT_EQ(Q, ct_large->GetModulus()); + + auto ct11 = cc.EvalBinGate(OR, ct_small, ct_large, true); + EXPECT_EQ(Q, ct11->GetModulus()); + + auto ct12 = cc.EvalBinGate(AND, ct_large, ct_small, true); + EXPECT_EQ(Q, ct12->GetModulus()); + + auto ct2 = cc.EvalBinGate(NAND, ct11, ct12, false); + EXPECT_NE(Q, ct2->GetModulus()); + EXPECT_EQ(4, ct2->GetptModulus()); + + LWEPlaintext result; + cc.Decrypt(sk, ct2, &result); + EXPECT_EQ(0, result); +} + +TEST(UNITTestFHEWExtended, EvalBinGate3) { + auto cc = BinFHEContext(); + cc.GenerateBinFHEContext(TOY, GINX); + + auto sk = cc.KeyGen(); + cc.BTKeyGen(sk, PUB_ENCRYPT); + + auto pk = cc.GetPublicKey(); + auto Q = cc.GetParams()->GetLWEParams()->GetQ(); + + auto ct_small = cc.Encrypt(pk, 1, SMALL_DIM, 6); + EXPECT_NE(Q, ct_small->GetModulus()); + + auto ct_large = cc.Encrypt(pk, 1, LARGE_DIM, 6); + EXPECT_EQ(Q, ct_large->GetModulus()); + + std::vector v{ct_small, ct_large, cc.Encrypt(pk, 0, SMALL_DIM, 6)}; + + auto ct11 = cc.EvalBinGate(OR3, v, true); + EXPECT_EQ(Q, ct11->GetModulus()); + EXPECT_EQ(6, ct11->GetptModulus()); + + auto ct12 = cc.EvalBinGate(AND3, v, true); + EXPECT_EQ(Q, ct12->GetModulus()); + EXPECT_EQ(6, ct12->GetptModulus()); + + auto ct2 = cc.EvalBinGate(NAND, ct11, ct12, false); + EXPECT_NE(Q, ct2->GetModulus()); + EXPECT_EQ(4, ct2->GetptModulus()); + + LWEPlaintext result; + cc.Decrypt(sk, ct2, &result); + EXPECT_EQ(1, result); +} + +TEST(UNITTestFHEWExtended, EvalBinGate4) { + auto cc = BinFHEContext(); + cc.GenerateBinFHEContext(TOY, GINX); + + auto sk = cc.KeyGen(); + cc.BTKeyGen(sk, PUB_ENCRYPT); + + auto pk = cc.GetPublicKey(); + auto Q = cc.GetParams()->GetLWEParams()->GetQ(); + + auto ct_small = cc.Encrypt(pk, 1, SMALL_DIM, 8); + EXPECT_NE(Q, ct_small->GetModulus()); + + auto ct_large = cc.Encrypt(pk, 1, LARGE_DIM, 8); + EXPECT_EQ(Q, ct_large->GetModulus()); + + std::vector v{ct_small, ct_large, cc.Encrypt(pk, 0, SMALL_DIM, 8), cc.Encrypt(pk, 1, LARGE_DIM, 8)}; + + auto ct11 = cc.EvalBinGate(OR4, v, true); + EXPECT_EQ(Q, ct11->GetModulus()); + EXPECT_EQ(8, ct11->GetptModulus()); + + auto ct12 = cc.EvalBinGate(AND4, v, true); + EXPECT_EQ(Q, ct12->GetModulus()); + EXPECT_EQ(8, ct12->GetptModulus()); + + auto ct2 = cc.EvalBinGate(NAND, ct11, ct12, false); + EXPECT_NE(Q, ct2->GetModulus()); + EXPECT_EQ(4, ct2->GetptModulus()); + + LWEPlaintext result; + cc.Decrypt(sk, ct2, &result); + EXPECT_EQ(1, result); +} + +TEST(UNITTestFHEWExtended, BootStrap) { + auto cc = BinFHEContext(); + cc.GenerateBinFHEContext(TOY, GINX); + + auto sk = cc.KeyGen(); + cc.BTKeyGen(sk, PUB_ENCRYPT); + + auto pk = cc.GetPublicKey(); + auto Q = cc.GetParams()->GetLWEParams()->GetQ(); + + auto ct1 = cc.Bootstrap(cc.Encrypt(pk, 1, SMALL_DIM, 4), true); + EXPECT_EQ(Q, ct1->GetModulus()); + + auto ct0 = cc.Bootstrap(cc.Encrypt(pk, 0, LARGE_DIM, 4), true); + EXPECT_EQ(Q, ct0->GetModulus()); +} diff --git a/src/binfhe/unittest/UnitTestFHEWPKESerial.cpp b/src/binfhe/unittest/UnitTestFHEWPKESerial.cpp index 4414cc6bc..9f64df3c2 100644 --- a/src/binfhe/unittest/UnitTestFHEWPKESerial.cpp +++ b/src/binfhe/unittest/UnitTestFHEWPKESerial.cpp @@ -127,7 +127,7 @@ void UnitTestFHEWPKESerial(const ST& sertype, BINFHE_PARAMSET secLevel, BINFHE_M // They are left in this file for debugging purposes only. // TEST(UnitTestFHEWSerialAP, JSON) { // std::string msg = "UnitTestFHEWSerialAP.JSON serialization test failed: "; -// UnitTestFHEWSerial(SerType::JSON, TOY, AP, FRESH, msg); +// UnitTestFHEWSerial(SerType::JSON, TOY, AP, SMALL_DIM, msg); // } TEST(UnitTestFHEWPKESerialAP, BINARY) { @@ -137,7 +137,7 @@ TEST(UnitTestFHEWPKESerialAP, BINARY) { // TEST(UnitTestFHEWSerialGINX, JSON) { // std::string msg = "UnitTestFHEWSerialGINX.JSON serialization test failed: "; -// UnitTestFHEWSerial(SerType::JSON, TOY, GINX, FRESH, msg); +// UnitTestFHEWSerial(SerType::JSON, TOY, GINX, SMALL_DIM, msg); // } TEST(UnitTestFHEWPKESerialGINX, BINARY) { diff --git a/src/binfhe/unittest/UnitTestFHEWSerial.cpp b/src/binfhe/unittest/UnitTestFHEWSerial.cpp index e167d3b38..dd664027e 100755 --- a/src/binfhe/unittest/UnitTestFHEWSerial.cpp +++ b/src/binfhe/unittest/UnitTestFHEWSerial.cpp @@ -119,25 +119,25 @@ void UnitTestFHEWSerial(const ST& sertype, BINFHE_PARAMSET secLevel, BINFHE_METH // They are left in this file for debugging purposes only. // TEST(UnitTestFHEWSerialAP, JSON) { // std::string msg = "UnitTestFHEWSerialAP.JSON serialization test failed: "; -// UnitTestFHEWSerial(SerType::JSON, TOY, AP, FRESH, msg); +// UnitTestFHEWSerial(SerType::JSON, TOY, AP, SMALL_DIM, msg); // } TEST(UnitTestFHEWSerialAP, BINARY) { std::string msg = "UnitTestFHEWSerialAP.BINARY serialization test failed: "; - UnitTestFHEWSerial(SerType::BINARY, TOY, AP, FRESH, msg); + UnitTestFHEWSerial(SerType::BINARY, TOY, AP, SMALL_DIM, msg); } // TEST(UnitTestFHEWSerialGINX, JSON) { // std::string msg = "UnitTestFHEWSerialGINX.JSON serialization test failed: "; -// UnitTestFHEWSerial(SerType::JSON, TOY, GINX, FRESH, msg); +// UnitTestFHEWSerial(SerType::JSON, TOY, GINX, SMALL_DIM, msg); // } TEST(UnitTestFHEWSerialGINX, BINARY) { std::string msg = "UnitTestFHEWSerialGINX.BINARY serialization test failed: "; - UnitTestFHEWSerial(SerType::BINARY, TOY, GINX, FRESH, msg); + UnitTestFHEWSerial(SerType::BINARY, TOY, GINX, SMALL_DIM, msg); } TEST(UnitTestFHEWSerialLMKCDEY, BINARY) { std::string msg = "UnitTestFHEWSerialGINX.BINARY serialization test failed: "; - UnitTestFHEWSerial(SerType::BINARY, TOY, LMKCDEY, FRESH, msg); -} \ No newline at end of file + UnitTestFHEWSerial(SerType::BINARY, TOY, LMKCDEY, SMALL_DIM, msg); +} diff --git a/src/binfhe/unittest/UnitTestFunc.cpp b/src/binfhe/unittest/UnitTestFunc.cpp index df50b9677..c48f515e9 100644 --- a/src/binfhe/unittest/UnitTestFunc.cpp +++ b/src/binfhe/unittest/UnitTestFunc.cpp @@ -57,7 +57,7 @@ TEST(UnitTestFHEWGINX, EvalArbFunc) { auto lut = cc.GenerateLUTviaFunction(fp, p); for (int i = 0; i < p; i++) { - auto ct1 = cc.Encrypt(sk, i % p, FRESH, p); + auto ct1 = cc.Encrypt(sk, i % p, LARGE_DIM, p); auto ct_cube = cc.EvalFunc(ct1, lut); @@ -82,7 +82,7 @@ TEST(UnitTestFHEWGINX, EvalFloorFunc) { int p = cc.GetMaxPlaintextSpace().ConvertToInt(); // Obtain the maximum plaintext space for (int i = p / 2 - 3; i < p / 2 + 5; i++) { - auto ct1 = cc.Encrypt(sk, i % p, FRESH, p); + auto ct1 = cc.Encrypt(sk, i % p, LARGE_DIM, p); // round by one bit. auto ctRounded = cc.EvalFloor(ct1, 1); @@ -111,7 +111,7 @@ TEST(UnitTestFHEWGINX, EvalSignFuncTime) { std::string failed = "Large Precision Sign Evalution failed"; for (int i = 0; i < 8; i++) { - auto ct1 = cc.Encrypt(sk, p * factor / 2 + i - 3, FRESH, p * factor, Q); + auto ct1 = cc.Encrypt(sk, p * factor / 2 + i - 3, LARGE_DIM, p * factor, Q); ct1 = cc.EvalSign(ct1); LWEPlaintext result; cc.Decrypt(sk, ct1, &result, 2); @@ -135,7 +135,7 @@ TEST(UnitTestFHEWGINX, EvalSignFuncSpace) { std::string failed = "Large Precision Sign Evalution failed"; for (int i = 0; i < 8; i++) { - auto ct1 = cc.Encrypt(sk, p * factor / 2 + i - 3, FRESH, p * factor, Q); + auto ct1 = cc.Encrypt(sk, p * factor / 2 + i - 3, LARGE_DIM, p * factor, Q); ct1 = cc.EvalSign(ct1); LWEPlaintext result; cc.Decrypt(sk, ct1, &result, 2); @@ -161,7 +161,7 @@ TEST(UnitTestFHEWGINX, EvalDigitDecompTime) { // digit decomposes values starting with st upto st + 7 and checks every digit of each decomposition for (uint64_t i = st; i < st + 8; i++) { - auto ct1 = cc.Encrypt(sk, i, FRESH, p_basic * factor, Q); + auto ct1 = cc.Encrypt(sk, i, LARGE_DIM, p_basic * factor, Q); auto decomp = cc.EvalDecomp(ct1); EXPECT_EQ(usint(ceil(log(factor) / log(p_basic)) + 1), decomp.size()) << failed; @@ -221,7 +221,7 @@ TEST(UnitTestFHEWGINX, EvalDigitDecompSpace) { std::string failed = "Large Precision Ciphertext Decomposition failed"; for (uint64_t i = st; i < st + 8; i++) { - auto ct1 = cc.Encrypt(sk, i, FRESH, p_basic * factor, Q); + auto ct1 = cc.Encrypt(sk, i, LARGE_DIM, p_basic * factor, Q); auto decomp = cc.EvalDecomp(ct1); EXPECT_EQ(usint(ceil(log(factor) / log(p_basic)) + 1), decomp.size()) << failed; diff --git a/src/pke/examples/scheme-switching.cpp b/src/pke/examples/scheme-switching.cpp index b964b7174..b58874407 100644 --- a/src/pke/examples/scheme-switching.cpp +++ b/src/pke/examples/scheme-switching.cpp @@ -311,29 +311,26 @@ void SwitchFHEWtoCKKS() { // Encrypt std::vector ctxtsLWE1(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE1[i] = - ccLWE->Encrypt(lwesk, x1[i]); // encrypted under small plantext modulus p = 4 and ciphertext modulus + // encrypted under small plantext modulus p = 4 and ciphertext modulus + ctxtsLWE1[i] = ccLWE->Encrypt(lwesk, x1[i]); } std::vector ctxtsLWE2(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE2[i] = - ccLWE->Encrypt(lwesk, x1[i], FRESH, - pLWE1); // encrypted under larger plaintext modulus p = 16 but small ciphertext modulus + // encrypted under larger plaintext modulus p = 16 but small ciphertext modulus + ctxtsLWE2[i] = ccLWE->Encrypt(lwesk, x1[i], LARGE_DIM, pLWE1); } std::vector ctxtsLWE3(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE3[i] = - ccLWE->Encrypt(lwesk, x2[i], FRESH, pLWE2, - modulus_LWE); // encrypted under larger plaintext modulus and large ciphertext modulus + // encrypted under larger plaintext modulus and large ciphertext modulus + ctxtsLWE3[i] = ccLWE->Encrypt(lwesk, x2[i], LARGE_DIM, pLWE2, modulus_LWE); } std::vector ctxtsLWE4(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE4[i] = - ccLWE->Encrypt(lwesk, x2[i], FRESH, pLWE3, - modulus_LWE); // encrypted under large plaintext modulus and large ciphertext modulus + // encrypted under large plaintext modulus and large ciphertext modulus + ctxtsLWE4[i] = ccLWE->Encrypt(lwesk, x2[i], LARGE_DIM, pLWE3, modulus_LWE); } // Step 5. Perform the scheme switching @@ -1485,15 +1482,14 @@ void PolyViaSchemeSwitching() { // Encrypt std::vector ctxtsLWE1(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE1[i] = ccLWE->Encrypt(privateKeyFHEW, - x1[i]); // encrypted under small plantext modulus p = 4 and ciphertext modulus + // encrypted under small plantext modulus p = 4 and ciphertext modulus + ctxtsLWE1[i] = ccLWE->Encrypt(privateKeyFHEW, x1[i]); } std::vector ctxtsLWE2(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE2[i] = - ccLWE->Encrypt(privateKeyFHEW, x2[i], FRESH, pLWE2, - modulus_LWE); // encrypted under large plaintext modulus and large ciphertext modulus + // encrypted under large plaintext modulus and large ciphertext modulus + ctxtsLWE2[i] = ccLWE->Encrypt(privateKeyFHEW, x2[i], LARGE_DIM, pLWE2, modulus_LWE); } // Step 5. Perform the scheme switching diff --git a/src/pke/extras/scheme-switching-timing.cpp b/src/pke/extras/scheme-switching-timing.cpp index 39bb00170..deef0ebec 100644 --- a/src/pke/extras/scheme-switching-timing.cpp +++ b/src/pke/extras/scheme-switching-timing.cpp @@ -290,9 +290,8 @@ void SwitchFHEWtoCKKS(uint32_t depth, uint32_t slots, uint32_t numValues) { // Encrypt std::vector ctxtsLWE(slots); for (uint32_t i = 0; i < slots; i++) { - ctxtsLWE[i] = - ccLWE->Encrypt(lwesk, x[i], FRESH, pLWE, - modulus_LWE); // encrypted under large plaintext modulus and large ciphertext modulus + // encrypted under large plaintext modulus and large ciphertext modulus + ctxtsLWE[i] = ccLWE->Encrypt(lwesk, x[i], LARGE_DIM, pLWE, modulus_LWE); } // Step 5. Perform the scheme switching diff --git a/src/pke/unittest/utckksrns/UnitTestSchemeSwitch.cpp b/src/pke/unittest/utckksrns/UnitTestSchemeSwitch.cpp index a4cb7a6f5..e99eae2c1 100644 --- a/src/pke/unittest/utckksrns/UnitTestSchemeSwitch.cpp +++ b/src/pke/unittest/utckksrns/UnitTestSchemeSwitch.cpp @@ -379,12 +379,12 @@ class UTCKKSRNS_SCHEMESWITCH : public ::testing::TestWithParamEncrypt(lwesk, x1[i], FRESH, 4, modulus_LWE); + ctxtsLWE1[i] = ccLWE->Encrypt(lwesk, x1[i], LARGE_DIM, 4, modulus_LWE); } std::vector ctxtsLWE2(testData.slots); for (uint32_t i = 0; i < testData.slots; i++) { // encrypted under larger plaintext modulus and large ciphertext modulus - ctxtsLWE2[i] = ccLWE->Encrypt(lwesk, x2[i], FRESH, pLWE, modulus_LWE); + ctxtsLWE2[i] = ccLWE->Encrypt(lwesk, x2[i], LARGE_DIM, pLWE, modulus_LWE); } cc->EvalFHEWtoCKKSSetup(ccLWE, testData.slots, testData.logQ); From 3f72724da523de56370f69567980085dece843bf Mon Sep 17 00:00:00 2001 From: dsuponitskiy Date: Fri, 18 Oct 2024 15:44:28 -0400 Subject: [PATCH 04/15] 852 support prngs v2 (#878) * Multiple changes for external PRNG * Changes for external PRNG * Corrections to the PRNG changes * Addressed code review comments * Correction to the condition allowing g++ only for external PRNG --------- Co-authored-by: Dmitriy Suponitskiy --- CMakeLists.txt | 6 + src/core/CMakeLists.txt | 4 +- src/core/examples/external-prng.cpp | 85 ++++++++++ src/core/include/math/distributiongenerator.h | 158 +++--------------- src/core/include/utils/memory.h | 3 + src/core/include/utils/prng/blake2engine.h | 151 +++++------------ src/core/include/utils/prng/prng.h | 102 +++++++++++ src/core/lib/math/distributiongenerator.cpp | 147 +++++++++++++++- src/core/lib/utils/memory.cpp | 41 +++++ src/core/lib/utils/prng/blake2engine.cpp | 51 ++++++ src/core/unittest/UnitTestTransform.cpp | 4 +- 11 files changed, 503 insertions(+), 249 deletions(-) create mode 100644 src/core/examples/external-prng.cpp create mode 100644 src/core/include/utils/prng/prng.h create mode 100644 src/core/lib/utils/memory.cpp create mode 100644 src/core/lib/utils/prng/blake2engine.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bc178b9e1..465c20b93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,6 +185,12 @@ if(WITH_COVTEST) set( COVDIR ${BUILDDIR}coverage/) endif() +if(UNIX AND NOT APPLE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # OpenFHE may use an external shared object provided by user for PRNG and linked with g++ on Linux. + # In order to ensure that OpenFHE can dynamically load shared objects at runtime, add an additional compiler flag: + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ldl") +endif() + if(BUILD_STATIC) set(OpenFHE_STATIC_LIBS OPENFHEcore_static OPENFHEpke_static OPENFHEbinfhe_static) endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 09206a5a8..5d01340dd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -46,13 +46,13 @@ install(DIRECTORY include/ add_custom_target( allcore ) if( BUILD_SHARED ) -set (CORELIBS PUBLIC OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) + set (CORELIBS PUBLIC OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) target_link_libraries (OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) add_dependencies( allcore OPENFHEcore) endif() if( BUILD_STATIC ) -set (CORELIBS ${CORELIBS} PUBLIC OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) + set (CORELIBS ${CORELIBS} PUBLIC OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) target_link_libraries (OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) add_dependencies( allcore OPENFHEcore_static) endif() diff --git a/src/core/examples/external-prng.cpp b/src/core/examples/external-prng.cpp new file mode 100644 index 000000000..507bf9790 --- /dev/null +++ b/src/core/examples/external-prng.cpp @@ -0,0 +1,85 @@ +//================================================================================== +// BSD 2-Clause License +// +// Copyright (c) 2014-2024, NJIT, Duality Technologies Inc. and other contributors +// +// All rights reserved. +// +// Author TPOC: contact@openfhe.org +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//================================================================================== + +// ATTENTION: enable this example for g++ on Linux only +//================================================================================== +#if (defined(__linux__) || defined(__unix__)) && !defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__) +//================================================================================== +#pragma GCC push_options +#pragma GCC optimize("O0") // Disable optimizations for this file +//================================================================================== + +#include "math/distributiongenerator.h" +#include +#include + +void usage() { + std::cerr << "Usage: ./external-prng [absolute path to the external PRNG library]" << std::endl; + std::cerr << " " << "If no absolute library path is provided, then the built-in OpenFHE's PRNG is used" << std::endl; +} + +int main(int argc, char* argv[]) { + if(argc > 1) { + std::string arg = argv[1]; + // handle -h + if (arg == "-h") { + usage(); + exit(0); + } + + std::cerr << "==== Using external PRNG" << std::endl; + lbcrypto::PseudoRandomNumberGenerator::InitPRNGEngine(arg); + } + else { + std::cerr << "==== Using OpenFHE's built-in PRNG" << std::endl; + } + + std::uniform_int_distribution<> dis(0, 10); + for ( size_t i = 0; i < 5; ++i) { + [[maybe_unused]] int randomNum = dis(lbcrypto::PseudoRandomNumberGenerator::GetPRNG()); + } + + return 0; +} + +//================================================================================== +#pragma GCC pop_options // Restore the previous optimization level +//================================================================================== +#else +// had to add the code below as clang++ didn't like linking this file without main. :) +#include + +int main(int argc, char* argv[]) { + std::cerr << "This example is for g++ on Linux only" << std::endl; + return 0; +} + +#endif diff --git a/src/core/include/math/distributiongenerator.h b/src/core/include/math/distributiongenerator.h index 960457021..70cbbebeb 100644 --- a/src/core/include/math/distributiongenerator.h +++ b/src/core/include/math/distributiongenerator.h @@ -34,160 +34,54 @@ all other distribution generators */ -#ifndef LBCRYPTO_INC_MATH_DISTRIBUTIONGENERATOR_H_ -#define LBCRYPTO_INC_MATH_DISTRIBUTIONGENERATOR_H_ +#ifndef __DISTRIBUTIONGENERATOR_H__ +#define __DISTRIBUTIONGENERATOR_H__ -// #include "math/math-hal.h" +#include "utils/prng/prng.h" -#include "utils/parallel.h" -#include "utils/prng/blake2engine.h" - -#include #include -// #include -#include -#include - -// #define FIXED_SEED // if defined, then uses a fixed seed number for -// reproducible results during debug. Use only one OMP thread to ensure -// reproducibility +#include namespace lbcrypto { +// if FIXED_SEED is defined, then PRNG uses a fixed seed number for reproducible results during debug. +// Use only one OMP thread to ensure reproducibility +// #define FIXED_SEED -// Defines the PRNG implementation used by OpenFHE. -// The cryptographically secure PRNG used by OpenFHE is based on BLAKE2 hash -// functions. A user can replace it with a different PRNG if desired by defining -// the same methods as for the Blake2Engine class. -typedef Blake2Engine PRNG; /** - * @brief The class providing the PRNG capability to all random distribution - * generators in OpenFHE. THe security of Ring Learning With Errors (used for - * all crypto capabilities in OpenFHE) depends on the randomness of uniform, - * ternary, and Gaussian distributions, which derive their randomness from the - * PRNG. + * @brief PseudoRandomNumberGenerator provides the PRNG capability to all random distribution generators in OpenFHE. + * The security of Ring Learning With Errors (used for all crypto capabilities in OpenFHE) depends on + * the randomness of uniform, ternary, and Gaussian distributions, which derive their randomness from the PRNG. */ class PseudoRandomNumberGenerator { public: /** - * @brief Returns a reference to the PRNG engine - */ - - // TODO: there may be an issue here - static void InitPRNG() { - int threads = OpenFHEParallelControls.GetNumThreads(); - if (threads == 0) { - threads = 1; - } -#pragma omp parallel for num_threads(threads) - for (int i = 0; i < threads; ++i) { - GetPRNG(); - } - } - - static PRNG& GetPRNG() { - // initialization of PRNGs - if (m_prng == nullptr) { -#pragma omp critical - { -#if defined(FIXED_SEED) - // Only used for debugging in the single-threaded mode. - std::cerr << "**FOR DEBUGGING ONLY!!!! Using fixed initializer for " - "PRNG. Use a single thread only, e.g., OMP_NUM_THREADS=1!" - << std::endl; - - std::array seed{}; - seed[0] = 1; - m_prng = std::make_shared(seed); -#else - // A 512-bit seed is generated for each thread (this roughly corresponds - // to 256 bits of security). The seed is the sum of a random sample - // generated using std::random_device (typically works correctly in - // Linux, MacOS X, and MinGW starting with GCC 9.2) and a BLAKE2 sample - // seeded from current time stamp, a hash of the current thread, and a - // memory location of a heap variable. The BLAKE2 sample is added in - // case random_device is deterministic (happens on MinGW with GCC - // below 9.2). All future calls to PRNG use the seed generated here. - - // The code below derives randomness from time, thread id, and a memory - // location of a heap variable. This seed is relevant only if the - // implementation of random_device is deterministic (as in older - // versions of GCC in MinGW) - std::array initKey{}; - // high-resolution clock typically has a nanosecond tick period - // Arguably this may give up to 32 bits of entropy as the clock gets - // recycled every 4.3 seconds - initKey[0] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); - // A thread id is often close to being random (on most systems) - initKey[1] = std::hash{}(std::this_thread::get_id()); - // On a 64-bit machine, the thread id is 64 bits long - // skip on 32-bit arm architectures - #if !defined(__arm__) && !defined(__EMSCRIPTEN__) - if (sizeof(size_t) == 8) - initKey[2] = (std::hash{}(std::this_thread::get_id()) >> 32); - #endif - - // heap variable; we are going to use the least 32 bits of its memory - // location as the counter for BLAKE2 This will increase the entropy of - // the BLAKE2 sample - void* mem = malloc(1); - uint32_t counter = reinterpret_cast(mem); // NOLINT - free(mem); + * @brief InitPRNGEngine() initializes the PRNG generator + * @param libPath a string with the absolute path to an external PRNG library ("/path/to/libprng.so"). + * If the string is empty, then the default (OpenFHE's built-in PRNG) library will be used. + * @note this function should be called at the beginning of main() if an external library to be used + */ + static void InitPRNGEngine(const std::string& libPath = std::string()); - PRNG gen(initKey, counter); - - std::uniform_int_distribution distribution(0); - std::array seed{}; - for (uint32_t i = 0; i < 16; i++) { - seed[i] = distribution(gen); - } - - std::array rdseed{}; - size_t attempts = 3; - bool rdGenPassed = false; - size_t idx = 0; - while (!rdGenPassed && idx < attempts) { - try { - std::random_device genR; - for (uint32_t i = 0; i < 16; i++) { - // we use the fact that there is no overflow for unsigned integers - // (from C++ standard) i.e., arithmetic mod 2^32 is performed. For - // the seed to be random, it is sufficient for one of the two - // samples below to be random. In almost all practical cases, - // distribution(genR) is random. We add distribution(gen) just in - // case there is an implementation issue with random_device (as in - // older MinGW systems). - rdseed[i] = distribution(genR); - } - rdGenPassed = true; - } - catch (std::exception& e) { - } - idx++; - } - - for (uint32_t i = 0; i < 16; i++) { - seed[i] += rdseed[i]; - } - - m_prng = std::make_shared(seed); -#endif - } - } - return *m_prng; - } + /** + * @brief Returns a reference to the PRNG engine + */ + static PRNG& GetPRNG(); private: + using GenPRNGEngineFuncPtr = PRNG* (*)(const PRNG::seed_array_t&, uint64_t counter); + // shared pointer to a thread-specific PRNG engine static std::shared_ptr m_prng; + // pointer to the function generating PRNG + static GenPRNGEngineFuncPtr genPRNGEngine; #if !defined(FIXED_SEED) - // avoid contention on m_prng - // local copies of m_prng are created for each thread + // avoid contention on m_prng: local copies of m_prng are created for each thread #pragma omp threadprivate(m_prng) #endif }; } // namespace lbcrypto -#endif // LBCRYPTO_INC_MATH_DISTRIBUTIONGENERATOR_H_ +#endif // __DISTRIBUTIONGENERATOR_H__ diff --git a/src/core/include/utils/memory.h b/src/core/include/utils/memory.h index 9f5db6397..99ebf9629 100644 --- a/src/core/include/utils/memory.h +++ b/src/core/include/utils/memory.h @@ -41,6 +41,7 @@ #include #include #include +#include namespace lbcrypto { @@ -79,6 +80,8 @@ void MoveAppend(std::vector& dst, std::vector& src) { } } +void secure_memset(void* mem, uint8_t c, size_t len); + } // namespace lbcrypto #endif // LBCRYPTO_UTILS_MEMORY_H diff --git a/src/core/include/utils/prng/blake2engine.h b/src/core/include/utils/prng/blake2engine.h index 48ff40133..e849439df 100644 --- a/src/core/include/utils/prng/blake2engine.h +++ b/src/core/include/utils/prng/blake2engine.h @@ -29,143 +29,72 @@ // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //================================================================================== - /* PRNG engine based on BLAKE2b */ -#ifndef _SRC_LIB_UTILS_BLAKE2ENGINE_H -#define _SRC_LIB_UTILS_BLAKE2ENGINE_H - -#include -#include -#include -#include -#include - -#include "blake2.h" - -#include "utils/exception.h" +#ifndef __BLAKE2ENGINE_H__ +#define __BLAKE2ENGINE_H__ -namespace lbcrypto { +#include "utils/prng/prng.h" -// the buffer stores 1024 samples of 32-bit integers -const uint32_t PRNG_BUFFER_SIZE = 1024; +#include +namespace default_prng { /** - * @brief Defines the PRNG engine used by OpenFHE. It is based on BLAKE2. Use - * this as a template for adding other PRNG engines to OpenFHE. + * @brief Defines the PRNG engine (based on BLAKE2) used by OpenFHE. It can be used + * as an example for adding other PRNG engines to OpenFHE. */ -class Blake2Engine { +class Blake2Engine : public PRNG { public: - // all C++11 distributions used in OpenFHE work by default with uint32_t - // a different data type can be specified if needed for a particular - // architecture - using result_type = uint32_t; - - /** - * @brief Constructor using a small seed - used for generating a large seed - */ - explicit Blake2Engine(result_type seed) - : m_counter(0), m_buffer({}), m_bufferIndex(0) { - m_seed[0] = seed; - } - - /** - * @brief Main constructor taking a vector of 16 integers as a seed - */ - explicit Blake2Engine(const std::array& seed) - : m_counter(0), m_seed(seed), m_buffer({}), m_bufferIndex(0) {} - - /** - * @brief Main constructor taking a vector of 16 integers as a seed and a - * counter - */ - explicit Blake2Engine(const std::array& seed, - result_type counter) - : m_counter(counter), m_seed(seed), m_buffer({}), m_bufferIndex(0) {} - - /** - * @brief minimum value used by C+11 distribution generators when no lower - * bound is explicitly specified by the user - */ - static constexpr result_type min() { - return std::numeric_limits::min(); - } - /** - * @brief maximum value used by C+11 distribution generators when no upper - * bound is explicitly specified by the user + * @brief Main constructor taking a vector of MAX_SEED_GENS integers as a seed and a counter. + * If there is no value for the counter, then pass zero as the counter value */ - static constexpr result_type max() { - return std::numeric_limits::max(); - } + explicit Blake2Engine(const PRNG::seed_array_t& seed, uint64_t counter) + : PRNG(seed, counter), m_buffer({}), m_bufferIndex(0) {} /** * @brief main call to the PRNG */ - result_type operator()() { - result_type result; - - if (m_bufferIndex == PRNG_BUFFER_SIZE) m_bufferIndex = 0; - - // makes a call to the BLAKE2 generator only when the currently buffered - // values are all consumed precomputations are done only once for the - // current buffer - if (m_bufferIndex == 0) Generate(); - - result = m_buffer[m_bufferIndex]; - - m_bufferIndex++; + PRNG::result_type operator()() override { + if (m_bufferIndex == static_cast(PRNG::PRNG_BUFFER_SIZE)) + m_bufferIndex = 0; - return result; - } + // makes a call to the BLAKE2 generator only when the currently buffered values are all consumed precomputations and + // done only once for the current buffer + if (m_bufferIndex == 0) + Generate(); - Blake2Engine(const Blake2Engine& other) { - m_counter = other.m_counter; - m_seed = other.m_seed; - m_buffer = other.m_buffer; - m_bufferIndex = other.m_bufferIndex; - } + PRNG::result_type result = m_buffer[m_bufferIndex]; + m_bufferIndex++; - void operator=(const Blake2Engine& other) { - m_counter = other.m_counter; - m_seed = other.m_seed; - m_buffer = other.m_buffer; - m_bufferIndex = other.m_bufferIndex; + return result; } private: - /** - * @brief The main call to blake2xb function - */ - void Generate() { - // m_counter is the input to the hash function - // m_buffer is the output - if (blake2xb(m_buffer.begin(), m_buffer.size() * sizeof(result_type), - &m_counter, sizeof(m_counter), m_seed.cbegin(), - m_seed.size() * sizeof(result_type)) != 0) { - OPENFHE_THROW("PRNG: blake2xb failed"); - } - m_counter++; - return; - } - - // counter used as input to the BLAKE2 hash function - // gets incremented after each call - uint64_t m_counter = 0; + /** + * @brief The main call to blake2xb function + */ + void Generate(); - // the seed for the BLAKE2 hash function - std::array m_seed{}; + // The vector that stores random samples generated using the hash function + std::array m_buffer{}; - // The vector that stores random samples generated using the hash function - std::array m_buffer{}; - - // Index in m_buffer corresponding to the current PRNG sample - uint16_t m_bufferIndex = 0; + // Index in m_buffer corresponding to the current PRNG sample + size_t m_bufferIndex = 0; }; -} // namespace lbcrypto +/** + * @brief createEngineInstance() generates a Blake2Engine object which is dynamically allocated + * @return pointer to the generated Blake2Engine object + * @attention the caller is responsible for freeing the memory allocated by this function + **/ +extern "C" { + PRNG* createEngineInstance(const PRNG::seed_array_t& seed, uint64_t counter); +} + +} // namespace default_prng #endif // clang-format on diff --git a/src/core/include/utils/prng/prng.h b/src/core/include/utils/prng/prng.h new file mode 100644 index 000000000..c64830138 --- /dev/null +++ b/src/core/include/utils/prng/prng.h @@ -0,0 +1,102 @@ +//================================================================================== +// BSD 2-Clause License +// +// Copyright (c) 2014-2022, NJIT, Duality Technologies Inc. and other contributors +// +// All rights reserved. +// +// Author TPOC: contact@openfhe.org +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//================================================================================== + +/** + * DISCLAIMER: IMPORTANT NOTICE ABOUT FILE MODIFICATIONS + * + * This file is used in OpenFHE's built-in PRNG and ANY EXTERNAL PRNG. + * The file is critical to the functionality and the security of the library. + * + * Modifications should only be performed by personnel who understand the potential impacts. + * + * By proceeding with changes to this file, you acknowledge that you understand the risks involved and + * accept full responsibility for any resulting issues. + */ + +/** + * Abstract base class for any PRNG engine. + */ + +#ifndef __PRNG_H__ +#define __PRNG_H__ + +#include +#include +#include + + +// ATTENTION (VERY IMPORTANT): +// for any engine class derived from the PRNG class there must be a C function named "createEngineInstance" +// returning a dynamically allocated object of that derived class (see how it is done in blake2engine.h) +class PRNG { +public: + enum { + MAX_SEED_GENS = 16, + // the buffer stores 1024 samples of 32-bit integers + PRNG_BUFFER_SIZE = 1024 + }; + + // all C++11 distributions used in OpenFHE work with uint32_t by default. + // a different data type can be specified if needed for a particular architecture + using result_type = uint32_t; + using seed_array_t = std::array; + + /** + * @brief minimum value used by C++11 distribution generators when no lower + * bound is explicitly specified by the user + */ + static constexpr result_type min() { + return std::numeric_limits::min(); + } + + /** + * @brief maximum value used by C++11 distribution generators when no upper + * bound is explicitly specified by the user + */ + static constexpr result_type max() { + return std::numeric_limits::max(); + } + + virtual result_type operator()() = 0; + virtual ~PRNG() = default; + +protected: + PRNG() = default; + PRNG(const seed_array_t &seed, uint64_t counter) : m_counter(counter), m_seed(seed) {} + + // counter used as input to the hash function; gets incremented after each call + uint64_t m_counter = 0; + + // the seed for the hash function + seed_array_t m_seed{}; +}; +#endif // __PRNG_H__ + diff --git a/src/core/lib/math/distributiongenerator.cpp b/src/core/lib/math/distributiongenerator.cpp index 995a412a7..b46b4f603 100644 --- a/src/core/lib/math/distributiongenerator.cpp +++ b/src/core/lib/math/distributiongenerator.cpp @@ -35,12 +35,157 @@ */ #include "math/distributiongenerator.h" +#include "utils/prng/blake2engine.h" +#include "utils/memory.h" +#include "utils/exception.h" -#include +#include +#include +#include #include +#include +#include namespace lbcrypto { std::shared_ptr PseudoRandomNumberGenerator::m_prng = nullptr; +PseudoRandomNumberGenerator::GenPRNGEngineFuncPtr PseudoRandomNumberGenerator::genPRNGEngine = nullptr; + +void PseudoRandomNumberGenerator::InitPRNGEngine(const std::string& libPath) { + if(genPRNGEngine) // if genPRNGEngine has already been initialized + return; + + if(libPath.empty()) { + // use the default OpenFHE PRNG that comes with the library + genPRNGEngine = default_prng::createEngineInstance; + if (!genPRNGEngine) + OPENFHE_THROW("Cannot find symbol: default_prng::createEngineInstance"); + // std::cerr << "InitPRNGEngine: using local PRNG" << std::endl; + } + else { + #if (defined(__linux__) || defined(__unix__)) && !defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__) + // enable this code for g++ on Linux only + // do not close libraryHandle, your application will crash if you do + void* libraryHandle = dlopen(libPath.c_str(), RTLD_LAZY); + if (!libraryHandle) { + std::string errMsg{std::string("Cannot open ") + libPath + ": "}; + const char* dlsym_error = dlerror(); + errMsg += dlsym_error; + OPENFHE_THROW(errMsg); + } + genPRNGEngine = (GenPRNGEngineFuncPtr)dlsym(libraryHandle, "createEngineInstance"); + if (!genPRNGEngine) { + std::string errMsg{std::string("Cannot load symbol createEngineInstance() from ") + libPath}; + const char* dlsym_error = dlerror(); + errMsg += ": "; + errMsg += dlsym_error; + dlclose(libraryHandle); + OPENFHE_THROW(errMsg); + } + std::cerr << __FUNCTION__ << ": using external PRNG" << std::endl; + #else + OPENFHE_THROW("OpenFHE may use an external PRNG library linked with g++ on Linux only"); + #endif + } +} + +PRNG& PseudoRandomNumberGenerator::GetPRNG() { + // initialization of PRNGs + if (m_prng == nullptr) { +#pragma omp critical + { + // we would like to believe that the block of code below is a good defense line + if (!genPRNGEngine) + InitPRNGEngine(); + if (!genPRNGEngine) + OPENFHE_THROW("Failure to initialize the PRNG engine"); + + PRNG::seed_array_t seed{}; + #if defined(FIXED_SEED) + // Only used for debugging in the single-threaded mode. + std::cerr << "**FOR DEBUGGING ONLY!!!! Using fixed initializer for PRNG. " + "Use a single thread only, e.g., OMP_NUM_THREADS=1!" + << std::endl; + + seed[0] = 1; + #else + // A 512-bit seed is generated for each thread (this roughly corresponds + // to 256 bits of security). The seed is the sum of a random sample + // generated using std::random_device (typically works correctly in + // Linux, MacOS X, and MinGW starting with GCC 9.2) and a PRNG sample + // seeded from current time stamp, a hash of the current thread, and a + // memory location of a heap variable. The PRNG sample is added in + // case random_device is deterministic (happens on MinGW with GCC + // below 9.2). All future calls to PRNG use the seed generated here. + + // The code below derives randomness from time, thread id, and a memory + // location of a heap variable. This seed is relevant only if the + // implementation of random_device is deterministic (as in older + // versions of GCC in MinGW) + PRNG::seed_array_t initKey{}; + // high-resolution clock typically has a nanosecond tick period + // Arguably this may give up to 32 bits of entropy as the clock gets + // recycled every 4.3 seconds + initKey[0] = std::chrono::high_resolution_clock::now().time_since_epoch().count(); + // A thread id is often close to being random (on most systems) + initKey[1] = std::hash{}(std::this_thread::get_id()); + // On a 64-bit machine, the thread id is 64 bits long + // skip on 32-bit arm architectures + #if !defined(__arm__) && !defined(__EMSCRIPTEN__) + if (sizeof(size_t) == 8) + initKey[2] = (std::hash{}(std::this_thread::get_id()) >> 32); + #endif + // heap variable; we are going to use the least 32 bits of its memory + // location as the counter. This will increase the entropy of the PRNG sample + void* mem = malloc(1); + uint64_t counter = reinterpret_cast(mem); + free(mem); + + std::uniform_int_distribution distribution(0); + // the code below is wrapped in to {} as we want to get rid of gen immediately after the loop + { + // "PRNG* gen" points at a dynamically allocated (using c++'s new()) memory!!! + std::unique_ptr gen(genPRNGEngine(initKey, counter)); + for (auto& s : seed) + s = distribution(*gen); + } + + PRNG::seed_array_t rdseed{}; + size_t attempts = 3; + bool rdGenPassed = false; + for (size_t i = 0; i < attempts && !rdGenPassed; ++i) { + try { + std::random_device genR; + for (auto& rds : rdseed) { + // we use the fact that there is no overflow for unsigned integers + // (from C++ standard) i.e., arithmetic mod 2^32 is performed. For + // the seed to be random, it is sufficient for one of the two + // samples below to be random. In almost all practical cases, + // distribution(genR) is random. We add distribution(gen) just in + // case there is an implementation issue with random_device (as in + // older MinGW systems). + rds = distribution(genR); + } + rdGenPassed = true; + } + catch (std::exception& e) { + } + } + for (uint32_t i = 0; i < PRNG::MAX_SEED_GENS; ++i) + seed[i] += rdseed[i]; + + // re-init rdseed for security reasons + const size_t bytes_to_clear = (rdseed.size()*sizeof(rdseed[0])); + secure_memset(rdseed.data(), 0, bytes_to_clear); + #endif // FIXED_SEED + m_prng = std::shared_ptr(genPRNGEngine(seed, 0)); + // re-init seed for security reasons + secure_memset(seed.data(), 0, bytes_to_clear); + if (!m_prng) + OPENFHE_THROW("Cannot create a PRNG engine"); + } // pragma omp critical + } + return *m_prng; +} } // namespace lbcrypto diff --git a/src/core/lib/utils/memory.cpp b/src/core/lib/utils/memory.cpp new file mode 100644 index 000000000..9c4b6b448 --- /dev/null +++ b/src/core/lib/utils/memory.cpp @@ -0,0 +1,41 @@ +//================================================================================== +// BSD 2-Clause License +// +// Copyright (c) 2014-2024, NJIT, Duality Technologies Inc. and other contributors +// +// All rights reserved. +// +// Author TPOC: contact@openfhe.org +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//================================================================================== +#include "utils/memory.h" + +namespace lbcrypto { + +void secure_memset(void* mem, uint8_t c, size_t len) { + volatile uint8_t* volatile ptr = (volatile uint8_t* volatile)mem; + for(size_t i = 0; i< len; ++i) + *(ptr+i) = c; +} + +} // namespace lbcrypto diff --git a/src/core/lib/utils/prng/blake2engine.cpp b/src/core/lib/utils/prng/blake2engine.cpp new file mode 100644 index 000000000..745be30e0 --- /dev/null +++ b/src/core/lib/utils/prng/blake2engine.cpp @@ -0,0 +1,51 @@ +//================================================================================== +// BSD 2-Clause License +// +// Copyright (c) 2014-2024, NJIT, Duality Technologies Inc. and other contributors +// +// All rights reserved. +// +// Author TPOC: contact@openfhe.org +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//================================================================================== +#include "utils/prng/blake2engine.h" +#include "utils/prng/blake2.h" +#include "utils/exception.h" + +namespace default_prng { + +void Blake2Engine::Generate() { + // m_counter is the input to the hash function + // m_buffer is the output + if (blake2xb(m_buffer.begin(), m_buffer.size() * sizeof(PRNG::result_type), &m_counter, sizeof(m_counter), + m_seed.cbegin(), m_seed.size() * sizeof(PRNG::result_type)) != 0) { + OPENFHE_THROW("PRNG: blake2xb failed"); + } + m_counter++; +} + +PRNG* createEngineInstance(const PRNG::seed_array_t& seed, uint64_t counter) { + return new Blake2Engine(seed, counter); +} + +} // namespace default_prng diff --git a/src/core/unittest/UnitTestTransform.cpp b/src/core/unittest/UnitTestTransform.cpp index b652c82dc..2e5803309 100644 --- a/src/core/unittest/UnitTestTransform.cpp +++ b/src/core/unittest/UnitTestTransform.cpp @@ -280,11 +280,9 @@ void CRT_CHECK_big_ring(const std::string& msg) { ChineseRemainderTransformArb().SetCylotomicPolynomial(cycloPoly, modulus); V input(n, modulus); - PRNG gen(1); - std::uniform_int_distribution<> dis(0, 100); // generates a number in [0,100] for (usint i = 0; i < n; i++) { - input.at(i) = typename V::Integer(dis(gen)); + input.at(i) = typename V::Integer(dis(PseudoRandomNumberGenerator::GetPRNG())); } auto output = ChineseRemainderTransformArb().ForwardTransform(input, squareRootOfRoot, bigModulus, bigRoot, m); From 1d3767917d86c0674c9cec0015f50ff6978fd65e Mon Sep 17 00:00:00 2001 From: yspolyakov <89226542+yspolyakov@users.noreply.github.com> Date: Mon, 21 Oct 2024 17:57:11 -0400 Subject: [PATCH 05/15] Added unit tests for larger plaintext moduli in BFV (#880) * added BFV unit tests for 45-bit plaintext moduli * changed the comment from 45-bit to 46-bit plaintext modulus --------- Co-authored-by: Yuriy Polyakov --- src/pke/unittest/UnitTestSHE.cpp | 54 +++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/pke/unittest/UnitTestSHE.cpp b/src/pke/unittest/UnitTestSHE.cpp index a1f41162c..ace66c117 100644 --- a/src/pke/unittest/UnitTestSHE.cpp +++ b/src/pke/unittest/UnitTestSHE.cpp @@ -143,7 +143,9 @@ constexpr usint BATCH = 16; constexpr usint BATCH_LRG = 1 << 12; constexpr usint PTM = 64; constexpr usint PTM_LRG = 65537; -constexpr usint BV_DSIZE = 4; +// checks BFV for a 46-bit plaintext modulus +constexpr uint64_t PTM_XTR_LRG = 35184372744193; +constexpr usint BV_DSIZE = 4; // clang-format off static std::vector testCases = { // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech, EncTech, PREMode @@ -271,6 +273,31 @@ static std::vector testCases = { { MULT_PACKED, "38", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, EXTENDED, DFLT}, }, { MULT_PACKED, "39", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, { MULT_PACKED, "40", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { MULT_PACKED, "41", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, EXTENDED, DFLT}, }, + { MULT_PACKED, "42", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, EXTENDED, DFLT}, }, + { MULT_PACKED, "43", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, EXTENDED, DFLT}, }, + { MULT_PACKED, "44", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, EXTENDED, DFLT}, }, + { MULT_PACKED, "45", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { MULT_PACKED, "46", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { MULT_PACKED, "47", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, EXTENDED, DFLT}, }, + { MULT_PACKED, "48", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, EXTENDED, DFLT}, }, + { MULT_PACKED, "49", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, EXTENDED, DFLT}, }, + { MULT_PACKED, "50", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, EXTENDED, DFLT}, }, + { MULT_PACKED, "51", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { MULT_PACKED, "52", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { MULT_PACKED, "53", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, STANDARD, DFLT}, }, + { MULT_PACKED, "54", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, STANDARD, DFLT}, }, + { MULT_PACKED, "55", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, STANDARD, DFLT}, }, + { MULT_PACKED, "56", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, STANDARD, DFLT}, }, + { MULT_PACKED, "57", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { MULT_PACKED, "58", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { MULT_PACKED, "59", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, STANDARD, DFLT}, }, + { MULT_PACKED, "60", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPS, STANDARD, DFLT}, }, + { MULT_PACKED, "61", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, STANDARD, DFLT}, }, + { MULT_PACKED, "62", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQ, STANDARD, DFLT}, }, + { MULT_PACKED, "63", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { MULT_PACKED, "64", {BFVRNS_SCHEME, DFLT, DFLT, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, DFLT, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + // ========================================== // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech, EncTech, PREMode { EVALATINDEX, "01", {BGVRNS_SCHEME, 256, 2, DFLT, BV_DSIZE, BATCH, UNIFORM_TERNARY, 1, 60, HEStd_NotSet, BV, FIXEDMANUAL, DFLT, PTM_LRG, DFLT, DFLT, DFLT, DFLT, STANDARD, DFLT}, }, @@ -313,6 +340,31 @@ static std::vector testCases = { { EVALATINDEX, "38", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_LRG, DFLT, DFLT, 1, HPSPOVERQ, EXTENDED, DFLT}, }, { EVALATINDEX, "39", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, { EVALATINDEX, "40", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { EVALATINDEX, "41", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, STANDARD, DFLT}, }, + { EVALATINDEX, "42", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, STANDARD, DFLT}, }, + { EVALATINDEX, "43", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, STANDARD, DFLT}, }, + { EVALATINDEX, "44", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, STANDARD, DFLT}, }, + { EVALATINDEX, "45", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { EVALATINDEX, "46", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { EVALATINDEX, "47", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, STANDARD, DFLT}, }, + { EVALATINDEX, "48", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, STANDARD, DFLT}, }, + { EVALATINDEX, "49", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, STANDARD, DFLT}, }, + { EVALATINDEX, "50", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, STANDARD, DFLT}, }, + { EVALATINDEX, "51", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { EVALATINDEX, "52", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, STANDARD, DFLT}, }, + { EVALATINDEX, "53", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, EXTENDED, DFLT}, }, + { EVALATINDEX, "54", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, EXTENDED, DFLT}, }, + { EVALATINDEX, "55", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, EXTENDED, DFLT}, }, + { EVALATINDEX, "56", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, EXTENDED, DFLT}, }, + { EVALATINDEX, "57", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { EVALATINDEX, "58", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, BV, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { EVALATINDEX, "59", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, EXTENDED, DFLT}, }, + { EVALATINDEX, "60", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPS, EXTENDED, DFLT}, }, + { EVALATINDEX, "61", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, EXTENDED, DFLT}, }, + { EVALATINDEX, "62", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQ, EXTENDED, DFLT}, }, + { EVALATINDEX, "63", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, UNIFORM_TERNARY, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + { EVALATINDEX, "64", {BFVRNS_SCHEME, DFLT, 0, DFLT, 20, BATCH, GAUSSIAN, DFLT, DFLT, DFLT, HYBRID, DFLT, DFLT, PTM_XTR_LRG, DFLT, DFLT, 1, HPSPOVERQLEVELED, EXTENDED, DFLT}, }, + // ========================================== // TestType, Descr, Scheme, RDim, MultDepth, SModSize, DSize, BatchSz, SecKeyDist, MaxRelinSkDeg, FModSize, SecLvl, KSTech, ScalTech, LDigits, PtMod, StdDev, EvalAddCt, KSCt, MultTech EncTech, PREMode { EVALMERGE, "01", {BGVRNS_SCHEME, 256, 2, DFLT, BV_DSIZE, BATCH, UNIFORM_TERNARY, 1, 60, HEStd_NotSet, BV, FIXEDMANUAL, DFLT, PTM_LRG, DFLT, DFLT, DFLT, DFLT, STANDARD, DFLT}, }, From 15b94b509a5f60859c474f88c814d6f4f9b992c5 Mon Sep 17 00:00:00 2001 From: yspolyakov <89226542+yspolyakov@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:20:29 -0400 Subject: [PATCH 06/15] removed noise during decoding for packed, coefpacked, and string encoding methods (#881) Co-authored-by: Yuriy Polyakov --- src/pke/lib/encoding/coefpackedencoding.cpp | 4 ++++ src/pke/lib/encoding/packedencoding.cpp | 6 ++++++ src/pke/lib/encoding/stringencoding.cpp | 10 ++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/pke/lib/encoding/coefpackedencoding.cpp b/src/pke/lib/encoding/coefpackedencoding.cpp index 6472b2f25..2ec3d8014 100644 --- a/src/pke/lib/encoding/coefpackedencoding.cpp +++ b/src/pke/lib/encoding/coefpackedencoding.cpp @@ -123,9 +123,13 @@ bool CoefPackedEncoding::Decode() { NativeInteger scfInv = scalingFactorInt.ModInverse(mod); NativePoly temp = encodedNativeVector.Times(scfInv).Mod(mod); fillVec(temp, mod, this->value); + // clears the values containing information about the noise + encodedNativeVector.SetValuesToZero(); } else { fillVec(this->encodedVector, mod, this->value); + // clears the values containing information about the noise + this->encodedVector.SetValuesToZero(); } return true; diff --git a/src/pke/lib/encoding/packedencoding.cpp b/src/pke/lib/encoding/packedencoding.cpp index 62a4b26e9..632eeeaa2 100644 --- a/src/pke/lib/encoding/packedencoding.cpp +++ b/src/pke/lib/encoding/packedencoding.cpp @@ -206,6 +206,8 @@ bool PackedEncoding::Decode() { firstElement = firstElement.Times(scfInv); firstElement = firstElement.Mod(ptm); fillVec(firstElement, ptm, this->value); + // clears the values containing information about the noise + this->GetElement().SetValuesToZero(); } else { NativePoly firstElement = this->GetElement().GetElementAtIndex(0); @@ -213,11 +215,15 @@ bool PackedEncoding::Decode() { firstElement = firstElement.Times(scfInv); firstElement = firstElement.Mod(ptm); fillVec(firstElement, ptm, this->value); + // clears the values containing information about the noise + this->GetElement().SetValuesToZero(); } } else { this->Unpack(&this->GetElement(), ptm); fillVec(this->encodedVector, ptm, this->value); + // clears the values containing information about the noise + this->GetElement().SetValuesToZero(); } return true; diff --git a/src/pke/lib/encoding/stringencoding.cpp b/src/pke/lib/encoding/stringencoding.cpp index 2a9a6ab78..307e9f6e3 100644 --- a/src/pke/lib/encoding/stringencoding.cpp +++ b/src/pke/lib/encoding/stringencoding.cpp @@ -92,10 +92,16 @@ static void fillPlaintext(const P& poly, std::string& str, const PlaintextModulu bool StringEncoding::Decode() { auto mod = this->encodingParams->GetPlaintextModulus(); - if (this->typeFlag == IsNativePoly) + if (this->typeFlag == IsNativePoly) { fillPlaintext(this->encodedNativeVector, this->ptx, mod); - else + // clears the values containing information about the noise + this->encodedNativeVector.SetValuesToZero(); + } + else { fillPlaintext(this->encodedVector, this->ptx, mod); + // clears the values containing information about the noise + this->encodedVector.SetValuesToZero(); + } return true; } From 67d3651b762a90ca3e89cfc7ceacd087ef5874d8 Mon Sep 17 00:00:00 2001 From: yspolyakov <89226542+yspolyakov@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:20:38 -0400 Subject: [PATCH 07/15] Update .readthedocs.yaml (#884) Added graphviz --- .readthedocs.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c1049bfaa..57a0a0ae8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -10,6 +10,8 @@ build: os: ubuntu-22.04 tools: python: "3.11" + apt_packages: + - graphviz # Build documentation in the docs/ directory with Sphinx sphinx: @@ -19,4 +21,4 @@ sphinx: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - - requirements: docs/requirements.txt \ No newline at end of file + - requirements: docs/requirements.txt From 2c05c4b138a12f0564abd263a4f64ba2ab3eedc7 Mon Sep 17 00:00:00 2001 From: dsuponitskiy Date: Wed, 23 Oct 2024 14:40:45 -0400 Subject: [PATCH 08/15] Additional library flag -ldl (#887) Co-authored-by: Dmitriy Suponitskiy --- CMakeLists.txt | 4 ++-- benchmark/CMakeLists.txt | 2 +- src/binfhe/CMakeLists.txt | 10 +++++----- src/core/CMakeLists.txt | 10 +++++----- src/pke/CMakeLists.txt | 10 +++++----- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 465c20b93..5e56efc06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,8 +187,8 @@ endif() if(UNIX AND NOT APPLE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # OpenFHE may use an external shared object provided by user for PRNG and linked with g++ on Linux. - # In order to ensure that OpenFHE can dynamically load shared objects at runtime, add an additional compiler flag: - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ldl") + # In order to ensure that OpenFHE can dynamically load shared objects at runtime, add an additional library: + set(ADDITIONAL_LIBS "-ldl") endif() if(BUILD_STATIC) diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 59e17e113..4384263d2 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -28,7 +28,7 @@ foreach (app ${BMARK_SRC_FILES}) target_include_directories( ${exe} PUBLIC ../third-party/google-benchmark/include ) set_target_properties( ${exe} PROPERTIES COMPILE_FLAGS "${REGEX_FLAG} -DHAVE_STEADY_CLOCK -DNDEBUG ${WARNING_FLAG}" ) - target_link_libraries ( ${exe} ${BMLIBS} ) + target_link_libraries ( ${exe} ${BMLIBS} ${ADDITIONAL_LIBS}) set (BMAPPS ${BMAPPS} ${exe}) endforeach() diff --git a/src/binfhe/CMakeLists.txt b/src/binfhe/CMakeLists.txt index 73864ffa9..568fc24ad 100644 --- a/src/binfhe/CMakeLists.txt +++ b/src/binfhe/CMakeLists.txt @@ -45,13 +45,13 @@ add_custom_target( allbinfhe ) if( BUILD_SHARED ) set (BINFHELIBS PUBLIC OPENFHEbinfhe PUBLIC OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) - target_link_libraries (OPENFHEbinfhe PUBLIC OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) + target_link_libraries (OPENFHEbinfhe PUBLIC OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS} ${ADDITIONAL_LIBS}) add_dependencies( allbinfhe OPENFHEbinfhe ) endif() if( BUILD_STATIC ) set (BINFHELIBS ${BINFHELIBS} PUBLIC OPENFHEbinfhe_static PUBLIC OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) - target_link_libraries (OPENFHEbinfhe_static PUBLIC OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) + target_link_libraries (OPENFHEbinfhe_static PUBLIC OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS} ${ADDITIONAL_LIBS}) add_dependencies( allbinfhe OPENFHEbinfhe_static ) endif() @@ -59,7 +59,7 @@ if( BUILD_UNITTESTS ) file (GLOB BINFHE_TEST_SRC_FILES CONFIGURE_DEPENDS unittest/*.cpp) add_executable (binfhe_tests ${BINFHE_TEST_SRC_FILES} ${UNITTESTMAIN}) set_property(TARGET binfhe_tests PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/unittest) - target_link_libraries ( binfhe_tests ${BINFHELIBS}) + target_link_libraries ( binfhe_tests ${BINFHELIBS} ${ADDITIONAL_LIBS}) if (NOT ${WITH_OPENMP}) target_link_libraries ( binfhe_tests PRIVATE Threads::Threads) endif() @@ -78,7 +78,7 @@ if( BUILD_EXAMPLES) add_executable ( ${exe} ${app} ) set_property(TARGET ${exe} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples/binfhe) set( BINFHEAPPS ${BINFHEAPPS} ${exe} ) - target_link_libraries ( ${exe} ${BINFHELIBS} ) + target_link_libraries ( ${exe} ${BINFHELIBS} ${ADDITIONAL_LIBS}) endforeach() file (GLOB BINFHE_EXAMPLES_SRC_FILES CONFIGURE_DEPENDS examples/pke/*.cpp) @@ -87,7 +87,7 @@ if( BUILD_EXAMPLES) add_executable ( ${exe} ${app} ) set_property(TARGET ${exe} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples/binfhe/pke) set( BINFHEAPPS ${BINFHEAPPS} ${exe} ) - target_link_libraries ( ${exe} ${BINFHELIBS} ) + target_link_libraries ( ${exe} ${BINFHELIBS} ${ADDITIONAL_LIBS}) endforeach() add_custom_target( allbinfheexamples ) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5d01340dd..e2c897ba6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -47,13 +47,13 @@ add_custom_target( allcore ) if( BUILD_SHARED ) set (CORELIBS PUBLIC OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) - target_link_libraries (OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) + target_link_libraries (OPENFHEcore ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS} ${ADDITIONAL_LIBS}) add_dependencies( allcore OPENFHEcore) endif() if( BUILD_STATIC ) set (CORELIBS ${CORELIBS} PUBLIC OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) - target_link_libraries (OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) + target_link_libraries (OPENFHEcore_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS} ${ADDITIONAL_LIBS}) add_dependencies( allcore OPENFHEcore_static) endif() @@ -66,7 +66,7 @@ if( BUILD_UNITTESTS ) set (CORE_TEST_SRC_FILES ${CORE_TEST_SRC_FILES}) add_executable( core_tests ${CORE_TEST_SRC_FILES} ${UNITTESTMAIN} ) set_property(TARGET core_tests PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/unittest) - target_link_libraries ( core_tests ${CORELIBS} ) + target_link_libraries ( core_tests ${CORELIBS} ${ADDITIONAL_LIBS}) if (NOT ${WITH_OPENMP}) target_link_libraries ( core_tests PRIVATE Threads::Threads) endif() @@ -89,7 +89,7 @@ if ( BUILD_EXAMPLES ) add_executable ( ${exe} ${app} ) set_property(TARGET ${exe} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples/core) set( COREAPPS ${COREAPPS} ${exe} ) - target_link_libraries ( ${exe} ${CORELIBS} ) + target_link_libraries ( ${exe} ${CORELIBS} ${ADDITIONAL_LIBS}) endforeach() add_custom_target( allcoreexamples ) @@ -105,7 +105,7 @@ if (BUILD_EXTRAS) add_executable ( ${exe} ${app} ) set_property(TARGET ${exe} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/extras/core) set( COREEXTRAS ${COREEXTRAS} ${exe} ) - target_link_libraries ( ${exe} ${CORELIBS} ) + target_link_libraries ( ${exe} ${CORELIBS} ${ADDITIONAL_LIBS}) endforeach() add_custom_target( allcoreextras ) diff --git a/src/pke/CMakeLists.txt b/src/pke/CMakeLists.txt index bdf9d5545..bad83a551 100644 --- a/src/pke/CMakeLists.txt +++ b/src/pke/CMakeLists.txt @@ -45,13 +45,13 @@ add_custom_target( allpke ) if( BUILD_SHARED ) set (PKELIBS PUBLIC OPENFHEpke PUBLIC OPENFHEcore PUBLIC OPENFHEbinfhe ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) - target_link_libraries (OPENFHEpke PUBLIC OPENFHEcore PUBLIC OPENFHEbinfhe ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) + target_link_libraries (OPENFHEpke PUBLIC OPENFHEcore PUBLIC OPENFHEbinfhe ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS} ${ADDITIONAL_LIBS}) add_dependencies( allpke OPENFHEpke) endif() if( BUILD_STATIC ) set (PKELIBS ${PKELIBS} PUBLIC OPENFHEpke_static PUBLIC OPENFHEcore_static PUBLIC OPENFHEbinfhe_static ${THIRDPARTYLIBS} ${OpenMP_CXX_FLAGS}) - target_link_libraries (OPENFHEpke_static PUBLIC OPENFHEcore_static PUBLIC OPENFHEbinfhe_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS}) + target_link_libraries (OPENFHEpke_static PUBLIC OPENFHEcore_static PUBLIC OPENFHEbinfhe_static ${THIRDPARTYSTATICLIBS} ${OpenMP_CXX_FLAGS} ${ADDITIONAL_LIBS}) add_dependencies( allpke OPENFHEpke_static) endif() @@ -65,7 +65,7 @@ if( BUILD_UNITTESTS ) set_property(TARGET pke_tests PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/unittest) target_include_directories(pke_tests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/unittest") target_include_directories(pke_tests PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/unittest/utils") - target_link_libraries ( pke_tests ${PKELIBS} ) + target_link_libraries ( pke_tests ${PKELIBS} ${ADDITIONAL_LIBS}) if (NOT ${WITH_OPENMP} ) target_link_libraries ( pke_tests PRIVATE Threads::Threads) endif() @@ -83,7 +83,7 @@ if ( BUILD_EXAMPLES) add_executable ( ${exe} ${app} ) set_property(TARGET ${exe} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/examples/pke) set( PKEAPPS ${PKEAPPS} ${exe} ) - target_link_libraries ( ${exe} ${PKELIBS} ) + target_link_libraries ( ${exe} ${PKELIBS} ${ADDITIONAL_LIBS}) endforeach() add_custom_target( allpkeexamples ) @@ -99,7 +99,7 @@ if (BUILD_EXTRAS) add_executable (${exe} ${app} ) set_property(TARGET ${exe} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/extras/pke) set( PKEEXTRAS ${PKEEXTRAS} ${exe} ) - target_link_libraries ( ${exe} ${PKELIBS} ) + target_link_libraries ( ${exe} ${PKELIBS} ${ADDITIONAL_LIBS}) endforeach() add_custom_target( allpkeextras ) From 651a244ff8333e44f235eb12ddb619a2cc7eb318 Mon Sep 17 00:00:00 2001 From: andreea-alexandru <31080521+andreea-alexandru@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:41:46 -0400 Subject: [PATCH 09/15] remove the extra keys in homomorphic decode (#888) --- src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp index d1cd57700..965453a2c 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-fhe.cpp @@ -937,7 +937,7 @@ std::vector FHECKKSRNS::FindSlotsToCoeffsRotationIndices(uint32_t slots // Computing all indices for baby-step giant-step procedure for encoding and decoding indexList.reserve(b + g - 2 + bRem + gRem - 2 + 1 + M); - for (int32_t s = 0; s < int32_t(levelBudget); s++) { + for (int32_t s = 0; s < int32_t(levelBudget) - flagRem; s++) { for (int32_t j = 0; j < g; j++) { indexList.emplace_back( ReduceRotation((j - (numRotations + 1) / 2 + 1) * (1 << (s * layersCollapse)), M / 4)); @@ -2337,8 +2337,8 @@ Plaintext FHECKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, co if (logc < 0) { OPENFHE_THROW("Too small scaling factor"); } - int32_t logValid = (logc <= MAX_BITS_IN_WORD) ? logc : MAX_BITS_IN_WORD; - int32_t logApprox = logc - logValid; + int32_t logValid = (logc <= MAX_BITS_IN_WORD) ? logc : MAX_BITS_IN_WORD; + int32_t logApprox = logc - logValid; double approxFactor = pow(2, logApprox); std::vector temp(2 * slots); @@ -2369,11 +2369,11 @@ Plaintext FHECKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, co double imagVal = prodFactor.imag(); if (realVal > realMax) { - realMax = realVal; + realMax = realVal; realMaxIdx = idx; } if (imagVal > imagMax) { - imagMax = imagVal; + imagMax = imagVal; imagMaxIdx = idx; } } @@ -2396,11 +2396,11 @@ Plaintext FHECKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, co int64_t re = std::llround(dre); int64_t im = std::llround(dim); - temp[i] = (re < 0) ? Max64BitValue() + re : re; + temp[i] = (re < 0) ? Max64BitValue() + re : re; temp[i + slots] = (im < 0) ? Max64BitValue() + im : im; } - const std::shared_ptr> bigParams = plainElement.GetParams(); + const std::shared_ptr> bigParams = plainElement.GetParams(); const std::vector>& nativeParams = bigParams->GetParams(); for (size_t i = 0; i < nativeParams.size(); i++) { @@ -2436,7 +2436,7 @@ Plaintext FHECKKSRNS::MakeAuxPlaintext(const CryptoContextImpl& cc, co // Scale back up by the approxFactor to get the correct encoding. if (logApprox > 0) { int32_t logStep = (logApprox <= MAX_LOG_STEP) ? logApprox : MAX_LOG_STEP; - auto intStep = DCRTPoly::Integer(uint64_t(1) << logStep); + auto intStep = DCRTPoly::Integer(uint64_t(1) << logStep); std::vector crtApprox(numTowers, intStep); logApprox -= logStep; From d006fb3731d85f9f4006bbbe061792f3d0232b6c Mon Sep 17 00:00:00 2001 From: yspolyakov <89226542+yspolyakov@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:07:28 -0400 Subject: [PATCH 10/15] Fixed the estimation logic for hybrid key switching (#883) * fixed the logic for estimating the hybrid key switching digit size * removed a dead branch --------- Co-authored-by: Yuriy Polyakov --- src/pke/include/schemerns/rns-cryptoparameters.h | 4 +++- .../scheme/bgvrns/bgvrns-parametergeneration.cpp | 14 +++++--------- .../scheme/ckksrns/ckksrns-parametergeneration.cpp | 7 +++++-- src/pke/lib/schemerns/rns-cryptoparameters.cpp | 6 +++++- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/pke/include/schemerns/rns-cryptoparameters.h b/src/pke/include/schemerns/rns-cryptoparameters.h index 2c2692c76..0d07d7db4 100644 --- a/src/pke/include/schemerns/rns-cryptoparameters.h +++ b/src/pke/include/schemerns/rns-cryptoparameters.h @@ -166,11 +166,13 @@ class CryptoParametersRNS : public CryptoParametersRLWE { * @param extraModulusSize bit size for extra modulus in FLEXIBLEAUTOEXT (CKKS and BGV only) * @param numPrimes number of moduli witout extraModulus * @param auxBits size of auxiliar moduli used for hybrid key switching + * @param addOne should an extra bit be added (for CKKS and BGV) * * @return log2 of the modulus and number of RNS limbs. */ static std::pair EstimateLogP(uint32_t numPartQ, double firstModulusSize, double dcrtBits, - double extraModulusSize, uint32_t numPrimes, uint32_t auxBits); + double extraModulusSize, uint32_t numPrimes, uint32_t auxBits, + bool addOne = false); /* * Estimates the extra modulus bitsize needed for threshold FHE noise flooding (only for BGV and BFV) diff --git a/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp b/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp index 8fb1340d1..007f1194b 100644 --- a/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp +++ b/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp @@ -444,21 +444,17 @@ bool ParameterGenerationBGVRNS::ParamsGenBGVRNS(std::shared_ptrEstimateMultipartyFloodingLogQ(); + // we add an extra bit to account for the special logic of selecting the RNS moduli in BGV + qBound++; + uint32_t auxTowers = 0; if (ksTech == HYBRID) { auto hybridKSInfo = - CryptoParametersRNS::EstimateLogP(numPartQ, firstModSize, dcrtBits, extraModSize, numPrimes, auxBits); + CryptoParametersRNS::EstimateLogP(numPartQ, firstModSize, dcrtBits, extraModSize, numPrimes, auxBits, true); qBound += std::get<0>(hybridKSInfo); auxTowers = std::get<1>(hybridKSInfo); } - // when the scaling technique is not FIXEDMANUAL (and not FLEXIBLEAUTOEXT), - // set a small value so that the rest of the logic could go through (this is a workaround) - // TODO we should uncouple the logic of FIXEDMANUAL and all FLEXIBLE MODES; some of the code above should be moved - // to the branch for FIXEDMANUAL - if (qBound == 0) - qBound = 20; - // HE Standards compliance logic/check uint32_t n = computeRingDimension(cryptoParams, qBound, cyclOrder); @@ -486,7 +482,7 @@ bool ParameterGenerationBGVRNS::ParamsGenBGVRNS(std::shared_ptr 1) ? std::log2(moduliQ[1].ConvertToDouble()) : 0, (scalTech == FLEXIBLEAUTOEXT) ? std::log2(moduliQ[moduliQ.size() - 1].ConvertToDouble()) : 0, - (scalTech == FLEXIBLEAUTOEXT) ? moduliQ.size() - 1 : moduliQ.size(), auxBits); + (scalTech == FLEXIBLEAUTOEXT) ? moduliQ.size() - 1 : moduliQ.size(), auxBits, true); newQBound += std::get<0>(hybridKSInfo); } } while (qBound < newQBound); diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp index 22bf6e99b..0c9e6a098 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp @@ -77,10 +77,13 @@ bool ParameterGenerationCKKSRNS::ParamsGenCKKSRNS(std::shared_ptr(hybridKSInfo); } diff --git a/src/pke/lib/schemerns/rns-cryptoparameters.cpp b/src/pke/lib/schemerns/rns-cryptoparameters.cpp index c7f3c8a5e..8028d5040 100644 --- a/src/pke/lib/schemerns/rns-cryptoparameters.cpp +++ b/src/pke/lib/schemerns/rns-cryptoparameters.cpp @@ -387,7 +387,7 @@ uint64_t CryptoParametersRNS::FindAuxPrimeStep() const { std::pair CryptoParametersRNS::EstimateLogP(uint32_t numPartQ, double firstModulusSize, double dcrtBits, double extraModulusSize, - uint32_t numPrimes, uint32_t auxBits) { + uint32_t numPrimes, uint32_t auxBits, bool addOne) { // numPartQ can not be zero as there is a division by numPartQ if (numPartQ == 0) OPENFHE_THROW("numPartQ is zero"); @@ -426,6 +426,10 @@ std::pair CryptoParametersRNS::EstimateLogP(uint32_t numPartQ, maxBits = bits; } + // we add an extra bit to account for for the special moduli selection logic in BGV and CKKS + if (addOne) + maxBits++; + // Select number of primes in auxiliary CRT basis auto sizeP = static_cast(std::ceil(maxBits / auxBits)); From c9384557eb39c4c58df5d15cc09d4d92c45f5c3a Mon Sep 17 00:00:00 2001 From: dsuponitskiy Date: Wed, 23 Oct 2024 16:51:27 -0400 Subject: [PATCH 11/15] Update quickstart.rst (#890) link to simple-real-numbers.cpp was broken Co-authored-by: rarzberger4 <91483040+rarzberger4@users.noreply.github.com> --- docs/sphinx_rsts/intro/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx_rsts/intro/quickstart.rst b/docs/sphinx_rsts/intro/quickstart.rst index 15ee5c7cd..b983758ac 100644 --- a/docs/sphinx_rsts/intro/quickstart.rst +++ b/docs/sphinx_rsts/intro/quickstart.rst @@ -129,7 +129,7 @@ Additionally, we include the variants detailing how to do serialization-deserial - `BFV-rns with Serialization/Deserialization (simple-integers-serial.cpp) `_ -- `Standard CKKS-rns (simple-real-numbers.cpp) `__: +- `Standard CKKS-rns (simple-real-numbers.cpp) `__: - `CKKS-rns with Serialization/Deserialization (simple-real-numbers-serial.cpp) `_ From 8f72e9031a9215e279a7c4ebb3d16cc9b35f9c45 Mon Sep 17 00:00:00 2001 From: pascoec <123595534+pascoec@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:36:29 -0400 Subject: [PATCH 12/15] intt optimization (#876) * intt optimization * added comments * more comments * omega[bitreversed(1)] * (n inverse) no longer precomputed --- .../math/hal/intnat/transformnat-impl.h | 91 ++++++++++++++++--- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/src/core/include/math/hal/intnat/transformnat-impl.h b/src/core/include/math/hal/intnat/transformnat-impl.h index fe8290ee2..772602e94 100644 --- a/src/core/include/math/hal/intnat/transformnat-impl.h +++ b/src/core/include/math/hal/intnat/transformnat-impl.h @@ -302,9 +302,25 @@ template void NumberTheoreticTransformNat::ForwardTransformToBitReverseInPlace(const VecType& rootOfUnityTable, const VecType& preconRootOfUnityTable, VecType* element) { - auto modulus{element->GetModulus()}; - uint32_t n(element->GetLength() >> 1), t{n}, logt{GetMSB(t)}; - for (uint32_t m{1}; m < n; m <<= 1, t >>= 1, --logt) { + // + // NTT based on the Cooley-Tukey (CT) butterfly + // Inputs: element (vector of size n in standard ordering) + // rootOfUnityTable (precomputed roots of unity in bit-reversed ordering) + // Output: NTT(element) in bit-reversed ordering + // + // for (m = 1, t = n, logt = log(t); m < n; m=2*m, t=t/2, --logt) do + // for (i = 0; i < m; ++i) do + // omega = rootOfUnityInverseTable[i + m] + // for (j1 = (i << logt), j2 = (j1 + t); j1 < j2; ++j1) do + // loVal = element[j1 + 0] + // hiVal = element[j1 + t]*omega + // element[j1 + 0] = (loVal + hiVal) mod modulus + // element[j1 + t] = (loVal - hiVal) mod modulus + // + + const auto modulus{element->GetModulus()}; + const uint32_t n(element->GetLength() >> 1); + for (uint32_t m{1}, t{n}, logt{GetMSB(t)}; m < n; m <<= 1, t >>= 1, --logt) { for (uint32_t i{0}; i < m; ++i) { auto omega{rootOfUnityTable[i + m]}; auto preconOmega{preconRootOfUnityTable[i + m]}; @@ -331,6 +347,7 @@ void NumberTheoreticTransformNat::ForwardTransformToBitReverseInPlace(c } } } + // peeled off last ntt stage for performance for (uint32_t i{0}; i < (n << 1); i += 2) { auto omegaFactor{(*element)[i + 1]}; auto omega{rootOfUnityTable[(i >> 1) + n]}; @@ -350,7 +367,7 @@ void NumberTheoreticTransformNat::ForwardTransformToBitReverseInPlace(c (*element)[i + 0] += omegaFactor - (omegaFactor >= (modulus - loVal) ? modulus : 0); if (omegaFactor > loVal) loVal += modulus; - (*element)[i + t] = loVal - omegaFactor; + (*element)[i + 1] = loVal - omegaFactor; #endif } } @@ -494,13 +511,38 @@ template void NumberTheoreticTransformNat::InverseTransformFromBitReverseInPlace( const VecType& rootOfUnityInverseTable, const VecType& preconRootOfUnityInverseTable, const IntType& cycloOrderInv, const IntType& preconCycloOrderInv, VecType* element) { + // + // INTT based on the Gentleman-Sande (GS) butterfly + // Inputs: element (vector of size n in bit-reversed ordering) + // rootOfUnityInverseTable (precomputed roots of unity in bit-reversed ordering) + // cycloOrderInv (n inverse) + // Output: INTT(element) in standard ordering + // + // for (m = n/2, t = 1, logt = 1; m >= 1; m=m/2, t=2*t, ++logt) do + // for (i = 0; i < m; ++i) do + // omega = rootOfUnityInverseTable[i + m] + // for (j1 = (i << logt), j2 = (j1 + t); j1 < j2; ++j1) do + // loVal = element[j1 + 0] + // hiVal = element[j1 + t] + // element[j1 + 0] = (loVal + hiVal) mod modulus + // element[j1 + t] = (loVal - hiVal)*omega mod modulus + // for (i = 0; i < n; ++i) do + // element[i] = element[i]*cycloOrderInv mod modulus + // + auto modulus{element->GetModulus()}; uint32_t n(element->GetLength()); + + // precomputed omega[bitreversed(1)] * (n inverse). used in final stage of intt. + auto omega1Inv{rootOfUnityInverseTable[1].ModMulFastConst(cycloOrderInv, modulus, preconCycloOrderInv)}; + auto preconOmega1Inv{omega1Inv.PrepModMulConst(modulus)}; + + // peeled off first stage for performance for (uint32_t i{0}; i < n; i += 2) { auto omega{rootOfUnityInverseTable[(i + n) >> 1]}; auto preconOmega{preconRootOfUnityInverseTable[(i + n) >> 1]}; - auto hiVal{(*element)[i + 1]}; auto loVal{(*element)[i + 0]}; + auto hiVal{(*element)[i + 1]}; #if defined(__GNUC__) && !defined(__clang__) auto omegaFactor{loVal}; if (omegaFactor < hiVal) @@ -509,28 +551,25 @@ void NumberTheoreticTransformNat::InverseTransformFromBitReverseInPlace loVal += hiVal; if (loVal >= modulus) loVal -= modulus; - loVal.ModMulFastConstEq(cycloOrderInv, modulus, preconCycloOrderInv); omegaFactor.ModMulFastConstEq(omega, modulus, preconOmega); - omegaFactor.ModMulFastConstEq(cycloOrderInv, modulus, preconCycloOrderInv); (*element)[i + 0] = loVal; (*element)[i + 1] = omegaFactor; #else auto omegaFactor{loVal + (hiVal > loVal ? modulus : 0) - hiVal}; loVal += hiVal - (hiVal >= (modulus - loVal) ? modulus : 0); - loVal.ModMulFastConstEq(cycloOrderInv, modulus, preconCycloOrderInv); (*element)[i + 0] = loVal; omegaFactor.ModMulFastConstEq(omega, modulus, preconOmega); - omegaFactor.ModMulFastConstEq(cycloOrderInv, modulus, preconCycloOrderInv); (*element)[i + 1] = omegaFactor; #endif } - for (uint32_t m{n >> 2}, t{2}, logt{2}; m >= 1; m >>= 1, t <<= 1, ++logt) { + // inner stages + for (uint32_t m{n >> 2}, t{2}, logt{2}; m > 1; m >>= 1, t <<= 1, ++logt) { for (uint32_t i{0}; i < m; ++i) { auto omega{rootOfUnityInverseTable[i + m]}; auto preconOmega{preconRootOfUnityInverseTable[i + m]}; for (uint32_t j1{i << logt}, j2{j1 + t}; j1 < j2; ++j1) { - auto hiVal{(*element)[j1 + t]}; auto loVal{(*element)[j1 + 0]}; + auto hiVal{(*element)[j1 + t]}; #if defined(__GNUC__) && !defined(__clang__) auto omegaFactor{loVal}; if (omegaFactor < hiVal) @@ -551,6 +590,35 @@ void NumberTheoreticTransformNat::InverseTransformFromBitReverseInPlace } } } + + // peeled off final stage to implement optimization where n/2 scalar multiplies + // by (n inverse) are incorporated into the omegaFactor calculation. + // Please see https://github.com/openfheorg/openfhe-development/issues/872 for details. + uint32_t j2{n >> 1}; + for (uint32_t j1{0}; j1 < j2; ++j1) { + auto loVal{(*element)[j1]}; + auto hiVal{(*element)[j1 + j2]}; +#if defined(__GNUC__) && !defined(__clang__) + auto omegaFactor{loVal}; + if (omegaFactor < hiVal) + omegaFactor += modulus; + omegaFactor -= hiVal; + loVal += hiVal; + if (loVal >= modulus) + loVal -= modulus; + omegaFactor.ModMulFastConstEq(omega1Inv, modulus, preconOmega1Inv); + (*element)[j1 + 0] = loVal; + (*element)[j1 + j2] = omegaFactor; +#else + (*element)[j1] += hiVal - (hiVal >= (modulus - loVal) ? modulus : 0); + auto omegaFactor = loVal + (hiVal > loVal ? modulus : 0) - hiVal; + omegaFactor.ModMulFastConstEq(omega1Inv, modulus, preconOmega1Inv); + (*element)[j1 + j2] = omegaFactor; +#endif + } + // perform remaining n/2 scalar multiplies by (n inverse) + for (uint32_t i = 0; i < j2; ++i) + (*element)[i].ModMulFastConstEq(cycloOrderInv, modulus, preconCycloOrderInv); } template @@ -706,7 +774,6 @@ void ChineseRemainderTransformFTTNat::InverseTransformFromBitReverse(co template void ChineseRemainderTransformFTTNat::PreCompute(const IntType& rootOfUnity, const usint CycloOrder, const IntType& modulus) { - // Half of cyclo order usint CycloOrderHf = (CycloOrder >> 1); auto mapSearch = m_rootOfUnityReverseTableByModulus.find(modulus); From f2012fe829259841bd2eba6ab6ffbd80c59d4c06 Mon Sep 17 00:00:00 2001 From: dsuponitskiy Date: Fri, 25 Oct 2024 10:04:34 -0400 Subject: [PATCH 13/15] 882 add documentation for external PRNG (#889) * Added documentation for external PRNGs, simplified a Blake2Engine constructor * Altered the documentation * Documentation changes * Documentation changes * Documentation formatting * Update prng.rst * Addressed review comments and added a condition for '#include ' --------- Co-authored-by: Dmitriy Suponitskiy Co-authored-by: pascoec <123595534+pascoec@users.noreply.github.com> --- docs/sphinx_rsts/modules/core/utils/prng.rst | 90 ++++++++++++++++++-- src/core/include/utils/prng/blake2engine.h | 3 +- src/core/lib/math/distributiongenerator.cpp | 4 +- 3 files changed, 88 insertions(+), 9 deletions(-) diff --git a/docs/sphinx_rsts/modules/core/utils/prng.rst b/docs/sphinx_rsts/modules/core/utils/prng.rst index 545a727f2..c64d020af 100644 --- a/docs/sphinx_rsts/modules/core/utils/prng.rst +++ b/docs/sphinx_rsts/modules/core/utils/prng.rst @@ -1,7 +1,6 @@ Pseudorandom Number Generator (PRNG) ===================================== - -Documentation for `core/include/utils/prng `_. Additionally, we refer users to :ref:`our sampling documentation` +.. note:: By default, OpenFHE uses a `built-in blake2-based PRNG `_, but provides the ability to integrate a cutom PRNG engine as a shared library. See below for instructions on how to implement and use a custom PRNG shared library. .. contents:: Page Contents :local: @@ -10,9 +9,88 @@ Documentation for `core/include/utils/prng `_, which allows fast hashing. +- The default cryptographic hash function in OpenFHE is based off of `Blake2b `_, which allows fast hashing. + +.. _for_existing_example: + +Building and testing an external PRNG engine (existing example) +------------------------------------------------------------- + +.. note:: Integration of an external PRNG engine is an experimental feature currently available only on Linux using the g++ compiler. We provide `an external blake2 PRNG example `_ as a refernece. See below for instructions on how to build your own custom PRNG engine. + +1. Build **OpenFHE 1.2.2+** by following `these instructions `_ and set **g++** as the default compiler. + +2. Clone `the external PRNG repo `_. + +3. Create a directory where the binaries will be built. The typical choice is a subfolder "build". In this case, the commands are: + :: + mkdir build + cd build + cmake .. + make + +4. Optionally install the shared object **libPRNGengine.so** you have just built. + + * for the default install location, run: + :: + sudo make install + + * for a custom install location you should run a different ``cmake`` command: + :: + cmake .. -DCMAKE_INSTALL_PREFIX=/custom/install/location + + and after that you run the remaining commands: + :: + make + make install + +5. Run `the example `_ to test the engine. It calls PseudoRandomNumberGenerator::InitPRNGEngine() which initializes PRNG either with the built-in engine or a custom one. + + * If executed without arguments, the example calls InitPRNGEngine() which initializes PRNG with the built-in engine: + :: + ./build/bin/examples/core/external-prng + + and the output should be: + :: + ==== Using internal PRNG + + * If your provide the absolute path to the external PRNG as an argument to the example, then InitPRNGEngine() will use that path to initialize PRNG with the custom engine. + + For example: if you install **libPRNGengine.so** to the default location (/usr/local), then you will run: + :: + ./build/bin/examples/core/external-prng /usr/local/lib/libPRNGengine.so + + which should produce: + :: + ==== Using external PRNG + InitPRNGEngine: using external PRNG + +.. note:: If PseudoRandomNumberGenerator::InitPRNGEngine() initializes PRNG with a custom engine, it always notifies the user by producing a trace **"InitPRNGEngine: using external PRNG"**. There is no trace for the built-in PRNG engine. InitPRNGEngine() always throws an exception if it fails. + + +Creating custom external PRNG engine using the existing example +---------------------------------------------------------------- + +You can create your own PRNG engine and use it with OpenFHE by following the steps below: + +1. Create a separate repo for your own engine and copy everything from `the example of external PRNG `_ to the new repo. + +2. Change CMakeLists.txt: replace **"PRNGengine"** (LIBRARY_NAME) with the name of your choice. + +3. Delete all source files from src/include and src/lib except: + :: + src/prng.h + src/include/blake2engine.h + src/lib/blake2engine.cpp + +4. Create a new class similar to Blake2Engine (use the code in blake2engine.h/blake2engine.cpp as an example), following the requirements below: + + * the class PRNG defined in prng.h must be used as the base class for the new class. The file prng.h is not allowed to be changed. + + * rename blake2engine.h and blake2engine.cpp with the name of your engine. -Using your own PRNG engine ------------------------------------ + * **only two public member functions** should be in the new class: a trivial **constructor with 2 input parameters** (seed array and counter) and **operator()** providing similar functionality as Blake2Engine does, which is generating numbers. + + * create extern "C" function **createEngineInstance()** returning a dynamically allocated object of the new class. OpenFHE finds this function by name using dlsym(), so you may not change the name. -To define new ``PRNG`` engines, refer to `blake2engine.h `_. +5. Follow `the instructions above <#for_existing_example>`_ to build and test your new PRNG. diff --git a/src/core/include/utils/prng/blake2engine.h b/src/core/include/utils/prng/blake2engine.h index e849439df..4ac7fa484 100644 --- a/src/core/include/utils/prng/blake2engine.h +++ b/src/core/include/utils/prng/blake2engine.h @@ -51,8 +51,7 @@ class Blake2Engine : public PRNG { * @brief Main constructor taking a vector of MAX_SEED_GENS integers as a seed and a counter. * If there is no value for the counter, then pass zero as the counter value */ - explicit Blake2Engine(const PRNG::seed_array_t& seed, uint64_t counter) - : PRNG(seed, counter), m_buffer({}), m_bufferIndex(0) {} + explicit Blake2Engine(const PRNG::seed_array_t& seed, uint64_t counter) : PRNG(seed, counter) {} /** * @brief main call to the PRNG diff --git a/src/core/lib/math/distributiongenerator.cpp b/src/core/lib/math/distributiongenerator.cpp index b46b4f603..29461fed7 100644 --- a/src/core/lib/math/distributiongenerator.cpp +++ b/src/core/lib/math/distributiongenerator.cpp @@ -41,10 +41,12 @@ #include #include -#include #include #include #include +#if (defined(__linux__) || defined(__unix__)) && !defined(__APPLE__) && defined(__GNUC__) && !defined(__clang__) + #include +#endif namespace lbcrypto { From 2592fc97189cf912462f0c293cb8292b72e797ea Mon Sep 17 00:00:00 2001 From: yspolyakov <89226542+yspolyakov@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:17:40 -0400 Subject: [PATCH 14/15] fixed a bug with parameter selection (#893) * fixed a bug with parameter selection * Addressed review comments --------- Co-authored-by: Yuriy Polyakov Co-authored-by: Dmitriy Suponitskiy --- .../lib/scheme/bgvrns/bgvrns-parametergeneration.cpp | 6 ++++-- .../scheme/ckksrns/ckksrns-parametergeneration.cpp | 4 +++- src/pke/lib/schemerns/rns-cryptoparameters.cpp | 11 ++++++----- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp b/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp index 007f1194b..c617d53ca 100644 --- a/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp +++ b/src/pke/lib/scheme/bgvrns/bgvrns-parametergeneration.cpp @@ -445,7 +445,9 @@ bool ParameterGenerationBGVRNS::ParamsGenBGVRNS(std::shared_ptrEstimateMultipartyFloodingLogQ(); // we add an extra bit to account for the special logic of selecting the RNS moduli in BGV - qBound++; + // ignore the case when there is only one max size modulus + if (qBound != auxBits) + qBound++; uint32_t auxTowers = 0; if (ksTech == HYBRID) { @@ -482,7 +484,7 @@ bool ParameterGenerationBGVRNS::ParamsGenBGVRNS(std::shared_ptr 1) ? std::log2(moduliQ[1].ConvertToDouble()) : 0, (scalTech == FLEXIBLEAUTOEXT) ? std::log2(moduliQ[moduliQ.size() - 1].ConvertToDouble()) : 0, - (scalTech == FLEXIBLEAUTOEXT) ? moduliQ.size() - 1 : moduliQ.size(), auxBits, true); + (scalTech == FLEXIBLEAUTOEXT) ? moduliQ.size() - 1 : moduliQ.size(), auxBits, false); newQBound += std::get<0>(hybridKSInfo); } } while (qBound < newQBound); diff --git a/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp b/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp index 0c9e6a098..c2840ad3d 100644 --- a/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp +++ b/src/pke/lib/scheme/ckksrns/ckksrns-parametergeneration.cpp @@ -78,7 +78,9 @@ bool ParameterGenerationCKKSRNS::ParamsGenCKKSRNS(std::shared_ptr CryptoParametersRNS::EstimateLogP(uint32_t numPartQ, qi[sizeQ - 1] = extraModulusSize; // Compute partitions of Q into numPartQ digits - double maxBits = 0; + uint32_t maxBits = 0; for (size_t j = 0; j < numPartQ; ++j) { size_t startTower = j * numPerPartQ; size_t endTower = ((j + 1) * numPerPartQ - 1 < sizeQ) ? (j + 1) * numPerPartQ - 1 : sizeQ - 1; // sum qi elements qi[startTower] + ... + qi[endTower] inclusive. the end element should be qi.begin()+(endTower+1) - double bits = std::accumulate(qi.begin() + startTower, qi.begin() + (endTower + 1), 0.0); + uint32_t bits = static_cast(std::accumulate(qi.begin() + startTower, qi.begin() + (endTower + 1), 0.0)); if (bits > maxBits) maxBits = bits; } - // we add an extra bit to account for for the special moduli selection logic in BGV and CKKS - if (addOne) + // we add an extra bit to account for the special moduli selection logic in BGV and CKKS + // ignore the case when there is only one max size modulus + if (addOne && (maxBits != auxBits)) maxBits++; // Select number of primes in auxiliary CRT basis - auto sizeP = static_cast(std::ceil(maxBits / auxBits)); + auto sizeP = static_cast(std::ceil(static_cast(maxBits) / auxBits)); return std::make_pair(sizeP * auxBits, sizeP); } From 22c0eca56c36e6d9fd49b64f7ad674dfa4d28fd2 Mon Sep 17 00:00:00 2001 From: yspolyakov <89226542+yspolyakov@users.noreply.github.com> Date: Mon, 28 Oct 2024 17:18:20 -0400 Subject: [PATCH 15/15] updates to v1.2.2 (#894) Co-authored-by: Yuriy Polyakov --- CMakeLists.txt | 2 +- docs/static_docs/Release_Notes.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e56efc06..9a87a38bf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ project (OpenFHE C CXX) set(OPENFHE_VERSION_MAJOR 1) set(OPENFHE_VERSION_MINOR 2) -set(OPENFHE_VERSION_PATCH 1) +set(OPENFHE_VERSION_PATCH 2) set(OPENFHE_VERSION ${OPENFHE_VERSION_MAJOR}.${OPENFHE_VERSION_MINOR}.${OPENFHE_VERSION_PATCH}) set(CMAKE_CXX_STANDARD 17) diff --git a/docs/static_docs/Release_Notes.md b/docs/static_docs/Release_Notes.md index 692661bcc..95ce30138 100644 --- a/docs/static_docs/Release_Notes.md +++ b/docs/static_docs/Release_Notes.md @@ -1,3 +1,11 @@ +10/28/2024: OpenFHE 1.2.2 (stable) is released + +* Improves the runtime of inverse NTT (for clang++; #872) +* Extends the support of extended (RLWE) parameters in DM/CGGI (#867) +* Includes several bug fixes + +The detailed list of changes is available at https://github.com/openfheorg/openfhe-development/issues?q=is%3Aissue+milestone%3A%22Release+1.2.2%22 + 09/10/2024: OpenFHE 1.2.1 (stable) is released * Fixes compilation issues with g++ 14 and clang++ 18 (#822, #835)