//================================================================================== // BSD 2-Clause License // // Copyright (c) 2014-2025, 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. //================================================================================== /* Control for encryption operations */ #ifndef SRC_PKE_INCLUDE_CRYPTOCONTEXT_H_ #define SRC_PKE_INCLUDE_CRYPTOCONTEXT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "binfhecontext.h" #include "ciphertext.h" #include "cryptocontext-fwd.h" #include "cryptocontextfactory.h" #include "encoding/plaintextfactory.h" #include "key/evalkey.h" #include "key/keypair.h" #include "scheme/scheme-swch-params.h" #include "schemebase/base-pke.h" #include "schemebase/base-scheme.h" #include "schemerns/rns-cryptoparameters.h" #include "utils/caller_info.h" #include "utils/diagnostic_output.h" #include "utils/type_name.h" #ifdef DEBUG_KEY #include #endif namespace lbcrypto { /** * @class CryptoContextImpl * @brief A class to simplify access to OpenFHE's PKE functionality. * * Functionality is accessed by creating an instance of CryptoContextImpl. * Various objects are "created" in the instance, but they can only be used with the context in which they were created. * * OpenFHE methods are accessed through CryptoContextImpl methods. Guards are implemented to make certain that * only valid objects that have been created in the context are used * * Contexts are created using GenCryptoContext(), and can be serialized and recovered from serialization */ template class CryptoContextImpl : public Serializable { using IntType = typename Element::Integer; using ParmType = typename Element::Params; /** * @brief Checks if the cryptocontext scheme is CKKS and throws an exception if it is not. * * @param functionName the calling function name. __func__ can be used instead */ inline void VerifyCKKSScheme(const std::string& functionName) const { if (!isCKKS(m_schemeId)) { std::string errMsg = std::string(functionName) + "() is available for the CKKS scheme only." " The current scheme is " + convertToString(m_schemeId); OPENFHE_THROW(errMsg); } } /** * @brief VerifyCKKSRealDataType Checks if the CKKS data type is real and throws an exception if it is not. * * @param functionName the calling function name. __func__ can be used instead */ inline void VerifyCKKSRealDataType(const std::string& functionName) const { if (GetCKKSDataType() != REAL) { std::string errMsg = "Function " + std::string(functionName) + " is available for the real CKKS data types only."; OPENFHE_THROW(errMsg); } } void SetKSTechniqueInScheme(); const CryptoContext GetContextForPointer(const CryptoContextImpl* cc) const { const auto& contexts = CryptoContextFactory::GetAllContexts(); for (const auto& ctx : contexts) { if (cc == ctx.get()) return ctx; } OPENFHE_THROW("Cannot find context for the given pointer to CryptoContextImpl"); } /** * @brief Constructs CoefPackedEncoding or PackedEncoding in this context * * @param encoding encoding type * @param value the value to encode * @param depth the multiplicative depth to encode the plaintext at * @param level the level to encode the plaintext at * @return new plaintext */ Plaintext MakePlaintext(const PlaintextEncodings encoding, const std::vector& value, size_t depth, uint32_t level) const { const auto cryptoParams = std::dynamic_pointer_cast(m_params); if (!cryptoParams) OPENFHE_THROW("Invalid crypto parameters: expected CryptoParametersRNS"); if (level > 0) { size_t numModuli = cryptoParams->GetElementParams()->GetParams().size(); if (!isBFVRNS(m_schemeId)) { // we throw an exception if level >= numModuli. However, we use multiplicativeDepth in the error message, // so the user can understand the error more easily. if (level >= numModuli) { uint32_t multiplicativeDepth = (cryptoParams->GetScalingTechnique() == FLEXIBLEAUTOEXT) ? (numModuli - 2) : (numModuli - 1); std::string errorMsg{"The level value should be less than or equal to "}; errorMsg += ((cryptoParams->GetScalingTechnique() == FLEXIBLEAUTOEXT) ? "(multiplicativeDepth + 1)." : "multiplicativeDepth."); errorMsg += " Currently: level is [" + std::to_string(level) + "] and multiplicativeDepth is [" + std::to_string(multiplicativeDepth) + "]"; OPENFHE_THROW(errorMsg); } } else { if ((cryptoParams->GetMultiplicationTechnique() == BEHZ) || (cryptoParams->GetMultiplicationTechnique() == HPS)) { OPENFHE_THROW( "BFV: Encoding at level > 0 is not currently supported for BEHZ or HPS. Use one of the HPSPOVERQ* methods instead."); } if ((cryptoParams->GetEncryptionTechnique() == EXTENDED)) { OPENFHE_THROW( "BFV: Encoding at level > 0 is not currently supported for the EXTENDED encryption method. Use the STANDARD encryption method instead."); } if (level >= numModuli) { std::string errorMsg = "The level value should be less the current number of RNS limbs in the cryptocontext."; errorMsg += " Currently: level is [" + std::to_string(level) + "] and number of RNS limbs is [" + std::to_string(numModuli) + "]"; OPENFHE_THROW(errorMsg); } } } // uses a parameter set with a reduced number of RNS limbs corresponding to the level std::shared_ptr> elemParamsPtr; if (level != 0) { ILDCRTParams elemParams = *(cryptoParams->GetElementParams()); for (uint32_t i = 0; i < level; i++) { elemParams.PopLastParam(); } elemParamsPtr = std::make_shared>(elemParams); } else { elemParamsPtr = cryptoParams->GetElementParams(); } NativeInteger scf{1}; bool setNoiseScaleDeg = false; auto scaleTech = cryptoParams->GetScalingTechnique(); if (isBGVRNS(m_schemeId) && (scaleTech == FLEXIBLEAUTO || scaleTech == FLEXIBLEAUTOEXT)) { if (scaleTech == FLEXIBLEAUTOEXT && level == 0) { scf = cryptoParams->GetScalingFactorIntBig(level); depth = 1; setNoiseScaleDeg = true; } else scf = cryptoParams->GetScalingFactorInt(level); } Plaintext p = PlaintextFactory::MakePlaintext(value, encoding, elemParamsPtr, this->GetEncodingParams(), getSchemeId(), depth, level, scf); if (setNoiseScaleDeg) p->SetNoiseScaleDeg(2); return p; } /** * @brief Constructs CoefPackedEncoding, PackedEncoding in this context * * @param encoding encoding type * @param cc the context to create a plaintext with * @param value the value to encode * @return new plaintext */ template static Plaintext MakePlaintext(PlaintextEncodings encoding, CryptoContext cc, const Value1& value) { return PlaintextFactory::MakePlaintext(value, encoding, cc->GetElementParams(), cc->GetEncodingParams()); } template static Plaintext MakePlaintext(PlaintextEncodings encoding, CryptoContext cc, const Value1& value, const Value2& value2) { return PlaintextFactory::MakePlaintext(encoding, cc->GetElementParams(), cc->GetEncodingParams(), value, value2); } /** * @brief Gets automorphism keys for a specific secret key tag and an array of specific indices * * @param keyTag secret key tag * @param indexList array of specific indices to retrieve key for * @return shared_ptr to std::map where the map key/data pair is index/automorphism key */ static std::shared_ptr>> GetPartialEvalAutomorphismKeyMapPtr( const std::string& keyTag, const std::vector& indexList); // cached evalmult keys, by secret key UID static std::map>> s_evalMultKeyMap; // cached evalautomorphism keys, by secret key UID static std::map>>> s_evalAutomorphismKeyMap; protected: /// crypto parameters std::shared_ptr> m_params{nullptr}; /// algorithm used; accesses all crypto methods std::shared_ptr> m_scheme{nullptr}; SCHEME m_schemeId{SCHEME::INVALID_SCHEME}; ///< identifier of the scheme this crypto context implements uint32_t m_keyGenLevel{0}; ///< level at which evaluation keys should be generated (for future use) /** * @brief TypeCheck makes sure that an operation between two ciphertexts is permitted * * @param a ciphertext1 * @param b ciphertext2 */ void TypeCheck(ConstCiphertext& a, ConstCiphertext& b, CALLER_INFO_ARGS_HDR) const { if (a == nullptr || b == nullptr) { std::string errorMsg(std::string("Null Ciphertext") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (a->GetCryptoContext().get() != this) { std::string errorMsg(std::string("Ciphertext was not created in this CryptoContext") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (a->GetCryptoContext() != b->GetCryptoContext()) { std::string errorMsg(std::string("Ciphertexts were not created in the same CryptoContext") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (a->GetKeyTag() != b->GetKeyTag()) { std::string errorMsg(std::string("Ciphertexts were not encrypted with same keys") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (a->GetEncodingType() != b->GetEncodingType()) { std::stringstream ss; ss << "Ciphertext encoding types " << a->GetEncodingType(); ss << " and " << b->GetEncodingType(); ss << " do not match"; ss << CALLER_INFO; OPENFHE_THROW(ss.str()); } } /** * @brief TypeCheck makes sure that an operation between a ciphertext and a plaintext is permitted * * @param a ciphertext * @param b plaintext */ void TypeCheck(ConstCiphertext& a, ConstPlaintext& b, CALLER_INFO_ARGS_HDR) const { if (a == nullptr) { std::string errorMsg(std::string("Null Ciphertext") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (b == nullptr) { std::string errorMsg(std::string("Null Plaintext") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (a->GetCryptoContext().get() != this) { std::string errorMsg(std::string("Ciphertext was not created in this CryptoContext") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (a->GetEncodingType() != b->GetEncodingType()) { std::stringstream ss; ss << "Ciphertext encoding type " << a->GetEncodingType(); ss << " and Plaintext encoding type " << b->GetEncodingType(); ss << " do not match"; ss << CALLER_INFO; OPENFHE_THROW(ss.str()); } } /** * @brief Checks whether a crypto context is a different object than this one. * * @param a the crypto context to compare with * @return true if a is not this crypto context */ bool Mismatched(const CryptoContext a) const { return a.get() != this; } /** * @brief Throws if a key is null or was not generated with this crypto context. * * @param key the key to validate (any key type with GetCryptoContext()) */ template void ValidateKey(const T& key, CALLER_INFO_ARGS_HDR) const { if (key == nullptr) { std::string errorMsg(std::string("Key is nullptr") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (Mismatched(key->GetCryptoContext())) { std::string errorMsg(std::string("Key was not generated with the same crypto context") + CALLER_INFO); OPENFHE_THROW(errorMsg); } } /** * @brief Throws if a ciphertext is null or was not generated with this crypto context. * * @param ciphertext the ciphertext to validate */ void ValidateCiphertext(ConstCiphertext& ciphertext, CALLER_INFO_ARGS_HDR) const { if (ciphertext == nullptr) { std::string errorMsg(std::string("Ciphertext is nullptr") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (Mismatched(ciphertext->GetCryptoContext())) { std::string errorMsg(std::string("Ciphertext was not generated with the same crypto context") + CALLER_INFO); OPENFHE_THROW(errorMsg); } } /** * @brief Throws if proxy re-encryption is disabled for this crypto context, i.e., HYBRID key switching is * used and the PRE mode is NOT_SET. */ void ValidatePREMode(CALLER_INFO_ARGS_HDR) const { const auto cryptoParams = std::dynamic_pointer_cast(m_params); if (cryptoParams == nullptr) OPENFHE_THROW("Invalid crypto parameters: expected CryptoParametersRNS"); if (cryptoParams->GetKeySwitchTechnique() == HYBRID && cryptoParams->GetPREMode() == NOT_SET) OPENFHE_THROW( "PRE is disabled (default = NOT_SET). Enable with SetPREMode() before generating cryptocontext."); } /** * @brief Throws if a precomputed set of powers is null, empty, or was not generated with this crypto context. * * @param powers the powers to validate */ void ValidateSeriesPowers(std::shared_ptr> powers, CALLER_INFO_ARGS_HDR) const { if (powers == nullptr) { std::string errorMsg(std::string("The object for powers is nullptr") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (powers->powersRe.size() == 0) { std::string errorMsg(std::string("The powersRe member is empty") + CALLER_INFO); OPENFHE_THROW(errorMsg); } if (Mismatched(powers->powersRe[0]->GetCryptoContext())) { std::string errorMsg(std::string("Power ciphertext was not generated with the same crypto context") + CALLER_INFO); OPENFHE_THROW(errorMsg); } } /** * @brief Creates and encodes a CKKS plaintext from complex values at a given level and noise scale degree, * validating the level against the modulus chain and choosing the element parameters (the towers remaining at * that level) and the scaling factor of the level when params is null. * * @param value the complex values to encode * @param noiseScaleDeg noise scale degree of the plaintext * @param level level of the plaintext (must be below the number of RNS moduli) * @param params element parameters to encode with (null = derived from the level) * @param slots number of slots (0 = default) * @return the encoded plaintext */ virtual Plaintext MakeCKKSPackedPlaintextInternal(const std::vector>& value, size_t noiseScaleDeg, uint32_t level, const std::shared_ptr params, uint32_t slots) const { VerifyCKKSScheme(__func__); const auto cryptoParams = std::dynamic_pointer_cast(m_params); if (!cryptoParams) OPENFHE_THROW("Invalid crypto parameters: expected CryptoParametersRNS"); if (level > 0) { // validation of level: We need to compare it to multiplicativeDepth, but multiplicativeDepth is not // readily available. so, what we get is numModuli and use it for calculations size_t numModuli = cryptoParams->GetElementParams()->GetParams().size(); uint32_t multiplicativeDepth = (cryptoParams->GetScalingTechnique() == FLEXIBLEAUTOEXT) ? (numModuli - 2) : (numModuli - 1); // we throw an exception if level >= numModuli. however, we use multiplicativeDepth in the error message, // so the user can understand the error more easily. if (level >= numModuli) { std::string errorMsg; if (cryptoParams->GetScalingTechnique() == FLEXIBLEAUTOEXT) errorMsg = "The level value should be less than or equal to (multiplicativeDepth + 1)."; else errorMsg = "The level value should be less than or equal to multiplicativeDepth."; errorMsg += " Currently: level is [" + std::to_string(level) + "] and multiplicativeDepth is [" + std::to_string(multiplicativeDepth) + "]"; OPENFHE_THROW(errorMsg); } } double scFact = 0; if (cryptoParams->GetScalingTechnique() == FLEXIBLEAUTOEXT && level == 0) { scFact = cryptoParams->GetScalingFactorRealBig(level); // In FLEXIBLEAUTOEXT mode at level 0, we don't use the noiseScaleDeg in our encoding function, // so we set it to 1 to make sure it has no effect on the encoding. noiseScaleDeg = 1; } else { scFact = cryptoParams->GetScalingFactorReal(level); } Plaintext p; if (params == nullptr) { std::shared_ptr> elemParamsPtr; if (level != 0) { ILDCRTParams elemParams = *(cryptoParams->GetElementParams()); for (uint32_t i = 0; i < level; i++) { elemParams.PopLastParam(); } elemParamsPtr = std::make_shared>(elemParams); } else { elemParamsPtr = cryptoParams->GetElementParams(); } // Check if plaintext has got enough slots for data (value) uint32_t ringDim = elemParamsPtr->GetRingDimension(); size_t valueSize = value.size(); if (valueSize > ringDim / 2) { OPENFHE_THROW("The size [" + std::to_string(valueSize) + "] of the vector with values should not be greater than ringDim/2 [" + std::to_string(ringDim / 2) + "] if the scheme is CKKS"); } // TODO (dsuponit): we should call a version of MakePlaintext instead of calling Plaintext() directly here p = Plaintext(std::make_shared(elemParamsPtr, this->GetEncodingParams(), value, noiseScaleDeg, level, scFact, slots, this->GetCKKSDataType())); } else { // Check if plaintext has got enough slots for data (value) uint32_t ringDim = params->GetRingDimension(); size_t valueSize = value.size(); if (valueSize > ringDim / 2) { OPENFHE_THROW("The size [" + std::to_string(valueSize) + "] of the vector with values should not be greater than ringDim/2 [" + std::to_string(ringDim / 2) + "] if the scheme is CKKS"); } // TODO (dsuponit): we should call a version of MakePlaintext instead of calling Plaintext() directly here p = Plaintext(std::make_shared(params, this->GetEncodingParams(), value, noiseScaleDeg, level, scFact, slots, this->GetCKKSDataType())); } p->Encode(); // In FLEXIBLEAUTOEXT mode, a fresh plaintext at level 0 always has noiseScaleDeg 2. if (cryptoParams->GetScalingTechnique() == FLEXIBLEAUTOEXT && level == 0) { p->SetNoiseScaleDeg(2); } return p; } /** * @brief Getter for composite degree of the current scheme crypto context. * @return integer value corresponding to composite degree */ uint32_t GetCompositeDegreeFromCtxt() const { const auto cryptoParams = std::dynamic_pointer_cast(m_params); if (!cryptoParams) OPENFHE_THROW("Invalid crypto parameters: expected CryptoParametersRNS"); return cryptoParams->GetCompositeDegree(); } #ifdef DEBUG_KEY PrivateKey m_privateKey; #endif public: #ifdef DEBUG_KEY /** * SetPrivateKey() stores the private key in the crypto context. * GetPrivateKey() gets the private key from the crypto context. * * Thees functions are only intended for debugging and should not be used in production systems. * Please define DEBUG_KEY in openfhe.h to enable them. * * If used, one can create a key pair and store the secret key in the crypto context like this: * * auto keys = cc->KeyGen(); * cc->SetPrivateKey(keys.secretKey); * * After that, anyone in the code can access the secret key by getting the crypto context and doing the following: * * auto sk = cc->GetPrivateKey(); * * The key can be used for decrypting any intermediate ciphertexts for debugging purposes. */ void SetPrivateKey(const PrivateKey privateKey) { OPENFHE_DIAGNOSTIC_ERR << "Warning - SetPrivateKey is only intended to be used for debugging " "purposes - not for production systems." << std::endl; m_privateKey = privateKey; } const PrivateKey& GetPrivateKey() const { return m_privateKey; } #endif /** * @brief Sets the identifier of the scheme this crypto context implements. * * @param schemeTag the scheme identifier */ void setSchemeId(SCHEME schemeTag) { m_schemeId = schemeTag; } /** * @brief Gets the identifier of the scheme this crypto context implements. * * @return the scheme identifier */ SCHEME getSchemeId() const { return m_schemeId; } /** * @brief Constructor from raw pointers to parameters and scheme * * @param params pointer to CryptoParameters * @param scheme pointer to Crypto Scheme object * @param schemeId scheme identifier */ // TODO (dsuponit): investigate if we really need 2 constructors for CryptoContextImpl as one of them take regular pointer // and the other one takes shared_ptr CryptoContextImpl(CryptoParametersBase* params = nullptr, SchemeBase* scheme = nullptr, SCHEME schemeId = SCHEME::INVALID_SCHEME) { m_params.reset(params); m_scheme.reset(scheme); m_keyGenLevel = 0; m_schemeId = schemeId; } /** * @brief Constructor from shared pointers to parameters and scheme * * @param params shared pointer to CryptoParameters * @param scheme sharedpointer to Crypto Scheme object * @param schemeId scheme identifier */ CryptoContextImpl(std::shared_ptr> params, std::shared_ptr> scheme, SCHEME schemeId = SCHEME::INVALID_SCHEME) { m_params = params; m_scheme = scheme; m_keyGenLevel = 0; m_schemeId = schemeId; } /** * @brief Copy constructor * @param other cryptocontext to copy from */ CryptoContextImpl(const CryptoContextImpl& other) { m_params = other.m_params; m_scheme = other.m_scheme; m_keyGenLevel = 0; m_schemeId = other.m_schemeId; } /** * @brief Assignment operator * @param rhs cryptocontext to assign values from * @return this */ CryptoContextImpl& operator=(const CryptoContextImpl& rhs) { m_params = rhs.m_params; m_scheme = rhs.m_scheme; m_keyGenLevel = rhs.m_keyGenLevel; m_schemeId = rhs.m_schemeId; return *this; } /** * @brief Checks the CryptoContextImpl object health. * @return true if params and scheme exists in the object */ operator bool() const { return m_params && m_scheme; } /** * @brief Equality comparison operator * * @param a cryptocontext object1 * @param b cryptocontext object2 * @return true if the implementations have identical params and scheme * @attention this is for internal use only */ friend bool operator==(const CryptoContextImpl& a, const CryptoContextImpl& b) { // Identical if the parameters and the schemes are identical... the exact // same object, OR the same type and the same values if (a.m_params.get() == b.m_params.get()) { return true; } else { if (typeid(*a.m_params.get()) != typeid(*b.m_params.get())) { return false; } if (*a.m_params.get() != *b.m_params.get()) return false; } if (a.m_scheme.get() == b.m_scheme.get()) { return true; } else { if (typeid(*a.m_scheme.get()) != typeid(*b.m_scheme.get())) { return false; } if (*a.m_scheme.get() != *b.m_scheme.get()) return false; } return true; } /** * @brief Inequality comparison operator * * @param a cryptocontext object1 * @param b cryptocontext object2 * @return true if the implementations do not have identical params and scheme * @attention this is for internal use only */ friend bool operator!=(const CryptoContextImpl& a, const CryptoContextImpl& b) { return !(a == b); } /** * @brief Clears various caches within the library */ static void ClearStaticMapsAndVectors(); /** * @brief Clear CKKS bootstrap precomputations cached by this context's scheme. */ void ClearBootstrapPrecom() { VerifyCKKSScheme(__func__); m_scheme->ClearBootstrapPrecom(); } /** * @brief Clear CKKS/FHEW scheme-switch precomputations cached by this context's scheme. */ void ClearSchemeSwitchPrecom() { VerifyCKKSScheme(__func__); m_scheme->ClearSchemeSwitchPrecom(); } /** * @brief Release all scheme-level caches (bootstrap + scheme-switch). Unlike the two calls * above this is a teardown helper used to sweep contexts of any scheme, so it is a * noexcept no-op when the caches cannot exist rather than an error. */ void ClearAllCKKSCaches() noexcept { if (m_scheme && isCKKS(m_schemeId)) { m_scheme->ClearBootstrapPrecom(); m_scheme->ClearSchemeSwitchPrecom(); } } /** * @brief Serializes either all EvalMult keys (if keyTag is empty) or the EvalMult keys for keyTag * * @param ser stream to serialize to * @param sertype type of serialization * @param keyTag secret key tag * @return true on success */ template static bool SerializeEvalMultKey(std::ostream& ser, const ST& sertype, const std::string& keyTag = "") { const auto& evalMultKeys = CryptoContextImpl::GetAllEvalMultKeys(); if (keyTag.length() == 0) { Serial::Serialize(evalMultKeys, ser, sertype); } else { const auto it = evalMultKeys.find(keyTag); if (it == evalMultKeys.end()) return false; // no such keyTag std::map>> omap{{it->first, it->second}}; Serial::Serialize(omap, ser, sertype); } return true; } /** * @brief Serializes all EvalMult keys associated with the given CryptoContext * * @param ser stream to serialize to * @param sertype type of serialization * @param cc the CryptoContext whose keys should be serialized * @return true on success */ template static bool SerializeEvalMultKey(std::ostream& ser, const ST& sertype, const CryptoContext cc) { std::map>> omap; for (const auto& [key, vec] : CryptoContextImpl::GetAllEvalMultKeys()) { if (vec[0]->GetCryptoContext() == cc) { omap[key] = vec; } } if (omap.empty()) return false; Serial::Serialize(omap, ser, sertype); return true; } /** * @brief Deserializes EvalMult keys * * @param ser stream to deserialize from * @param sertype type of serialization * @return true on success * @attention Silently replaces any existing matching keys and creates a new CryptoContextImpl if necessary */ template static bool DeserializeEvalMultKey(std::istream& ser, const ST& sertype) { std::map>> omap; Serial::Deserialize(omap, ser, sertype); // The deserialize call creates all contexts that need to be created... // so, all we need to do is to insert the keys into the maps for their context(s) for (auto& [tag, vec] : omap) { CryptoContextImpl::InsertEvalMultKey(vec, tag); } return true; } /** * @brief Clears the entire EvalMultKey cache */ static void ClearEvalMultKeys(); /** * @brief Clears the EvalMultKey cache for the given keyTag or the entire EvalMultKey cache if keyTag is empty * @param keyTag secret key tag */ static void ClearEvalMultKeys(const std::string& keyTag); /** * @brief Clears EvalMultKey cache for the given context * @param cc the context to clear all EvalMultKey for */ static void ClearEvalMultKeys(const CryptoContext& cc); /** * @brief Adds the given vector of keys for the given keyTag to the map of all EvalMult keys * * @param evalKeyVec vector of keys * @param keyTag secret key tag * @attention Silently replaces any existing matching keys and if keyTag is empty, then the key tag is retrieved from evalKeyVec */ static void InsertEvalMultKey(const std::vector>& evalKeyVec, const std::string& keyTag = ""); /** * @brief Serializes either all EvalSum keys (if keyTag is empty) or the EvalSum keys for keyTag * * @param ser stream to serialize to * @param sertype type of serialization * @param keyTag secret key tag * @return true on success */ template static bool SerializeEvalSumKey(std::ostream& ser, const ST& sertype, const std::string& keyTag = "") { return CryptoContextImpl::SerializeEvalAutomorphismKey(ser, sertype, keyTag); } /** * @brief Serializes all EvalSum keys associated with the given CryptoContext * * @param ser stream to serialize to * @param sertype type of serialization * @param cc the CryptoContext whose keys should be serialized * @return true on success */ template static bool SerializeEvalSumKey(std::ostream& ser, const ST& sertype, const CryptoContext cc) { return CryptoContextImpl::SerializeEvalAutomorphismKey(ser, sertype, cc); } /** * @brief Deserializes EvalSum keys * * @param ser stream to deserialize from * @param sertype type of serialization * @return true on success * @attention Silently replaces any existing matching keys and creates a new CryptoContextImpl if necessary */ template static bool DeserializeEvalSumKey(std::istream& ser, const ST& sertype) { return CryptoContextImpl::DeserializeEvalAutomorphismKey(ser, sertype); } /** * @brief Clears the entire EvalSumKey cache */ static void ClearEvalSumKeys(); /** * @brief Clears the EvalSumKey cache for the given keyTag or the entire EvalSumKey cache if keyTag is empty * @param keyTag secret key tag */ static void ClearEvalSumKeys(const std::string& keyTag); /** * @brief Clears EvalSumKey cache for the given context * @param cc the context to clear all EvalSumKey for */ static void ClearEvalSumKeys(const CryptoContext cc); /** * @brief Adds the given map of keys for the given keyTag to the map of all EvalSum keys * * @param mapToInsert map of keys * @param keyTag secret key tag * @attention Silently replaces any existing matching keys and if keyTag is empty, then the key tag is retrieved from mapToInsert */ static void InsertEvalSumKey(const std::shared_ptr>> mapToInsert, std::string keyTag = "") { CryptoContextImpl::InsertEvalAutomorphismKey(mapToInsert, keyTag); } /** * @brief Serializes either all EvalAutomorphism keys (if keyTag is empty) or the EvalAutomorphism keys for keyTag * * @param ser stream to serialize to * @param sertype type of serialization * @param keyTag secret key tag * @return true on success */ template static bool SerializeEvalAutomorphismKey(std::ostream& ser, const ST& sertype, const std::string& keyTag = "") { // TODO (dsuponit): do we need Serailize/Deserialized to return bool? std::map>>>* smap; std::map>>> omap; if (keyTag.length() == 0) { smap = &CryptoContextImpl::GetAllEvalAutomorphismKeys(); } else { const auto keys = CryptoContextImpl::GetEvalAutomorphismKeyMapPtr(keyTag); omap[keyTag] = keys; smap = &omap; } Serial::Serialize(*smap, ser, sertype); return true; } /** * @brief Serializes all EvalAutomorphism keys associated with the given CryptoContext * * @param ser stream to serialize to * @param sertype type of serialization * @param cc the CryptoContext whose keys should be serialized * @return true on success */ template static bool SerializeEvalAutomorphismKey(std::ostream& ser, const ST& sertype, const CryptoContext cc) { std::map>>> omap; for (const auto& k : CryptoContextImpl::GetAllEvalAutomorphismKeys()) { if (k.second->begin()->second->GetCryptoContext() == cc) { omap[k.first] = k.second; } } if (omap.empty()) return false; Serial::Serialize(omap, ser, sertype); return true; } /** * @brief Serializes EvalAutomorphism keys for an array of specific indices associated with the given keyTag * * @param ser stream to serialize to * @param sertype type of serialization * @param keyTag secret key tag * @param indexList array of specific indices to serialize keys for * @return true on success */ template static bool SerializeEvalAutomorphismKey(std::ostream& ser, const ST& sertype, const std::string& keyTag, const std::vector& indexList) { std::map>>> keyMap = { {keyTag, CryptoContextImpl::GetPartialEvalAutomorphismKeyMapPtr(keyTag, indexList)}}; Serial::Serialize(keyMap, ser, sertype); return true; } /** * @brief Deserializes EvalAutomorphism keys for an array of specific indices associated with the given keyTag * * @param ser stream to deserialize from * @param sertype type of serialization * @param keyTag secret key tag * @param indexList array of specific indices to deserialize keys for * @return true on success */ template static bool DeserializeEvalAutomorphismKey(std::istream& ser, const ST& sertype, const std::string& keyTag, const std::vector& indexList) { if (indexList.empty()) OPENFHE_THROW("indexList may not be empty"); if (keyTag.empty()) OPENFHE_THROW("keyTag may not be empty"); std::map>>> allDeserKeys; Serial::Deserialize(allDeserKeys, ser, sertype); const auto& keyMapIt = allDeserKeys.find(keyTag); if (keyMapIt == allDeserKeys.end()) { OPENFHE_THROW("Deserialized automorphism keys are not generated for ID [" + keyTag + "]."); } // create a new map with evalkeys for the specified indices std::map> newMap; for (const uint32_t indx : indexList) { const auto& key = keyMapIt->second->find(indx); if (key == keyMapIt->second->end()) { OPENFHE_THROW("No automorphism key generated for index [" + std::to_string(indx) + "] within keyTag [" + keyTag + "]."); } newMap[indx] = key->second; } CryptoContextImpl::InsertEvalAutomorphismKey( std::make_shared>>(newMap), keyTag); return true; } /** * @brief Serializes bootstrap EvalAutomorphism keys associated with the given key tag * * @param ser stream to serialize to * @param sertype type of serialization * @param cc crypto context * @param keyTag secret key tag * @param slots number of slots for which the bootstrapping is performed * @return true on success */ template static bool SerializeEvalBootstrapKey(std::ostream& ser, const ST& sertype, const CryptoContext& cc, const std::string& keyTag, uint32_t slots) { const auto indexList = cc->GetScheme()->EvalBootstrapKeyMapIndices(cc, slots); std::map>>> keyMap = { {keyTag, CryptoContextImpl::GetPartialEvalAutomorphismKeyMapPtr(keyTag, indexList)}}; Serial::Serialize(keyMap, ser, sertype); return true; } /** * @brief Deserializes bootstrap EvalAutomorphism keys for an array of specific indices associated with the given keyTag * * @param ser stream to deserialize from * @param sertype type of serialization * @param keyTag secret key tag * @param indexList array of specific indices to deserialize keys for * @return true on success */ template static bool DeserializeEvalBootstrapKey(std::istream& ser, const ST& sertype, const std::string& keyTag, const std::vector& indexList) { return CryptoContextImpl::DeserializeEvalAutomorphismKey(ser, sertype, keyTag, indexList); } /** * @brief Deserializes bootstrap EvalAutomorphism keys derived from the given crypto context, key tag, and slots count * * @param ser stream to deserialize from * @param sertype type of serialization * @param cc crypto context * @param keyTag secret key tag * @param slots number of slots for which the bootstrapping was performed * @return true on success */ template static bool DeserializeEvalBootstrapKey(std::istream& ser, const ST& sertype, const CryptoContext& cc, const std::string& keyTag, uint32_t slots) { const auto indexList = cc->GetScheme()->EvalBootstrapKeyMapIndices(cc, slots); return CryptoContextImpl::DeserializeEvalAutomorphismKey(ser, sertype, keyTag, indexList); } /** * @brief Deserializes EvalAutomorphism keys * * @param ser stream to deserialize from * @param sertype type of serialization * @return true on success * @attention Silently replaces any existing matching keys and creates a new CryptoContextImpl if necessary */ template static bool DeserializeEvalAutomorphismKey(std::istream& ser, const ST& sertype) { std::map>>> keyMap; Serial::Deserialize(keyMap, ser, sertype); // The deserialize call created any contexts that needed to be created.... // so all we need to do is put the keys into the maps for their context for (auto& k : keyMap) { CryptoContextImpl::InsertEvalAutomorphismKey(k.second, k.first); } return true; } /** * @brief Clears the entire EvalAutomorphismKey cache */ static void ClearEvalAutomorphismKeys(); /** * @brief Clears the EvalAutomorphismKey cache for the given keyTag or the entire EvalAutomorphismKey cache if keyTag is empty * @param keyTag secret key tag */ static void ClearEvalAutomorphismKeys(const std::string& keyTag); /** * @brief Clears EvalAutomorphismKey cache for the given context * @param cc the context to clear all EvalAutomorphismKeys for */ static void ClearEvalAutomorphismKeys(const CryptoContext cc); /** * @brief Adds the given map of keys for the given keyTag to the map of all EvalAutomorphism keys * * @param mapToInsert map of keys * @param keyTag secret key tag * @attention Silently replaces any existing matching keys and if keyTag is empty, then the key tag is retrieved from mapToInsert */ // TODO (dsuponit): move InsertEvalAutomorphismKey() to the private section of the class static void InsertEvalAutomorphismKey(const std::shared_ptr>> mapToInsert, const std::string& keyTag = ""); //------------------------------------------------------------------------------ // TURN FEATURES ON //------------------------------------------------------------------------------ /** * @brief Enable a particular feature for use with this CryptoContextImpl * @param feature the feature that should be enabled */ void Enable(PKESchemeFeature feature) { m_scheme->Enable(feature); } /** * @brief Enable several features at once * @param featureMask bitwise value of several PKESchemeFeatures */ void Enable(uint32_t featureMask) { m_scheme->Enable(featureMask); } // GETTERS /** * @brief Getter for Scheme * @return Scheme object */ const std::shared_ptr> GetScheme() const { return m_scheme; } /** * @brief Getter for CryptoParams * @return CryptoParams */ const std::shared_ptr> GetCryptoParameters() const { return m_params; } /** * @brief Getter for the level at which evaluation keys should be generated * @return level * @attention For future use */ size_t GetKeyGenLevel() const { return m_keyGenLevel; } /** * @brief Setter for the level at which evaluation keys should be generated * @param level the level at which evaluation keys should be generated * @attention For future use */ void SetKeyGenLevel(size_t level) { m_keyGenLevel = level; } /** * @brief Getter for element params * @return ElementParams */ const std::shared_ptr GetElementParams() const { return m_params->GetElementParams(); } /** * @brief Getter for encoding params * @return EncodingParams */ const EncodingParams GetEncodingParams() const { return m_params->GetEncodingParams(); } /** * @brief Getter for cyclotomic order * @return CyclotomicOrder */ uint32_t GetCyclotomicOrder() const { return m_params->GetElementParams()->GetCyclotomicOrder(); } /** * @brief Getter for ring dimension * @return RingDimension */ uint32_t GetRingDimension() const { return m_params->GetElementParams()->GetRingDimension(); } /** * @brief Getter for ciphertext modulus * @return modulus */ const IntType& GetModulus() const { return m_params->GetElementParams()->GetModulus(); } /** * @brief Getter for root of unity * @return RootOfUnity */ const IntType& GetRootOfUnity() const { return m_params->GetElementParams()->GetRootOfUnity(); } /** * @brief Getter for the CKKS data type * @return data type of the CKKS data */ CKKSDataType GetCKKSDataType() const { const auto cryptoParams = std::dynamic_pointer_cast(m_params); if (!cryptoParams) OPENFHE_THROW("Invalid crypto parameters: expected CryptoParametersRNS"); return cryptoParams->GetCKKSDataType(); } //------------------------------------------------------------------------------ // KEYS GETTERS //------------------------------------------------------------------------------ /** * @brief Gets a map of all relinearization/evaluation multiplication keys * @return std::map where the map key/data pair is "keyTag"/"EvalMultKeys vector" */ static std::map>>& GetAllEvalMultKeys(); /** * @brief Gets a vector of relinearization/evaluation multiplication keys for the given keyTag * @param keyTag secret key tag * @return vector of EvalMultKeys */ static const std::vector>& GetEvalMultKeyVector(const std::string& keyTag); /** * @brief Gets a map of all EvalAutomorphism keys * @return std::map where the map key/data pair is "keyTag"/"shared_ptr to EvalMultKey map" */ static std::map>>>& GetAllEvalAutomorphismKeys(); /** * @brief Gets a map of EvalAutomorphism keys for the given keyTag * @param keyTag secret key tag * @return shared_ptr to EvalAutomorphismKey map */ static std::shared_ptr>> GetEvalAutomorphismKeyMapPtr( const std::string& keyTag); /** * @brief Gets a map of EvalAutomorphism keys for the given keyTag * @param keyTag secret key tag * @return EvalAutomorphismKey map */ static std::map>& GetEvalAutomorphismKeyMap(const std::string& keyTag) { return *(CryptoContextImpl::GetEvalAutomorphismKeyMapPtr(keyTag)); } /** * @brief Gets a map of all summation keys * @return std::map where the map key/data pair is "keyTag"/"shared_ptr to EvalSumKey map" */ static std::map>>>& GetAllEvalSumKeys(); /** * @brief Gets a map of EvalSum keys for the given keyTag * @param keyTag secret key tag * @return EvalSumKey map */ static const std::map>& GetEvalSumKeyMap(const std::string& keyTag); //------------------------------------------------------------------------------ // PLAINTEXT FACTORY METHODS //------------------------------------------------------------------------------ // TODO to be deprecated in 2.0 /** * @brief Creates a plaintext from a string using string encoding. * * @param str Input string to encode. * @return Encoded plaintext. */ Plaintext MakeStringPlaintext(const std::string& str) const { return PlaintextFactory::MakePlaintext(str, STRING_ENCODING, this->GetElementParams(), this->GetEncodingParams()); } /** * @brief Encodes a vector of integers into a coefficient-packed plaintext. * * @param value Input vector to encode. * @param noiseScaleDeg Degree of the scaling factor to encode the plaintext at. * @param level Encryption level for the input vector. * @return Encoded plaintext. */ Plaintext MakeCoefPackedPlaintext(const std::vector& value, size_t noiseScaleDeg = 1, uint32_t level = 0) const { if (value.empty()) OPENFHE_THROW("Cannot encode an empty value vector"); return MakePlaintext(COEF_PACKED_ENCODING, value, noiseScaleDeg, level); } /** * @brief Encodes a vector of integers into a packed plaintext. * * @param value Input vector to encode. * @param noiseScaleDeg Degree of the scaling factor to encode the plaintext at. * @param level Encryption level for the input vector. * @return Encoded plaintext. */ Plaintext MakePackedPlaintext(const std::vector& value, size_t noiseScaleDeg = 1, uint32_t level = 0) const { if (value.empty()) OPENFHE_THROW("Cannot encode an empty value vector"); return MakePlaintext(PACKED_ENCODING, value, noiseScaleDeg, level); } /** * @brief Encodes a vector of complex numbers into a CKKS packed plaintext. * * @param value Input vector to encode. * @param noiseScaleDeg Degree of the scaling factor to encode the plaintext at. * @param level Encryption level for the input vector. * @param params Encoding parameters. * @param slots Number of slots to use. * @return Encoded CKKS plaintext. */ Plaintext MakeCKKSPackedPlaintext(const std::vector>& value, size_t noiseScaleDeg = 1, uint32_t level = 0, const std::shared_ptr params = nullptr, uint32_t slots = 0) const { VerifyCKKSScheme(__func__); if (value.empty()) OPENFHE_THROW("Cannot encode an empty value vector"); return MakeCKKSPackedPlaintextInternal(value, noiseScaleDeg, level, params, slots); } /** * @brief Encodes a vector of real numbers into a CKKS packed plaintext. * * @param value Input vector to encode. * @param noiseScaleDeg Degree of the scaling factor to encode the plaintext at. * @param level Encryption level for the input vector. * @param params Encoding parameters. * @param slots Number of slots to use. * @return Encoded CKKS plaintext. */ Plaintext MakeCKKSPackedPlaintext(const std::vector& value, size_t noiseScaleDeg = 1, uint32_t level = 0, const std::shared_ptr params = nullptr, uint32_t slots = 0) const { VerifyCKKSScheme(__func__); if (value.empty()) OPENFHE_THROW("Cannot encode an empty value vector"); std::vector> complexValue(value.size()); std::transform(value.begin(), value.end(), complexValue.begin(), [](double da) { return std::complex(da); }); return MakeCKKSPackedPlaintextInternal(complexValue, noiseScaleDeg, level, params, slots); } /** * @brief Returns a plaintext object for decryption based on encoding type and parameters. * * @param pte Plaintext encoding type. * @param evp Element parameters. * @param ep Encoding parameters. * @param cdt CKKS data type. * @return Plaintext for decryption. */ static Plaintext GetPlaintextForDecrypt(PlaintextEncodings pte, std::shared_ptr evp, EncodingParams ep, CKKSDataType cdt = REAL); //------------------------------------------------------------------------------ // PKE Wrapper //------------------------------------------------------------------------------ /** * @brief Generates a standard public/secret key pair. * * @return Generated key pair. */ KeyPair KeyGen() const { return m_scheme->KeyGen(GetContextForPointer(this), false); } /** * @brief Generates a sparse key pair (with special structure and without full entropy) for special use cases like ring reduction. * * @return Generated key pair. * @attention Not supported by any crypto scheme currently. */ KeyPair SparseKeyGen() const { return m_scheme->KeyGen(GetContextForPointer(this), true); } /** * @brief Encrypts a plaintext using the given public key. * * @param plaintext Plaintext to encrypt. * @param publicKey Public key to use for encryption. * @return Encrypted ciphertext (or null on failure). */ Ciphertext Encrypt(ConstPlaintext& plaintext, const PublicKey& publicKey) const { if (plaintext == nullptr) OPENFHE_THROW("Input plaintext is nullptr"); ValidateKey(publicKey); Ciphertext ciphertext = m_scheme->Encrypt(plaintext->GetElement(), publicKey); if (ciphertext) { ciphertext->SetSlots(plaintext->GetSlots()); ciphertext->SetLevel(plaintext->GetLevel()); ciphertext->SetNoiseScaleDeg(plaintext->GetNoiseScaleDeg()); ciphertext->SetScalingFactor(plaintext->GetScalingFactor()); ciphertext->SetScalingFactorInt(plaintext->GetScalingFactorInt()); ciphertext->SetEncodingType(plaintext->GetEncodingType()); } return ciphertext; } /** * @brief Encrypts a plaintext using the given public key. * * @param publicKey Public key to use for encryption. * @param plaintext Plaintext to encrypt. * @return Encrypted ciphertext (or null on failure). */ Ciphertext Encrypt(const PublicKey& publicKey, ConstPlaintext& plaintext) const { return Encrypt(plaintext, publicKey); } /** * @brief Encrypts a plaintext using the given private key. * * @param plaintext Plaintext to encrypt. * @param privateKey Private key to use for encryption. * @return Encrypted ciphertext (or null on failure). */ Ciphertext Encrypt(ConstPlaintext& plaintext, const PrivateKey& privateKey) const { // if (plaintext == nullptr) // OPENFHE_THROW( "Input plaintext is nullptr"); ValidateKey(privateKey); Ciphertext ciphertext = m_scheme->Encrypt(plaintext->GetElement(), privateKey); if (ciphertext) { ciphertext->SetSlots(plaintext->GetSlots()); ciphertext->SetLevel(plaintext->GetLevel()); ciphertext->SetNoiseScaleDeg(plaintext->GetNoiseScaleDeg()); ciphertext->SetScalingFactor(plaintext->GetScalingFactor()); ciphertext->SetScalingFactorInt(plaintext->GetScalingFactorInt()); ciphertext->SetEncodingType(plaintext->GetEncodingType()); } return ciphertext; } /** * @brief Encrypts a plaintext using the given private key. * * @param privateKey Private key to use for encryption. * @param plaintext Plaintext to encrypt. * @return Encrypted ciphertext (or null on failure). */ Ciphertext Encrypt(const PrivateKey& privateKey, ConstPlaintext& plaintext) const { return Encrypt(plaintext, privateKey); } /** * @brief Decrypts a ciphertext using the given private key. * * @param ciphertext Ciphertext to decrypt. * @param privateKey Private key for decryption. * @param plaintext Output pointer for the resulting plaintext. * @return Decryption result status. */ DecryptResult Decrypt(ConstCiphertext& ciphertext, const PrivateKey& privateKey, Plaintext* plaintext); /** * @brief Decrypts a ciphertext using the given private key. * * @param privateKey Private key for decryption. * @param ciphertext Ciphertext to decrypt. * @param plaintext Output pointer for the resulting plaintext. * @return Decryption result status. */ inline DecryptResult Decrypt(const PrivateKey& privateKey, ConstCiphertext& ciphertext, Plaintext* plaintext) { return Decrypt(ciphertext, privateKey, plaintext); } //------------------------------------------------------------------------------ // KeySwitch Wrapper //------------------------------------------------------------------------------ /** * @brief Generates a key switching key from one secret key to another. * * @param oldPrivateKey Original secret key. * @param newPrivateKey Target secret key. * @return New evaluation key for key switching. */ EvalKey KeySwitchGen(const PrivateKey& oldPrivateKey, const PrivateKey& newPrivateKey) const { ValidateKey(oldPrivateKey); ValidateKey(newPrivateKey); return m_scheme->KeySwitchGen(oldPrivateKey, newPrivateKey); } /** * @brief Applies key switching to a ciphertext using the given evaluation key. * * @param ciphertext Input ciphertext. * @param evalKey Evaluation key for key switching. * @return Ciphertext after key switching. */ Ciphertext KeySwitch(ConstCiphertext& ciphertext, const EvalKey& evalKey) const { ValidateCiphertext(ciphertext); ValidateKey(evalKey); return m_scheme->KeySwitch(ciphertext, evalKey); } /** * @brief Applies key switching in place on the given ciphertext. * * @param ciphertext Ciphertext to modify. * @param evalKey Evaluation key for key switching. */ void KeySwitchInPlace(Ciphertext& ciphertext, const EvalKey& evalKey) const { ValidateCiphertext(ciphertext); ValidateKey(evalKey); m_scheme->KeySwitchInPlace(ciphertext, evalKey); } //------------------------------------------------------------------------------ // SHE NEGATION Wrapper //------------------------------------------------------------------------------ /** * @brief Negates a ciphertext. * * @param ciphertext Input ciphertext. * @return Negated ciphertext. */ Ciphertext EvalNegate(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); return m_scheme->EvalNegate(ciphertext); } /** * @brief Performs in-place negation of a ciphertext. * * @param ciphertext Ciphertext to negate. */ void EvalNegateInPlace(Ciphertext& ciphertext) const { ValidateCiphertext(ciphertext); m_scheme->EvalNegateInPlace(ciphertext); } //------------------------------------------------------------------------------ // SHE ADDITION Wrapper //------------------------------------------------------------------------------ /** * @brief Homomorphic addition of two ciphertexts. * * @param ciphertext1 First addend. * @param ciphertext2 Second addend. * @return Resulting ciphertext. */ Ciphertext EvalAdd(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); return m_scheme->EvalAdd(ciphertext1, ciphertext2); } /** * @brief In-place homomorphic addition of two ciphertexts. * * @param ciphertext1 First addend (modified in place). * @param ciphertext2 Second addend. */ void EvalAddInPlace(Ciphertext& ciphertext1, ConstCiphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); m_scheme->EvalAddInPlace(ciphertext1, ciphertext2); } /** * @brief In-place element-wise addition of two ciphertexts without any type, level or scaling factor checks; * the ciphertexts must already have the same number of elements, level and noise scale degree. * * @param ctxt1 First addend (modified in place). * @param ctxt2 Second addend. */ void EvalAddInPlaceNoCheck(Ciphertext& ctxt1, ConstCiphertext& ctxt2) const { auto& cv1 = ctxt1->GetElements(); auto& cv2 = ctxt2->GetElements(); uint32_t n = cv1.size(); for (uint32_t i = 0; i < n; ++i) cv1[i] += cv2[i]; } /** * @brief Homomorphic addition of two mutable ciphertexts. * * @param ciphertext1 First addend (may be modified). * @param ciphertext2 Second addend (may be modified). * @return Resulting ciphertext. */ Ciphertext EvalAddMutable(Ciphertext& ciphertext1, Ciphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); return m_scheme->EvalAddMutable(ciphertext1, ciphertext2); } /** * @brief In-place homomorphic addition of two mutable ciphertexts. * * @param ciphertext1 First addend (modified in place). * @param ciphertext2 Second addend (may be modified). */ void EvalAddMutableInPlace(Ciphertext& ciphertext1, Ciphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); m_scheme->EvalAddMutableInPlace(ciphertext1, ciphertext2); } /** * @brief Homomorphic addition of a ciphertext and a plaintext. * * @param ciphertext Input ciphertext. * @param plaintext Input plaintext. * @return Resulting ciphertext. */ Ciphertext EvalAdd(ConstCiphertext& ciphertext, Plaintext& plaintext) const { TypeCheck(ciphertext, plaintext); plaintext->SetFormat(EVALUATION); return m_scheme->EvalAdd(ciphertext, plaintext); } /** * @brief Homomorphic addition of a plaintext and a ciphertext. * * @param plaintext Input plaintext. * @param ciphertext Input ciphertext. * @return Resulting ciphertext. */ inline Ciphertext EvalAdd(Plaintext& plaintext, ConstCiphertext& ciphertext) const { return EvalAdd(ciphertext, plaintext); } /** * @brief In-place addition of a ciphertext and a plaintext. * * @param ciphertext Ciphertext to modify. * @param plaintext Plaintext to add. */ void EvalAddInPlace(Ciphertext& ciphertext, Plaintext& plaintext) const { TypeCheck(ciphertext, plaintext); plaintext->SetFormat(EVALUATION); m_scheme->EvalAddInPlace(ciphertext, plaintext); } /** * @brief In-place addition of a plaintext and a ciphertext. * * @param plaintext Plaintext to add. * @param ciphertext Ciphertext to modify. */ void EvalAddInPlace(Plaintext& plaintext, Ciphertext& ciphertext) const { EvalAddInPlace(ciphertext, plaintext); } /** * @brief Homomorphic addition of a mutable ciphertext and a plaintext. * * @param ciphertext Input ciphertext (may be modified). * @param plaintext Input plaintext. * @return Resulting ciphertext. */ Ciphertext EvalAddMutable(Ciphertext& ciphertext, Plaintext& plaintext) const { TypeCheck(ciphertext, plaintext); plaintext->SetFormat(EVALUATION); return m_scheme->EvalAddMutable(ciphertext, plaintext); } /** * @brief Homomorphic addition of a plaintext and a mutable ciphertext. * * @param plaintext Input plaintext. * @param ciphertext Input ciphertext (may be modified). * @return Resulting ciphertext. */ Ciphertext EvalAddMutable(Plaintext& plaintext, Ciphertext& ciphertext) const { return EvalAddMutable(ciphertext, plaintext); } /** * @brief Homomorphic addition of a ciphertext and a real number (CKKS only). * * @param ciphertext Input ciphertext. * @param scalar Real number to add. * @return Resulting ciphertext. */ Ciphertext EvalAdd(ConstCiphertext& ciphertext, double scalar) const { return scalar >= 0. ? m_scheme->EvalAdd(ciphertext, scalar) : m_scheme->EvalSub(ciphertext, -scalar); } /** * @brief Homomorphic addition of a real number and a ciphertext (CKKS only). * * @param scalar Real number to add. * @param ciphertext Input ciphertext. * @return Resulting ciphertext. */ Ciphertext EvalAdd(double scalar, ConstCiphertext& ciphertext) const { return EvalAdd(ciphertext, scalar); } /** * @brief In-place addition of a ciphertext and a real number (CKKS only). * * @param ciphertext Ciphertext to modify. * @param scalar Real number to add. */ void EvalAddInPlace(Ciphertext& ciphertext, double scalar) const { if (scalar == 0.) return; if (scalar > 0.) m_scheme->EvalAddInPlace(ciphertext, scalar); else m_scheme->EvalSubInPlace(ciphertext, -scalar); } /** * @brief In-place addition of a real number and a ciphertext (CKKS only). * * @param scalar Real number to add. * @param ciphertext Ciphertext to modify. */ void EvalAddInPlace(double scalar, Ciphertext& ciphertext) const { EvalAddInPlace(ciphertext, scalar); } /** * @brief Homomorphic addition of a ciphertext and a complex number (CKKS only). * * @param ciphertext Input ciphertext. * @param scalar Complex number to add. * @return Resulting ciphertext. */ Ciphertext EvalAdd(ConstCiphertext& ciphertext, std::complex scalar) const { return m_scheme->EvalAdd(ciphertext, scalar); } /** * @brief Homomorphic addition of a complex number and a ciphertext (CKKS only). * * @param scalar Complex number to add. * @param ciphertext Input ciphertext. * @return Resulting ciphertext. */ Ciphertext EvalAdd(std::complex scalar, ConstCiphertext& ciphertext) const { return EvalAdd(ciphertext, scalar); } /** * @brief In-place addition of a ciphertext and a complex number (CKKS only). * * @param ciphertext Ciphertext to modify. * @param scalar Complex number to add. */ void EvalAddInPlace(Ciphertext& ciphertext, std::complex scalar) const { if (scalar == std::complex(0.0, 0.0)) return; m_scheme->EvalAddInPlace(ciphertext, scalar); } /** * @brief In-place addition of a complex number and a ciphertext (CKKS only). * * @param scalar Complex number to add. * @param ciphertext Ciphertext to modify. */ void EvalAddInPlace(std::complex scalar, Ciphertext& ciphertext) const { EvalAddInPlace(ciphertext, scalar); } //------------------------------------------------------------------------------ // SHE SUBTRACTION Wrapper //------------------------------------------------------------------------------ /** * @brief Homomorphic subtraction of two ciphertexts. * * @param ciphertext1 Minuend. * @param ciphertext2 Subtrahend. * @return Resulting ciphertext. */ Ciphertext EvalSub(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); return m_scheme->EvalSub(ciphertext1, ciphertext2); } /** * @brief In-place homomorphic subtraction of two ciphertexts. * * @param ciphertext1 Minuend (modified in place). * @param ciphertext2 Subtrahend. */ void EvalSubInPlace(Ciphertext& ciphertext1, ConstCiphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); m_scheme->EvalSubInPlace(ciphertext1, ciphertext2); } /** * @brief Homomorphic subtraction of two mutable ciphertexts. * * @param ciphertext1 Minuend (may be modified). * @param ciphertext2 Subtrahend (may be modified). * @return Resulting ciphertext. */ Ciphertext EvalSubMutable(Ciphertext& ciphertext1, Ciphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); return m_scheme->EvalSubMutable(ciphertext1, ciphertext2); } /** * @brief In-place homomorphic subtraction of two mutable ciphertexts. * * @param ciphertext1 Minuend (modified in place). * @param ciphertext2 Subtrahend (may be modified). */ void EvalSubMutableInPlace(Ciphertext& ciphertext1, Ciphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); m_scheme->EvalSubMutableInPlace(ciphertext1, ciphertext2); } /** * @brief Homomorphic subtraction of a ciphertext and a plaintext. * * @param ciphertext Minuend. * @param plaintext Subtrahend. * @return Resulting ciphertext. */ Ciphertext EvalSub(ConstCiphertext& ciphertext, Plaintext& plaintext) const { TypeCheck(ciphertext, plaintext); return m_scheme->EvalSub(ciphertext, plaintext); } /** * @brief Homomorphic subtraction of a ciphertext from a plaintext. * * @param plaintext Minuend. * @param ciphertext Subtrahend. * @return Resulting ciphertext. */ Ciphertext EvalSub(Plaintext& plaintext, ConstCiphertext& ciphertext) const { return EvalAdd(EvalNegate(ciphertext), plaintext); } /** * @brief Homomorphic subtraction of a plaintext from a mutable ciphertext. * * @param ciphertext Minuend (may be modified). * @param plaintext Subtrahend. * @return Resulting ciphertext. */ Ciphertext EvalSubMutable(Ciphertext& ciphertext, Plaintext& plaintext) const { TypeCheck(ciphertext, plaintext); return m_scheme->EvalSubMutable(ciphertext, plaintext); } /** * @brief Homomorphic subtraction of a mutable ciphertext from a plaintext. * * @param plaintext Minuend. * @param ciphertext Subtrahend (may be modified). * @return Resulting ciphertext. */ Ciphertext EvalSubMutable(Plaintext& plaintext, Ciphertext& ciphertext) const { Ciphertext negated = EvalNegate(ciphertext); Ciphertext result = EvalAddMutable(negated, plaintext); ciphertext = EvalNegate(negated); return result; } /** * @brief In-place subtraction of a plaintext from a ciphertext. * * @param ciphertext Minuend (modified in place). * @param plaintext Subtrahend. */ void EvalSubInPlace(Ciphertext& ciphertext, ConstPlaintext& plaintext) const { TypeCheck(ciphertext, plaintext); m_scheme->EvalSubInPlace(ciphertext, plaintext); } /** * @brief In-place subtraction of a ciphertext from a plaintext. * * @param plaintext Minuend (may be modified). * @param ciphertext Subtrahend (modified in place to hold the result). */ void EvalSubInPlace(Plaintext& plaintext, Ciphertext& ciphertext) const { EvalNegateInPlace(ciphertext); EvalAddInPlace(ciphertext, plaintext); } /** * @brief Homomorphic subtraction of a real number from a ciphertext (CKKS only). * * @param ciphertext Input ciphertext. * @param scalar Real number to subtract. * @return Resulting ciphertext (ciphertext - scalar). */ Ciphertext EvalSub(ConstCiphertext& ciphertext, double scalar) const { return scalar >= 0 ? m_scheme->EvalSub(ciphertext, scalar) : m_scheme->EvalAdd(ciphertext, -scalar); } /** * @brief Homomorphic subtraction of a ciphertext from a real number (CKKS only). * * @param scalar Real number. * @param ciphertext Ciphertext to subtract. * @return Resulting ciphertext (scalar - ciphertext). */ Ciphertext EvalSub(double scalar, ConstCiphertext& ciphertext) const { return EvalAdd(EvalNegate(ciphertext), scalar); } /** * @brief In-place subtraction of a real number from a ciphertext (CKKS only). * * @param ciphertext Ciphertext to modify. * @param scalar Real number to subtract. */ void EvalSubInPlace(Ciphertext& ciphertext, double scalar) const { if (scalar == 0.) return; if (scalar > 0.) m_scheme->EvalSubInPlace(ciphertext, scalar); else m_scheme->EvalAddInPlace(ciphertext, -scalar); } /** * @brief In-place subtraction of a ciphertext from a real number (CKKS only). * * @param scalar Real number. * @param ciphertext Ciphertext to modify. */ void EvalSubInPlace(double scalar, Ciphertext& ciphertext) const { EvalNegateInPlace(ciphertext); EvalAddInPlace(ciphertext, scalar); } /** * @brief Homomorphic subtraction of a complex number from a ciphertext (CKKS only). * * @param ciphertext Input ciphertext. * @param scalar Complex number to subtract. * @return Resulting ciphertext (ciphertext - scalar). */ Ciphertext EvalSub(ConstCiphertext& ciphertext, std::complex scalar) const { return m_scheme->EvalAdd(ciphertext, -scalar); } /** * @brief Homomorphic subtraction of a ciphertext from a complex number (CKKS only). * * @param scalar Complex number. * @param ciphertext Ciphertext to subtract. * @return Resulting ciphertext (scalar - ciphertext). */ Ciphertext EvalSub(std::complex scalar, ConstCiphertext& ciphertext) const { return EvalAdd(EvalNegate(ciphertext), scalar); } /** * @brief In-place subtraction of a complex number from a ciphertext (CKKS only). * * @param ciphertext Ciphertext to modify. * @param scalar Complex number to subtract. */ void EvalSubInPlace(Ciphertext& ciphertext, std::complex scalar) const { if (scalar == std::complex(0.0, 0.0)) return; m_scheme->EvalAddInPlace(ciphertext, -scalar); } /** * @brief In-place subtraction of a ciphertext from a complex number (CKKS only). * * @param scalar Complex number. * @param ciphertext Ciphertext to modify. */ void EvalSubInPlace(std::complex scalar, Ciphertext& ciphertext) const { EvalNegateInPlace(ciphertext); EvalAddInPlace(ciphertext, scalar); } //------------------------------------------------------------------------------ // SHE MULTIPLICATION Wrapper //------------------------------------------------------------------------------ /** * @brief Creates a relinearization key (for s^2) that can be used with the OpenFHE EvalMult operator * * @param key secret key * @note the new evaluation key is stored in cryptocontext */ void EvalMultKeyGen(const PrivateKey& key); /** * @brief Creates a vector evalmult keys that can be used with the OpenFHE EvalMult operator * @param key secret key * * @note 1st key (for s^2) is used for multiplication of ciphertexts of depth 1, * 2nd key (for s^3) is used for multiplication of ciphertexts of depth 2, etc. * A vector of new evaluation keys is stored in cryptocontext */ void EvalMultKeysGen(const PrivateKey& key); /** * @brief Homomorphic multiplication of two ciphertexts using a relinearization key. * * @param ciphertext1 Multiplier. * @param ciphertext2 Multiplicand. * @return Resulting ciphertext (ciphertext1 * ciphertext2). */ Ciphertext EvalMult(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext1->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalMult"); return m_scheme->EvalMult(ciphertext1, ciphertext2, evalKeyVec[0]); } /** * @brief Homomorphic multiplication of two mutable ciphertexts using a relinearization key. * * @param ciphertext1 Multiplier (may be modified). * @param ciphertext2 Multiplicand (may be modified). * @return Resulting ciphertext (ciphertext1 * ciphertext2). */ Ciphertext EvalMultMutable(Ciphertext& ciphertext1, Ciphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext1->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalMultMutable"); return m_scheme->EvalMultMutable(ciphertext1, ciphertext2, evalKeyVec[0]); } /** * @brief In-place homomorphic multiplication of two mutable ciphertexts using a relinearization key. * * @param ciphertext1 Multiplier (modified in place). * @param ciphertext2 Multiplicand (may be modified). */ void EvalMultMutableInPlace(Ciphertext& ciphertext1, Ciphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext1->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalMultMutableInPlace"); m_scheme->EvalMultMutableInPlace(ciphertext1, ciphertext2, evalKeyVec[0]); } /** * @brief Homomorphic squaring of a ciphertext using a relinearization key. * * @param ciphertext Input ciphertext. * @return Squared ciphertext. */ Ciphertext EvalSquare(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalSquare"); return m_scheme->EvalSquare(ciphertext, evalKeyVec[0]); } /** * @brief Homomorphic squaring of a mutable ciphertext using a relinearization key. * * @param ciphertext Input ciphertext (may be modified). * @return Squared ciphertext. */ Ciphertext EvalSquareMutable(Ciphertext& ciphertext) const { ValidateCiphertext(ciphertext); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalSquareMutable"); return m_scheme->EvalSquareMutable(ciphertext, evalKeyVec[0]); } /** * @brief In-place homomorphic squaring of a ciphertext using a relinearization key. * * @param ciphertext Ciphertext to square (modified in place). */ void EvalSquareInPlace(Ciphertext& ciphertext) const { ValidateCiphertext(ciphertext); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalSquareInPlace"); m_scheme->EvalSquareInPlace(ciphertext, evalKeyVec[0]); } /** * @brief Homomorphic multiplication of two ciphertexts without relinearization. * * @param ciphertext1 Multiplier. * @param ciphertext2 Multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMultNoRelin(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { TypeCheck(ciphertext1, ciphertext2); return m_scheme->EvalMult(ciphertext1, ciphertext2); } /** * @brief Homomorphic multiplication of two ciphertexts without relinearization and without any type, level * or scaling factor checks: the tensor product of the ciphertext elements is computed directly, so the * ciphertexts must already have the same level and scaling factor. The result carries the summed noise scale * degree and the product of the scaling factors. * * @param ctxt1 Multiplier. * @param ctxt2 Multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMultNoRelinNoCheck(ConstCiphertext& ctxt1, ConstCiphertext& ctxt2) const { auto& cv1 = ctxt1->GetElements(); auto& cv2 = ctxt2->GetElements(); uint32_t n1 = cv1.size(); uint32_t n2 = cv2.size(); uint32_t nr = n1 + n2 - 1; std::vector cvr; cvr.reserve(nr); if (n1 == 2 && n2 == 2) { cvr.emplace_back(cv1[0] * cv2[0]); cvr.emplace_back((cv1[0] * cv2[1]) += (cv1[1] * cv2[0])); cvr.emplace_back(cv1[1] * cv2[1]); } else { uint32_t m = 0; for (uint32_t i = 0; i < n1; ++i) { auto& cv1i = cv1[i]; for (uint32_t j = 0, k = i; j < n2; ++j, ++k) { if (k == m) { cvr.emplace_back(cv1i * cv2[j]); ++m; } else { cvr[k] += (cv1i * cv2[j]); } } } } auto result = ctxt1->CloneEmpty(); result->SetElements(std::move(cvr)); result->SetNoiseScaleDeg(ctxt1->GetNoiseScaleDeg() + ctxt2->GetNoiseScaleDeg()); result->SetScalingFactor(ctxt1->GetScalingFactor() * ctxt2->GetScalingFactor()); result->SetScalingFactorInt(ctxt1->GetScalingFactorInt().ModMul( ctxt2->GetScalingFactorInt(), ctxt1->GetCryptoParameters()->GetPlaintextModulus())); return result; } /** * @brief Relinearizes a ciphertext to reduce it to two components (2 polynomials per ciphertext). * * @param ciphertext Input ciphertext. * @return Relinearized ciphertext. */ Ciphertext Relinearize(ConstCiphertext& ciphertext) const { // input parameter check if (!ciphertext) OPENFHE_THROW("Input ciphertext is nullptr"); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext->GetKeyTag()); if (evalKeyVec.size() < (ciphertext->NumberCiphertextElements() - 2)) OPENFHE_THROW("Insufficient value was used for maxRelinSkDeg to generate keys for Relinearize"); return m_scheme->Relinearize(ciphertext, evalKeyVec); } /** * @brief In-place relinearization of a ciphertext to reduce it to two components (2 polynomials per ciphertext). * * @param ciphertext Ciphertext to relinearize (modified in place). */ void RelinearizeInPlace(Ciphertext& ciphertext) const { // input parameter check if (!ciphertext) OPENFHE_THROW("Input ciphertext is nullptr"); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext->GetKeyTag()); if (evalKeyVec.size() < (ciphertext->NumberCiphertextElements() - 2)) OPENFHE_THROW("Insufficient value was used for maxRelinSkDeg to generate keys for RelinearizeInPlace"); m_scheme->RelinearizeInPlace(ciphertext, evalKeyVec); } /** * @brief Homomorphic multiplication of two ciphertexts followed by relinearization to the lowest level. * * @param ciphertext1 First input ciphertext. * @param ciphertext2 Second input ciphertext. * @return Resulting ciphertext. */ Ciphertext EvalMultAndRelinearize(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { if (!ciphertext1 || !ciphertext2) OPENFHE_THROW("Input ciphertext is nullptr"); const auto& evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext1->GetKeyTag()); if (evalKeyVec.size() < (ciphertext1->NumberCiphertextElements() + ciphertext2->NumberCiphertextElements() - 3)) { OPENFHE_THROW("Insufficient value was used for maxRelinSkDeg to generate keys for EvalMultAndRelinearize"); } return m_scheme->EvalMultAndRelinearize(ciphertext1, ciphertext2, evalKeyVec); } /** * @brief Multiplies all ciphertext elements by an unscaled integer without any checks; the level, noise scale * degree and scaling factor of the ciphertext are unchanged. * * @param ctxt Input ciphertext. * @param k Integer to multiply by. * @return Resulting ciphertext. */ Ciphertext EvalMultNoCheck(ConstCiphertext& ctxt, NativeInteger k) const { auto result = ctxt->Clone(); auto& cv = result->GetElements(); uint32_t n = cv.size(); for (uint32_t i = 0; i < n; ++i) cv[i] *= k; return result; } /** * @brief Homomorphic multiplication of a ciphertext by a plaintext. * * @param ciphertext Multiplier. * @param plaintext Multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMult(ConstCiphertext& ciphertext, ConstPlaintext& plaintext) const { TypeCheck(ciphertext, plaintext); return m_scheme->EvalMult(ciphertext, plaintext); } /** * @brief Homomorphic multiplication of a plaintext by a ciphertext. * * @param plaintext Multiplier. * @param ciphertext Multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMult(ConstPlaintext& plaintext, ConstCiphertext& ciphertext) const { return EvalMult(ciphertext, plaintext); } /** * @brief Homomorphic multiplication of a mutable ciphertext and a plaintext. * * @param ciphertext Multiplier (may be modified). * @param plaintext Multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMultMutable(Ciphertext& ciphertext, Plaintext& plaintext) const { TypeCheck(ciphertext, plaintext); return m_scheme->EvalMultMutable(ciphertext, plaintext); } /** * @brief Homomorphic multiplication of a plaintext and a mutable ciphertext. * * @param plaintext Multiplier. * @param ciphertext Multiplicand (may be modified). * @return Resulting ciphertext. */ Ciphertext EvalMultMutable(Plaintext& plaintext, Ciphertext& ciphertext) const { return EvalMultMutable(ciphertext, plaintext); } /** * @brief Homomorphic multiplication of a ciphertext by a real number (CKKS only). * * @param ciphertext Multiplier. * @param scalar Real number multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMult(ConstCiphertext& ciphertext, double scalar) const { if (!ciphertext) OPENFHE_THROW("Input ciphertext is nullptr"); return m_scheme->EvalMult(ciphertext, scalar); } /** * @brief Homomorphic multiplication of a ciphertext by a real number (CKKS only). * * @param scalar Real number multiplier. * @param ciphertext Multiplicand. * @return Resulting ciphertext. */ inline Ciphertext EvalMult(double scalar, ConstCiphertext& ciphertext) const { return EvalMult(ciphertext, scalar); } /** * @brief In-place multiplication of a ciphertext by a real number (CKKS only). * * @param ciphertext Ciphertext to modify. * @param scalar Real number multiplicand. */ void EvalMultInPlace(Ciphertext& ciphertext, double scalar) const { if (!ciphertext) OPENFHE_THROW("Input ciphertext is nullptr"); m_scheme->EvalMultInPlace(ciphertext, scalar); } /** * @brief In-place multiplication of a ciphertext by a real number (CKKS only). * * @param scalar Real number multiplier. * @param ciphertext Ciphertext to modify (multiplicand). */ inline void EvalMultInPlace(double scalar, Ciphertext& ciphertext) const { EvalMultInPlace(ciphertext, scalar); } /** * @brief Homomorphic multiplication of a ciphertext by a complex number (CKKS only). * * @param ciphertext Multiplier. * @param scalar Complex number multiplicand. * @return Resulting ciphertext. */ Ciphertext EvalMult(ConstCiphertext& ciphertext, std::complex scalar) const { if (!ciphertext) OPENFHE_THROW("Input ciphertext is nullptr"); return m_scheme->EvalMult(ciphertext, scalar); } /** * @brief Homomorphic multiplication of a ciphertext by a complex number (CKKS only). * * @param scalar Complex number multiplier. * @param ciphertext Multiplicand. * @return Resulting ciphertext. */ inline Ciphertext EvalMult(std::complex scalar, ConstCiphertext& ciphertext) const { return EvalMult(ciphertext, scalar); } /** * @brief In-place multiplication of a ciphertext by a complex number (CKKS only). * * @param ciphertext Ciphertext to modify. * @param scalar Complex number multiplicand. */ void EvalMultInPlace(Ciphertext& ciphertext, std::complex scalar) const { if (!ciphertext) OPENFHE_THROW("Input ciphertext is nullptr"); m_scheme->EvalMultInPlace(ciphertext, scalar); } /** * @brief In-place multiplication of a ciphertext by a complex number (CKKS only). * * @param scalar Complex number multiplier. * @param ciphertext Ciphertext to modify (multiplicand). */ inline void EvalMultInPlace(std::complex scalar, Ciphertext& ciphertext) const { EvalMultInPlace(ciphertext, scalar); } //------------------------------------------------------------------------------ // SHE AUTOMORPHISM Wrapper //------------------------------------------------------------------------------ /** * @brief Generates automorphism evaluation keys for the given private key. * * @param privateKey Private key to use for key generation. * @param indexList List of automorphism indices to be computed. * @return Map of generated evaluation keys. */ std::shared_ptr>> EvalAutomorphismKeyGen( const PrivateKey privateKey, const std::vector& indexList) const { ValidateKey(privateKey); if (indexList.empty()) OPENFHE_THROW("Input index vector is empty"); auto evalKeys = m_scheme->EvalAutomorphismKeyGen(privateKey, indexList); CryptoContextImpl::InsertEvalAutomorphismKey(evalKeys, privateKey->GetKeyTag()); return evalKeys; } /** * @brief Applies an automorphism to a ciphertext using the given evaluation keys. * * @param ciphertext Input ciphertext. * @param i Automorphism index. * @param evalKeyMap Map of evaluation keys generated by EvalAutomorphismKeyGen. * @return Transformed ciphertext. */ Ciphertext EvalAutomorphism(ConstCiphertext& ciphertext, uint32_t i, const std::map>& evalKeyMap, CALLER_INFO_ARGS_HDR) const { ValidateCiphertext(ciphertext); if (evalKeyMap.empty()) OPENFHE_THROW(std::string("Empty input key map") + CALLER_INFO); auto key = evalKeyMap.find(i); if (key == evalKeyMap.end()) OPENFHE_THROW(std::string("Could not find an EvalKey for index ") + std::to_string(i) + CALLER_INFO); auto evalKey = key->second; ValidateKey(evalKey); return m_scheme->EvalAutomorphism(ciphertext, i, evalKeyMap); } /** * @brief Computes the automorphism index for a given vector index. * * @param idx Vector index. * @return Corresponding automorphism index. */ uint32_t FindAutomorphismIndex(const uint32_t idx) const { const auto cryptoParams = m_params; const auto elementParams = cryptoParams->GetElementParams(); uint32_t m = elementParams->GetCyclotomicOrder(); return m_scheme->FindAutomorphismIndex(idx, m); } /** * @brief Computes automorphism indices for a list of vector indices. * * @param idxList List of vector indices. * @return Vector of corresponding automorphism indices. */ std::vector FindAutomorphismIndices(const std::vector& idxList) const { std::vector newIndices; newIndices.reserve(idxList.size()); for (const auto idx : idxList) { newIndices.emplace_back(FindAutomorphismIndex(idx)); } return newIndices; } /** * @brief Rotates a ciphertext by the given index using a stored rotation key. * * @param ciphertext Input ciphertext. * @param index Rotation index (positive for left, negative for right). * @return Rotated ciphertext. */ Ciphertext EvalRotate(ConstCiphertext& ciphertext, int32_t index) const { ValidateCiphertext(ciphertext); auto evalKeyMap = CryptoContextImpl::GetEvalAutomorphismKeyMap(ciphertext->GetKeyTag()); return m_scheme->EvalAtIndex(ciphertext, index, evalKeyMap); } /** * @brief implements the precomputation step of hoisted automorphisms. * * Please refer to Section 5 of Halevi and Shoup, "Faster Homomorphic linear transformations in HELib." * for more details, link: https://eprint.iacr.org/2018/244. * * Generally, automorphisms are performed with three steps: * (1) the automorphism is applied on the ciphertext * (2) the automorphed values are decomposed into digits * (3) key switching is applied to make it possible to further compute on the ciphertext. * * Hoisted automorphisms is a technique that performs the digit decomposition for the original ciphertext first, * and then performs the automorphism and the key switching on the decomposed digits. The benefit of this is that the * digit decomposition is independent of the automorphism rotation index, so it can be reused for * multiple different indices. This can greatly improve performance when we have to compute many automorphisms * on the same ciphertext. This routinely happens when we do permutations (EvalPermute). * * it implements the digit decomposition step of hoisted automorphisms. * * @param ciphertext Input ciphertext on which to do the precomputation (digit decomposition). * @return Pointer to precomputed rotation data. */ std::shared_ptr> EvalFastRotationPrecompute(ConstCiphertext& ciphertext) const { return m_scheme->EvalFastRotationPrecompute(ciphertext); } /** * @brief Implements the automorphism and key switching step of hoisted automorphisms. * * Please refer to Section 5 of Halevi and Shoup, "Faster Homomorphic linear transformations in HELib." * for more details, link: https://eprint.iacr.org/2018/244. * * Generally, automorphisms are performed with three steps: * (1) the automorphism is applied on the ciphertext * (2) the automorphed values are decomposed into digits * (3) key switching is applied to make it possible to further compute on the ciphertext. * * Hoisted automorphisms is a technique that performs the digit decomposition for the original ciphertext first, * and then performs the automorphism and the key switching on the decomposed digits. The benefit of this is that the * digit decomposition is independent of the automorphism rotation index, so it can be reused for * multiple different indices. This can greatly improve performance when we have to compute many automorphisms * on the same ciphertext. This routinely happens when we do permutations (EvalPermute). * * This method assumes that all required rotation keys exist. This may not be true if we are * using baby-step/giant-step key switching. Please refer to Section 5.1 of the above reference and * EvalPermuteBGStepHoisted to see how to deal with this issue. * * @param ciphertext Input ciphertext. * @param index Rotation index (positive for left, negative for right). * @param m Cyclotomic order. * @param digits Precomputed rotation data (the digit decomposition created by EvalFastRotationPrecompute). * @return Rotated ciphertext. */ Ciphertext EvalFastRotation(ConstCiphertext& ciphertext, const uint32_t index, const uint32_t m, const std::shared_ptr> digits) const { return m_scheme->EvalFastRotation(ciphertext, index, m, digits); } /** * @brief Implements the automorphism and key switching step of hoisted automorphisms. * * Please refer to Section 5 of Halevi and Shoup, "Faster Homomorphic linear transformations in HELib." * for more details, link: https://eprint.iacr.org/2018/244. * * Generally, automorphisms are performed with three steps: * (1) the automorphism is applied on the ciphertext * (2) the automorphed values are decomposed into digits * (3) key switching is applied to make it possible to further compute on the ciphertext. * * Hoisted automorphisms is a technique that performs the digit decomposition for the original ciphertext first, * and then performs the automorphism and the key switching on the decomposed digits. The benefit of this is that the * digit decomposition is independent of the automorphism rotation index, so it can be reused for * multiple different indices. This can greatly improve performance when we have to compute many automorphisms * on the same ciphertext. This routinely happens when we do permutations (EvalPermute). * * This method assumes that all required rotation keys exist. This may not be true if we are * using baby-step/giant-step key switching. Please refer to Section 5.1 of the above reference and * EvalPermuteBGStepHoisted to see how to deal with this issue. * * The cyclotomic order is computed from the CryptoContext. * * @param ciphertext Input ciphertext. * @param index Rotation index (positive for left, negative for right). * @param digits Precomputed rotation data (the digit decomposition created by EvalFastRotationPrecompute). * @return Rotated ciphertext. */ Ciphertext EvalFastRotation(ConstCiphertext& ciphertext, const uint32_t index, const std::shared_ptr> digits) const { return EvalFastRotation(ciphertext, index, GetRingDimension() * 2, digits); } /** * @brief Performs fast (hoisted) rotation in the extended CRT basis P*Q. Only supported with hybrid key switching. * * @param ciphertext Input ciphertext. * @param index Rotation index (positive for left, negative for right). * @param digits Precomputed digits for the ciphertext. * @param addFirst If true, the first element c0 is also computed. * @return Rotated ciphertext in extended basis. */ Ciphertext EvalFastRotationExt(ConstCiphertext& ciphertext, uint32_t index, const std::shared_ptr> digits, bool addFirst) const { auto evalKeyMap = CryptoContextImpl::GetEvalAutomorphismKeyMap(ciphertext->GetKeyTag()); return m_scheme->EvalFastRotationExt(ciphertext, index, digits, addFirst, evalKeyMap); } /** * @brief Scales a ciphertext down from the extended CRT basis P*Q to Q. Only supported with hybrid key switching. * * @param ciphertext Input ciphertext in extended basis. * @return Scaled ciphertext in basis Q. */ Ciphertext KeySwitchDown(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); return m_scheme->KeySwitchDown(ciphertext); } /** * @brief Scales down the first polynomial c0 from extended CRT basis P*Q to Q. Only supported with hybrid key switching. * * @param ciphertext Input ciphertext in extended basis. * @return Scaled polynomial c0 in basis Q. */ Element KeySwitchDownFirstElement(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); return m_scheme->KeySwitchDownFirstElement(ciphertext); } /** * @brief Extends a ciphertext from basis Q to the extended CRT basis P*Q. Only supported with hybrid key switching. * * @param ciphertext Input ciphertext in basis Q. * @param addFirst If true, includes the first component c0 in the output. * @return Extended ciphertext in basis P*Q. */ Ciphertext KeySwitchExt(ConstCiphertext& ciphertext, bool addFirst) const { ValidateCiphertext(ciphertext); return m_scheme->KeySwitchExt(ciphertext, addFirst); } /** * @brief Generates evaluation keys for a list of rotation indices. * * @param privateKey Private key used for key generation. * @param indexList List of rotation indices. */ void EvalAtIndexKeyGen(const PrivateKey privateKey, const std::vector& indexList); /** * @brief Generates rotation evaluation keys for a list of indices. Internally calls EvalAtIndexKeyGen. * * @param privateKey Private key used for key generation. * @param indexList List of rotation indices. */ void EvalRotateKeyGen(const PrivateKey privateKey, const std::vector& indexList) { EvalAtIndexKeyGen(privateKey, indexList); }; /** * @brief Rotates a ciphertext by the given index using stored rotation keys. * Positive index = left shift; negative index = right shift. * * @param ciphertext Input ciphertext. * @param index Rotation index. * @return Rotated ciphertext. */ Ciphertext EvalAtIndex(ConstCiphertext& ciphertext, int32_t index) const; //------------------------------------------------------------------------------ // SHE Leveled Methods Wrapper //------------------------------------------------------------------------------ /** * @brief Performs multiplication, relinearization, and rescaling in one step. * Uses a relinearization key from the crypto context. * * @param ciphertext1 First ciphertext. * @param ciphertext2 Second ciphertext. * @return Resulting ciphertext. */ Ciphertext ComposedEvalMult(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { ValidateCiphertext(ciphertext1); ValidateCiphertext(ciphertext2); auto evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertext1->GetKeyTag()); if (evalKeyVec.empty()) OPENFHE_THROW("Evaluation key has not been generated for EvalMult"); return m_scheme->ComposedEvalMult(ciphertext1, ciphertext2, evalKeyVec[0]); } /** * @brief Rescales a ciphertext by reducing its modulus (alias for ModReduce in CKKS). * * @param ciphertext Input ciphertext. * @return Rescaled ciphertext. */ Ciphertext Rescale(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); return m_scheme->ModReduce(ciphertext, GetCompositeDegreeFromCtxt()); } /** * @brief In-place rescaling of a ciphertext (alias for ModReduceInPlace in CKKS). * * @param ciphertext Ciphertext to rescale. */ void RescaleInPlace(Ciphertext& ciphertext) const { ValidateCiphertext(ciphertext); m_scheme->ModReduceInPlace(ciphertext, GetCompositeDegreeFromCtxt()); } /** * @brief Performs modulus reduction on a ciphertext (used in BGV/CKKS). * * @param ciphertext Input ciphertext. * @return Modulus-reduced ciphertext. */ Ciphertext ModReduce(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); return m_scheme->ModReduce(ciphertext, GetCompositeDegreeFromCtxt()); } /** * @brief In-place modulus reduction of a ciphertext (used in BGV/CKKS). * * @param ciphertext Ciphertext to reduce. */ void ModReduceInPlace(Ciphertext& ciphertext) const { ValidateCiphertext(ciphertext); m_scheme->ModReduceInPlace(ciphertext, GetCompositeDegreeFromCtxt()); } /** * @brief Reduces the number of RNS limbs (levels) in a ciphertext. * * @param ciphertext Input ciphertext. * @param evalKey Evaluation key (currently unused; kept for API compatibility). * @param levels Number of levels to drop. * @return Ciphertext with reduced levels. * * @note Supported in BGV and CKKS. In CKKS with COMPOSITESCALING*, levels are scaled by the composite degree. */ Ciphertext LevelReduce(ConstCiphertext& ciphertext, const EvalKey evalKey, size_t levels = 1) const { ValidateCiphertext(ciphertext); return m_scheme->LevelReduce(ciphertext, evalKey, levels * GetCompositeDegreeFromCtxt()); } /** * @brief In-place reduction of RNS limbs (levels) in a ciphertext. * * @param ciphertext Ciphertext to modify. * @param evalKey Evaluation key (currently unused; kept for API compatibility). * @param levels Number of levels to drop. * * @note Supported in BGV and CKKS. In CKKS with COMPOSITESCALING*, levels are scaled by the composite degree. */ void LevelReduceInPlace(Ciphertext& ciphertext, const EvalKey evalKey, size_t levels = 1) const { ValidateCiphertext(ciphertext); if (levels > 0) m_scheme->LevelReduceInPlace(ciphertext, evalKey, levels * GetCompositeDegreeFromCtxt()); } /** * @brief Compresses a ciphertext by reducing its modulus to lower communication cost. * * @param ciphertext Input ciphertext. * @param towersLeft Number of RNS limbs to retain. * @param noiseScaleDeg Noise scale degree the ciphertext is reduced to before dropping limbs (must not exceed towersLeft). * @return Compressed ciphertext. */ Ciphertext Compress(ConstCiphertext& ciphertext, uint32_t towersLeft = 1, size_t noiseScaleDeg = 1) const { if (ciphertext == nullptr) OPENFHE_THROW("input ciphertext is invalid (has no data)"); if (towersLeft < noiseScaleDeg) { std::string errorMsg{"Input towersLeft["}; errorMsg += std::to_string(towersLeft) + "] may not be less than noiseScaleDeg["; errorMsg += std::to_string(noiseScaleDeg) + "]"; OPENFHE_THROW(errorMsg); } return m_scheme->Compress(ciphertext, towersLeft, noiseScaleDeg); } //------------------------------------------------------------------------------ // Advanced SHE Wrapper //------------------------------------------------------------------------------ /** * @brief Homomorphic addition of multiple ciphertexts (null entries are skipped). * * @param ciphertextVec Vector of ciphertexts. * @return Resulting ciphertext; never aliases an input, so it is always safe to mutate. */ Ciphertext EvalAddMany(const std::vector>& ciphertextVec) const { if (ciphertextVec.empty()) OPENFHE_THROW("Empty input ciphertext vector"); return m_scheme->EvalAddMany(ciphertextVec); } /** * @brief In-place homomorphic addition of multiple ciphertexts (null entries are skipped; the result is also left in slot 0 of the vector). * * @param ciphertextVec Vector of ciphertexts (modified in place to store intermediate results). * @return Resulting ciphertext. */ Ciphertext EvalAddManyInPlace(std::vector>& ciphertextVec) const { if (ciphertextVec.empty()) OPENFHE_THROW("Empty input ciphertext vector"); return m_scheme->EvalAddManyInPlace(ciphertextVec); } /** * @brief Homomorphic multiplication of multiple ciphertexts using a binary tree approach, * followed by relinearization to reduce ciphertext size to two elements after each multiplication. * * @param ciphertextVec Vector of ciphertexts to multiply. * @return Resulting ciphertext. * * @note Assumes each multiplication produces a ciphertext within the supported ring size * (for the secret key degree used by EvalMultKeysGen). * Otherwise, it throws an error */ Ciphertext EvalMultMany(const std::vector>& ciphertextVec) const { if (ciphertextVec.empty()) OPENFHE_THROW("Empty input ciphertext vector"); if (ciphertextVec.size() == 1) return ciphertextVec[0]->Clone(); const auto evalKeyVec = CryptoContextImpl::GetEvalMultKeyVector(ciphertextVec[0]->GetKeyTag()); if (evalKeyVec.size() < (ciphertextVec[0]->NumberCiphertextElements() - 2)) OPENFHE_THROW("Insufficient value was used for maxRelinSkDeg to generate keys"); return m_scheme->EvalMultMany(ciphertextVec, evalKeyVec); } //------------------------------------------------------------------------------ // Advanced SHE LINEAR WEIGHTED SUM //------------------------------------------------------------------------------ /** * @brief Computes a linear weighted sum of ciphertexts (CKKS only). * * @param ciphertextVec List of ciphertexts. * @param constantVec Corresponding weights. * @return Weighted sum as a ciphertext. */ template Ciphertext EvalLinearWSum(std::vector>& ciphertextVec, const std::vector& constantVec) const { return m_scheme->EvalLinearWSum(ciphertextVec, constantVec); } /** * @brief Computes a linear weighted sum of ciphertexts (CKKS only). * * @param constantsVec Corresponding weights. * @param ciphertextVec List of ciphertexts. * @return Weighted sum as a ciphertext. */ template Ciphertext EvalLinearWSum(const std::vector& constantsVec, std::vector>& ciphertextVec) const { return EvalLinearWSum(ciphertextVec, constantsVec); } /** * @brief Computes a linear weighted sum using mutable ciphertexts (CKKS only). * * @param ciphertextVec List of mutable ciphertexts. * @param constantsVec Corresponding weights. * @return Weighted sum as a ciphertext. */ template Ciphertext EvalLinearWSumMutable(std::vector>& ciphertextVec, const std::vector& constantsVec) const { return m_scheme->EvalLinearWSumMutable(ciphertextVec, constantsVec); } /** * @brief Computes a linear weighted sum using mutable ciphertexts (CKKS only). * * @param constantsVec Corresponding weights. * @param ciphertextVec List of mutable ciphertexts. * @return Weighted sum as a ciphertext. */ template Ciphertext EvalLinearWSumMutable(const std::vector& constantsVec, std::vector>& ciphertextVec) const { return EvalLinearWSumMutable(ciphertextVec, constantsVec); } //------------------------------------------------------------------------------ // Advanced SHE EVAL POLYNOMIAL //------------------------------------------------------------------------------ /** * @brief Computes the powers of a ciphertext to be used when evaluating a polynomial (CKKS only). * Uses EvalPowersLinear() for low polynomial degrees (degree < 5), or EvalPowersPS() for higher degrees. * * @param ciphertext Input ciphertext. * @param coefficients Polynomial coefficients (vector's size = (degree + 1)). * @return Resulting structure of powers. */ template std::shared_ptr> EvalPowers(ConstCiphertext& ciphertext, const std::vector& coefficients) const { ValidateCiphertext(ciphertext); return m_scheme->EvalPowers(ciphertext, coefficients); } /** * @brief Evaluates a polynomial (given as a power series) on a ciphertext (CKKS only). * Use EvalPolyLinear() for low polynomial degrees (degree < 5), or EvalPolyPS() for higher degrees. * * @param ciphertext Input ciphertext. * @param coefficients Polynomial coefficients (vector's size = (degree + 1)). * @return Resulting ciphertext. */ template Ciphertext EvalPoly(ConstCiphertext& ciphertext, const std::vector& coefficients) const { ValidateCiphertext(ciphertext); return m_scheme->EvalPoly(ciphertext, coefficients); } /** * @brief Evaluates a polynomial (given as a power series) from the powers of a ciphertext precomputed * by EvalPowers (CKKS only). The powers are left unmodified, so the same precomputation can be * reused for further polynomials of the same degree as the one it was computed for (below degree 5, * EvalPowers builds only the powers its coefficients need, so the nonzero terms must match as well). * * @param powers Powers of the input ciphertext, as returned by EvalPowers. * @param coefficients Polynomial coefficients (vector's size = (degree + 1)). * @return Resulting ciphertext. */ template Ciphertext EvalPolyWithPrecomp(std::shared_ptr> powers, const std::vector& coefficients) const { ValidateSeriesPowers(powers); return m_scheme->EvalPolyWithPrecomp(powers, coefficients); } /** * @brief Naive polynomial evaluation using a binary tree approach (efficient for low-degree polynomials, <10). * Polynomials are given as a power series. Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param coefficients Polynomial coefficients (vector's size = (degree + 1)). * @return Resulting ciphertext. */ template Ciphertext EvalPolyLinear(ConstCiphertext& ciphertext, const std::vector& coefficients) const { ValidateCiphertext(ciphertext); return m_scheme->EvalPolyLinear(ciphertext, coefficients); } /** * @brief Evaluates a polynomial (given as a power series) using the Paterson-Stockmeyer method (efficient for high-degree polynomials). * Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param coefficients Polynomial coefficients (vector's size = (degree + 1)). * @return Resulting ciphertext. */ template Ciphertext EvalPolyPS(ConstCiphertext& ciphertext, const std::vector& coefficients) const { ValidateCiphertext(ciphertext); return m_scheme->EvalPolyPS(ciphertext, coefficients); } //------------------------------------------------------------------------------ // Advanced SHE EVAL CHEBYSHEV SERIES //------------------------------------------------------------------------------ /** * @brief Computes the Chebyshev polynomials for a ciphertext to be used when evaluating a Chebyshev series (CKKS only). * Maps [a, b] to [-1, 1] using the linear transformation 1 + 2(x-a)/(b-a), then computes the polynomials * needed by EvalChebyshevSeriesLinear (degree < 5) or EvalChebyshevSeriesPS (higher degrees) depending on degree. * * @param ciphertext Input ciphertext. * @param coefficients Polynomial coefficients (vector's size = (degree + 1)). * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @return Resulting structure of Chebyshev polynomials. */ template std::shared_ptr> EvalChebyPolys(ConstCiphertext& ciphertext, const std::vector& coefficients, double a, double b) const { ValidateCiphertext(ciphertext); return m_scheme->EvalChebyPolys(ciphertext, coefficients, a, b); } /** * @brief Evaluates a Chebyshev interpolated polynomial on a ciphertext. * Uses a linear transformation to map [a, b] to [-1, 1] using linear transformation 1 + 2(x-a)/(b-a), * then applies either EvalChebyshevSeriesLinear (degree < 5) or EvalChebyshevSeriesPS depending on degree. * Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param coefficients Chebyshev series coefficients. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @return Resulting ciphertext. */ template Ciphertext EvalChebyshevSeries(ConstCiphertext& ciphertext, const std::vector& coefficients, double a, double b) const { ValidateCiphertext(ciphertext); return m_scheme->EvalChebyshevSeries(ciphertext, coefficients, a, b); } /** * @brief Evaluates a Chebyshev series from the Chebyshev polynomials of a ciphertext precomputed by * EvalChebyPolys (CKKS only). The polynomials are left unmodified, so the same precomputation * can be reused for further series of the same degree as the one it was computed for. * * @param polys Chebyshev polynomials of the input ciphertext, as returned by EvalChebyPolys. * @param coefficients Chebyshev series coefficients. * @return Resulting ciphertext. */ template Ciphertext EvalChebyshevSeriesWithPrecomp(std::shared_ptr> polys, const std::vector& coefficients) const { ValidateSeriesPowers(polys); return m_scheme->EvalChebyshevSeriesWithPrecomp(polys, coefficients); } /** * @brief Evaluates a Chebyshev interpolated polynomial using a naive linear method. * Maps [a, b] to [-1, 1] using linear transformation 1 + 2(x-a)/(b-a). Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param coefficients Chebyshev series coefficients. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @return Resulting ciphertext. */ template Ciphertext EvalChebyshevSeriesLinear(ConstCiphertext& ciphertext, const std::vector& coefficients, double a, double b) const { ValidateCiphertext(ciphertext); return m_scheme->EvalChebyshevSeriesLinear(ciphertext, coefficients, a, b); } /** * @brief Evaluates a Chebyshev interpolated polynomial using the Paterson-Stockmeyer method. * Maps [a, b] to [-1, 1] using linear transformation 1 + 2(x-a)/(b-a). Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param coefficients Chebyshev series coefficients. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @return Resulting ciphertext. */ template Ciphertext EvalChebyshevSeriesPS(ConstCiphertext& ciphertext, const std::vector& coefficients, double a, double b) const { ValidateCiphertext(ciphertext); return m_scheme->EvalChebyshevSeriesPS(ciphertext, coefficients, a, b); } /** * @brief Evaluates a smooth function on a ciphertext using Chebyshev polynomial approximation over [a, b]. * Supported only in CKKS. * * @param func Function to approximate. * @param ciphertext Input ciphertext. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @param degree Degree of the Chebyshev approximation. * @return Ciphertext after function evaluation. */ Ciphertext EvalChebyshevFunction(std::function func, ConstCiphertext& ciphertext, double a, double b, uint32_t degree) const; /** * @brief Evaluates an approximate sine function on a ciphertext using Chebyshev approximation. * Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @param degree Degree of the Chebyshev approximation. * @return Ciphertext after sine approximation. */ Ciphertext EvalSin(ConstCiphertext& ciphertext, double a, double b, uint32_t degree) const; /** * @brief Evaluates an approximate cosine function on a ciphertext using Chebyshev approximation. * Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @param degree Degree of the Chebyshev approximation. * @return Ciphertext after cosine approximation. */ Ciphertext EvalCos(ConstCiphertext& ciphertext, double a, double b, uint32_t degree) const; /** * @brief Evaluates an approximate logistic function 1 / (1 + exp(-x)) on a ciphertext using Chebyshev approximation. * Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @param degree Degree of the Chebyshev approximation. * @return Ciphertext after logistic approximation. */ Ciphertext EvalLogistic(ConstCiphertext& ciphertext, double a, double b, uint32_t degree) const; /** * @brief Evaluates an approximate reciprocal function 1 / x (for x ≥ 1) on a ciphertext using Chebyshev approximation. * Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param a Lower bound of argument for which the coefficients were found. * @param b Upper bound of argument for which the coefficients were found. * @param degree Degree of the Chebyshev approximation. * @return Ciphertext after reciprocal approximation. */ Ciphertext EvalDivide(ConstCiphertext& ciphertext, double a, double b, uint32_t degree) const; //------------------------------------------------------------------------------ // Advanced SHE EVAL SUM //------------------------------------------------------------------------------ /** * @brief Generates evaluation keys required for homomorphic summation (EvalSum). * * @param privateKey Private key used for key generation. */ void EvalSumKeyGen(const PrivateKey privateKey); /** * @brief Generates automorphism keys for EvalSumRows (only for packed encoding). * * @param privateKey Private key used for key generation. * @param rowSize Number of slots per row in the packed matrix. * @param subringDim Subring dimension (use cyclotomic order if 0). * @return Map of generated evaluation keys. */ std::shared_ptr>> EvalSumRowsKeyGen(const PrivateKey privateKey, uint32_t rowSize = 0, uint32_t subringDim = 0); /** * @brief Generates automorphism keys for EvalSumRows (only for packed encoding). Kept for backwards * compatibility; the public key is unused. * * @param privateKey Private key used for key generation. * @param publicKey Unused. * @param rowSize Number of slots per row in the packed matrix. * @param subringDim Subring dimension (use cyclotomic order if 0). * @return Map of generated evaluation keys. */ // TODO: this is here for backwards compatibility; should remove in v2.0 std::shared_ptr>> EvalSumRowsKeyGen(const PrivateKey privateKey, const PublicKey publicKey, uint32_t rowSize = 0, uint32_t subringDim = 0); /** * @brief Generates automorphism keys for EvalSumCols (only for packed encoding). * * @param privateKey Private key used for key generation. * @return Map of generated evaluation keys. */ std::shared_ptr>> EvalSumColsKeyGen(const PrivateKey privateKey); /** * @brief Computes the sum of all components in a packed ciphertext vector. * * @param ciphertext Input ciphertext. * @param batchSize Number of slots to sum over. * @return Resulting ciphertext containing the sum. */ Ciphertext EvalSum(ConstCiphertext& ciphertext, uint32_t batchSize) const; /** * @brief Sums all elements across each row in a packed-encoded matrix ciphertext. * * @param ciphertext Input ciphertext. * @param numRows Number of rows in the matrix. * @param evalSumKeyMap Map of evaluation keys generated for row summation. * @param subringDim Subring dimension (use full cyclotomic order if 0). * @return Ciphertext containing row-wise sums. */ Ciphertext EvalSumRows(ConstCiphertext& ciphertext, uint32_t numRows, const std::map>& evalSumKeyMap, uint32_t subringDim = 0) const; /** * @brief Sums all elements across each column in a packed-encoded matrix ciphertext. * * @param ciphertext Input ciphertext. * @param numCols Number of columns in the matrix. * @param evalSumKeyMap Map of evaluation keys generated for column summation. * @return Ciphertext containing column-wise sums. */ Ciphertext EvalSumCols(ConstCiphertext& ciphertext, uint32_t numCols, const std::map>& evalSumKeyMap) const; //------------------------------------------------------------------------------ // Advanced SHE EVAL INNER PRODUCT //------------------------------------------------------------------------------ /** * @brief Computes the inner product of two ciphertext vectors using packed encoding and EvalSum. * * @param ciphertext1 First input ciphertext vector. * @param ciphertext2 Second input ciphertext vector. * @param batchSize Number of slots to sum over. * @return Ciphertext containing the inner product. */ Ciphertext EvalInnerProduct(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2, uint32_t batchSize) const; /** * @brief Computes the inner product of a ciphertext and a plaintext using packed encoding and EvalSum. * * @param ciphertext Encrypted input vector. * @param plaintext Plaintext input vector. * @param batchSize Number of slots to sum over. * @return Ciphertext containing the inner product. */ Ciphertext EvalInnerProduct(ConstCiphertext& ciphertext, ConstPlaintext& plaintext, uint32_t batchSize) const; /** * @brief Merges multiple ciphertexts with values in slot 0 into a single packed ciphertext. * * @param ciphertextVec Vector of ciphertexts to merge. * @return Merged ciphertext with values placed into slots in order. * * @note Requires rotation keys for the necessary indices. */ Ciphertext EvalMerge(const std::vector>& ciphertextVec) const; //------------------------------------------------------------------------------ // PRE Wrapper //------------------------------------------------------------------------------ /** * @brief Generates a re-encryption key for Proxy Re-Encryption (PRE). * * @param oldPrivateKey Original private key. * @param newPublicKey Public key of the target recipient. * @return Re-encryption evaluation key. */ EvalKey ReKeyGen(const PrivateKey oldPrivateKey, const PublicKey newPublicKey) const { ValidateKey(oldPrivateKey); ValidateKey(newPublicKey); ValidatePREMode(); return m_scheme->ReKeyGen(oldPrivateKey, newPublicKey); } /** * @brief Produces an Eval Key that OpenFHE can use for Proxy Re-Encryption * * @param originalPrivateKey original secret key * @param newPrivateKey new secret key * @return new evaluation key * @attention This functionality has been completely removed from OpenFHE */ EvalKey ReKeyGen(const PrivateKey originalPrivateKey, const PrivateKey newPrivateKey) const __attribute__((deprecated("functionality removed from OpenFHE"))); /** * @brief Re-encrypts a ciphertext using a re-encryption key for Proxy Re-Encryption. * * @param ciphertext Input ciphertext. * @param evalKey Re-encryption key. * @param publicKey Optional public key of the recipient. * @return Re-encrypted ciphertext. */ Ciphertext ReEncrypt(ConstCiphertext& ciphertext, EvalKey evalKey, const PublicKey publicKey = nullptr) const { ValidateCiphertext(ciphertext); ValidateKey(evalKey); ValidatePREMode(); return m_scheme->ReEncrypt(ciphertext, evalKey, publicKey); } //------------------------------------------------------------------------------ // Multiparty Wrapper //------------------------------------------------------------------------------ /** * @brief Generates a joined public key from a set of secret key shares (Threshold FHE). * * @param privateKeyVec Vector of secret key shares. * @return Key pair containing this party's private key and the joined public key. * * @attention Only for debugging purposes. Not for production use. */ KeyPair MultipartyKeyGen(const std::vector>& privateKeyVec) { if (privateKeyVec.empty()) OPENFHE_THROW("Input private key vector is empty"); return m_scheme->MultipartyKeyGen(GetContextForPointer(this), privateKeyVec, false); } /** * @brief Generates a joined public key using a prior public key and the current party's secret share (Threshold FHE). * * @param publicKey joined public key from prior parties. * @param makeSparse Use ring reduction (no longer supported). * @param fresh Indicates if proxy re-encryption is used in the multi-party protocol or star topology is used. * @return Key pair containing this party's private key and the updated joined public key. */ KeyPair MultipartyKeyGen(const PublicKey publicKey, bool makeSparse = false, bool fresh = false) { if (!publicKey) OPENFHE_THROW("Input public key is empty"); return m_scheme->MultipartyKeyGen(GetContextForPointer(this), publicKey, makeSparse, fresh); } /** * @brief Performs partial decryption as the lead decryption party (Threshold FHE). * * @param ciphertextVec Vector of ciphertexts to decrypt. * @param privateKey Secret key share of the lead party. * @return Vector of partially decrypted ciphertexts. */ std::vector> MultipartyDecryptLead(const std::vector>& ciphertextVec, const PrivateKey privateKey) const { ValidateKey(privateKey); std::vector> newCiphertextVec; newCiphertextVec.reserve(ciphertextVec.size()); for (const auto& ciphertext : ciphertextVec) { ValidateCiphertext(ciphertext); newCiphertextVec.push_back(m_scheme->MultipartyDecryptLead(ciphertext, privateKey)); } return newCiphertextVec; } /** * @brief Performs partial decryption by non-lead parties in a Threshold FHE setting. * * @param ciphertextVec Vector of ciphertexts to decrypt. * @param privateKey Secret key share of a non-lead party. * @return Vector of partially decrypted ciphertexts. */ std::vector> MultipartyDecryptMain(const std::vector>& ciphertextVec, const PrivateKey privateKey) const { ValidateKey(privateKey); std::vector> newCiphertextVec; newCiphertextVec.reserve(ciphertextVec.size()); for (const auto& ciphertext : ciphertextVec) { ValidateCiphertext(ciphertext); newCiphertextVec.push_back(m_scheme->MultipartyDecryptMain(ciphertext, privateKey)); } return newCiphertextVec; } /** * @brief Combines partially decrypted ciphertexts into the final plaintext result (Threshold FHE). * * @param partialCiphertextVec Vector of partial decryptions. * @param plaintext Output plaintext. * @return Decoding result. */ DecryptResult MultipartyDecryptFusion(const std::vector>& partialCiphertextVec, Plaintext* plaintext) const { std::string datatype = demangle(typeid(Element).name()); OPENFHE_THROW("Not implemented for " + datatype); } /** * @brief Generates a new joined evaluation key from a prior key and secret key shares (Threshold FHE). * * @param originalPrivateKey Original private key. * @param newPrivateKey New private key. * @param evalKey Prior joined evaluation key. * @return New joined evaluation key. */ EvalKey MultiKeySwitchGen(const PrivateKey originalPrivateKey, const PrivateKey newPrivateKey, const EvalKey evalKey) const { if (!originalPrivateKey) OPENFHE_THROW("Input first private key is nullptr"); if (!newPrivateKey) OPENFHE_THROW("Input second private key is nullptr"); if (!evalKey) OPENFHE_THROW("Input evaluation key is nullptr"); return m_scheme->MultiKeySwitchGen(originalPrivateKey, newPrivateKey, evalKey); } /** * @brief Generates joined automorphism keys from the current secret share and prior keys (Threshold FHE). * * @param privateKey Secret key share. * @param evalKeyMap Prior joined automorphism keys. * @param indexList List of automorphism indices. * @param keyTag Secret key tag (optional). * @return Map of updated joined automorphism keys. */ std::shared_ptr>> MultiEvalAutomorphismKeyGen( const PrivateKey privateKey, const std::shared_ptr>> evalKeyMap, const std::vector& indexList, const std::string& keyTag = "") { if (!privateKey) OPENFHE_THROW("Input private key is nullptr"); if (!evalKeyMap) OPENFHE_THROW("Input evaluation key map is nullptr"); if (indexList.empty()) OPENFHE_THROW("Input index vector is empty"); return m_scheme->MultiEvalAutomorphismKeyGen(privateKey, evalKeyMap, indexList, keyTag); } /** * @brief Generates joined rotation keys from the current secret share and prior keys (Threshold FHE). * * @param privateKey Secret key share. * @param evalKeyMap Prior joined rotation keys. * @param indexList List of rotation indices. * @param keyTag Secret key tag (optional). * @return Map of updated joined rotation keys. */ std::shared_ptr>> MultiEvalAtIndexKeyGen( const PrivateKey privateKey, const std::shared_ptr>> evalKeyMap, const std::vector& indexList, const std::string& keyTag = "") { if (!privateKey) OPENFHE_THROW("Input private key is nullptr"); if (!evalKeyMap) OPENFHE_THROW("Input evaluation key map is nullptr"); if (indexList.empty()) OPENFHE_THROW("Input index vector is empty"); return m_scheme->MultiEvalAtIndexKeyGen(privateKey, evalKeyMap, indexList, keyTag); } /** * @brief Generates joined summation evaluation keys from the current secret share and prior keys (Threshold FHE). * * @param privateKey Secret key share. * @param evalKeyMap Prior summation evaluation keys. * @param keyTag Secret key tag (optional). * @return Map of updated summation evaluation keys. */ std::shared_ptr>> MultiEvalSumKeyGen( const PrivateKey privateKey, const std::shared_ptr>> evalKeyMap, const std::string& keyTag = "") { if (!privateKey) OPENFHE_THROW("Input private key is nullptr"); if (!evalKeyMap) OPENFHE_THROW("Input evaluation key map is nullptr"); return m_scheme->MultiEvalSumKeyGen(privateKey, evalKeyMap, keyTag); } /** * @brief Adds two evaluation keys to produce a new joined evaluation key (Threshold FHE). * * @param evalKey1 First evaluation key. * @param evalKey2 Second evaluation key. * @param keyTag Secret key tag (optional). * @return Joined evaluation key. */ EvalKey MultiAddEvalKeys(EvalKey evalKey1, EvalKey evalKey2, const std::string& keyTag = "") { if (!evalKey1) OPENFHE_THROW("Input first evaluation key is nullptr"); if (!evalKey2) OPENFHE_THROW("Input second evaluation key is nullptr"); return m_scheme->MultiAddEvalKeys(evalKey1, evalKey2, keyTag); } /** * @brief Generates a joined partial evaluation key for homomorphic multiplication from a partial key and current secret share (Threshold FHE). * * @param privateKey Current secret share. * @param evalKey Prior partial evaluation key. * @param keyTag Secret key tag (optional). * @return Joined evaluation key. */ EvalKey MultiMultEvalKey(PrivateKey privateKey, EvalKey evalKey, const std::string& keyTag = "") { if (!privateKey) OPENFHE_THROW("Input private key is nullptr"); if (!evalKey) OPENFHE_THROW("Input evaluation key is nullptr"); return m_scheme->MultiMultEvalKey(privateKey, evalKey, keyTag); } /** * @brief Adds two summation evaluation key sets (Threshold FHE). * * @param evalKeyMap1 First summation key set. * @param evalKeyMap2 Second summation key set. * @param keyTag Secret key tag (optional). * @return Combined summation evaluation key set. */ std::shared_ptr>> MultiAddEvalSumKeys( const std::shared_ptr>> evalKeyMap1, const std::shared_ptr>> evalKeyMap2, const std::string& keyTag = "") { if (!evalKeyMap1) OPENFHE_THROW("Input first evaluation key map is nullptr"); if (!evalKeyMap2) OPENFHE_THROW("Input second evaluation key map is nullptr"); return m_scheme->MultiAddEvalSumKeys(evalKeyMap1, evalKeyMap2, keyTag); } /** * @brief Adds two automorphism evaluation key sets (Threshold FHE). * * @param evalKeyMap1 First automorphism key set. * @param evalKeyMap2 Second automorphism key set. * @param keyTag Secret key tag (optional). * @return Combined automorphism evaluation key set. */ std::shared_ptr>> MultiAddEvalAutomorphismKeys( const std::shared_ptr>> evalKeyMap1, const std::shared_ptr>> evalKeyMap2, const std::string& keyTag = "") { if (!evalKeyMap1) OPENFHE_THROW("Input first evaluation key map is nullptr"); if (!evalKeyMap2) OPENFHE_THROW("Input second evaluation key map is nullptr"); return m_scheme->MultiAddEvalAutomorphismKeys(evalKeyMap1, evalKeyMap2, keyTag); } /** * @brief Adds two public keys to produce a combined public key (Threshold FHE). * * @param publicKey1 First public key. * @param publicKey2 Second public key. * @param keyTag Secret key tag (optional). * @return Combined public key. */ PublicKey MultiAddPubKeys(PublicKey publicKey1, PublicKey publicKey2, const std::string& keyTag = "") { if (!publicKey1) OPENFHE_THROW("Input first public key is nullptr"); if (!publicKey2) OPENFHE_THROW("Input second public key is nullptr"); return m_scheme->MultiAddPubKeys(publicKey1, publicKey2, keyTag); } /** * @brief Adds two partial evaluation keys for multiplication (Threshold FHE). * * @param evalKey1 First evaluation key. * @param evalKey2 Second evaluation key. * @param keyTag Secret key tag (optional). * @return Combined evaluation key. */ EvalKey MultiAddEvalMultKeys(EvalKey evalKey1, EvalKey evalKey2, const std::string& keyTag = "") { if (!evalKey1) OPENFHE_THROW("Input first evaluation key is nullptr"); if (!evalKey2) OPENFHE_THROW("Input second evaluation key is nullptr"); return m_scheme->MultiAddEvalMultKeys(evalKey1, evalKey2, keyTag); } /** * @brief Performs masked decryption for interactive bootstrapping (2-party protocol). * * @param privateKey Secret key share. * @param ciphertext Input ciphertext. * @return Masked decrypted ciphertext. * * @note For Server, expects ciphertext with both polynomials a and b. * For Client, expects only the linear term a. * Includes rounding as part of decryption. */ Ciphertext IntBootDecrypt(const PrivateKey privateKey, ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); ValidateKey(privateKey); return m_scheme->IntBootDecrypt(privateKey, ciphertext); } /** * @brief Encrypts Client's masked decryption for interactive bootstrapping. * Increases ciphertext modulus to allow further computation. Done by Client. * * @param publicKey Joined public key (Threshold FHE). * @param ciphertext Input ciphertext. * @return Encrypted ciphertext. */ Ciphertext IntBootEncrypt(const PublicKey publicKey, ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); ValidateKey(publicKey); return m_scheme->IntBootEncrypt(publicKey, ciphertext); } /** * @brief Combines encrypted and unencrypted masked decryptions in 2-party interactive bootstrapping. * It is the last step in the bootstrapping. * * @param ciphertext1 Encrypted masked decryption. * @param ciphertext2 Unencrypted masked decryption. * @return Refreshed ciphertext. */ Ciphertext IntBootAdd(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2) const { ValidateCiphertext(ciphertext1); ValidateCiphertext(ciphertext2); return m_scheme->IntBootAdd(ciphertext1, ciphertext2); } /** * @brief Prepares a ciphertext for interactive bootstrapping. * * @param ciphertext Input ciphertext. * @return Adjusted ciphertext. * * @note CKKS FIXEDMANUAL/FIXEDAUTO: requires ≥ 2 towers; reduces to 2 towers and sets scale to Delta (not a power of Delta). * CKKS FLEXIBLEAUTO: requires ≥ 3 towers; adjusts scale to level 0. */ Ciphertext IntBootAdjustScale(ConstCiphertext& ciphertext) const { ValidateCiphertext(ciphertext); return m_scheme->IntBootAdjustScale(ciphertext); } /** * @brief Prepares a ciphertext for multi-party interactive bootstrapping (Threshold FHE). * * @param ciphertext Input ciphertext. * @return Adjusted ciphertext. */ Ciphertext IntMPBootAdjustScale(ConstCiphertext& ciphertext) const; /** * @brief Generates a common random polynomial for Multi-Party Interactive Bootstrapping (Threshold FHE). * * @param publicKey Scheme public key or lead party's public key. * @return Random ring element as ciphertext. */ Ciphertext IntMPBootRandomElementGen(const PublicKey publicKey) const; /** * @brief Generates a common random polynomial for Multi-Party Interactive Bootstrapping (Threshold FHE) * using an existing ciphertext, eliminating the need to supply a public key explicitly * * @param ciphertext Reference ciphertext used to derive the cryptocontext and parameters. * @return Random ring element as ciphertext. */ Ciphertext IntMPBootRandomElementGen(ConstCiphertext& ciphertext) const; /** * @brief Performs masked decryption as part of Multi-Party Interactive Bootstrapping (Threshold FHE). * Each party calls this function as part of the protocol * * @param privateKey Secret key share for the party. * @param ciphertext Input ciphertext. * @param a Common random polynomial. * @return Vector of masked decryption shares. */ std::vector> IntMPBootDecrypt(const PrivateKey privateKey, ConstCiphertext& ciphertext, ConstCiphertext& a) const; /** * @brief Aggregates masked decryption and re-encryption shares (Threshold FHE). * It is the second step of the interactive multiparty bootstrapping procedure. * * @param sharesPairVec Vector of (h_0i, h_1i) shares from each party. * @return Aggregated pair of shares (h_0, h_1). */ std::vector> IntMPBootAdd(std::vector>>& sharesPairVec) const; /** * @brief Encrypts the lead party's masked decryption result as the final step of Multi-Party Interactive Bootstrapping (Threshold FHE). * It increases the ciphertext modulus and enables future computations. This operation is done by the lead party as the final step * of interactive multi-party bootstrapping. * * @param publicKey Lead party's public key. * @param sharesPair Aggregated masked decryption and re-encryption shares. * @param a Common random polynomial. * @param ciphertext Input ciphertext. * @return Encrypted refreshed ciphertext. */ Ciphertext IntMPBootEncrypt(const PublicKey publicKey, const std::vector>& sharesPair, ConstCiphertext& a, ConstCiphertext& ciphertext) const; /** * @brief Performs secret sharing of a secret key for Threshold FHE with aborts. * * @param sk Secret key to be shared. * @param N Total number of parties. * @param threshold Threshold number of parties required to reconstruct the key. * @param index Index of the current party. * @param shareType Type of secret sharing ("additive" or "shamir"). * @return Map of secret key shares indexed by party ID. */ std::unordered_map ShareKeys(const PrivateKey& sk, uint32_t N, uint32_t threshold, uint32_t index, const std::string& shareType) const { std::string datatype = demangle(typeid(Element).name()); OPENFHE_THROW("Not implemented for " + datatype); } /** * @brief Recovers a secret key share from existing shares for Threshold FHE with aborts. * * @param sk Output: recovered secret key. * @param sk_shares Map of secret key shares indexed by party ID. * @param N Total number of parties. * @param threshold Threshold number of parties required to reconstruct the key. * @param shareType Type of secret sharing ("additive" or "shamir"). */ void RecoverSharedKey(PrivateKey& sk, std::unordered_map& sk_shares, uint32_t N, uint32_t threshold, const std::string& shareType) const; //------------------------------------------------------------------------------ // FHE Bootstrap Methods //------------------------------------------------------------------------------ /** * Bootstrap functionality: * There are four methods that have to be called in this specific order: * 1. EvalBootstrapSetup: computes and encodes the coefficients for encoding and * decoding and stores the necessary parameters * 2. EvalBootstrapKeyGen: computes and stores the keys for rotations and conjugation * 3. EvalBootstrapPrecompute: computes and stores the plaintexts for encoding and decoding if not already done in EvalBootstrapSetup * 4. EvalBootstrap: refreshes the given ciphertext */ /** * @brief Sets all bootstrapping parameters for both linear and FFT-like methods. Supported only in CKKS. * * @param levelBudget Vector of level budgets for encoding and decoding. * @param dim1 Inner dimensions for baby-step giant-step routine. * @param slots Number of slots to be bootstrapped. * @param correctionFactor Internal rescaling factor to improve precision (only for NATIVE_SIZE=64; 0 = default). * @param precompute Whether to precompute plaintexts for encoding/decoding. * @param BTSlotsEncoding Whether the approximate modular reduction happens over the message being in slots or coefficients */ void EvalBootstrapSetup(std::vector levelBudget = {5, 4}, std::vector dim1 = {0, 0}, uint32_t slots = 0, uint32_t correctionFactor = 0, bool precompute = true, bool BTSlotsEncoding = false) { m_scheme->EvalBootstrapSetup(*this, levelBudget, dim1, slots, correctionFactor, precompute, BTSlotsEncoding); } /** * @brief Generates automorphism keys for EvalBootstrap. Uses baby-step/giant-step strategy. Supported only in CKKS. * * @param privateKey Secret key. * @param slots Number of slots to support permutations on. */ void EvalBootstrapKeyGen(const PrivateKey privateKey, uint32_t slots) { ValidateKey(privateKey); auto evalKeys = m_scheme->EvalBootstrapKeyGen(privateKey, slots); CryptoContextImpl::InsertEvalAutomorphismKey(evalKeys, privateKey->GetKeyTag()); } /** * @brief Precomputes plaintexts for encoding and decoding used in bootstrapping. Supported only in CKKS. * * @param slots Number of slots to be bootstrapped. */ void EvalBootstrapPrecompute(uint32_t slots = 0) { m_scheme->EvalBootstrapPrecompute(*this, slots); } /** * @brief Evaluates bootstrapping on a ciphertext using FFT-like or linear method. Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param numIterations Number of Meta-BTS iterations to improve precision. * @param precision Initial bootstrapping precision (set to 0 for default; tune experimentally). * @return Refreshed ciphertext. */ Ciphertext EvalBootstrap(ConstCiphertext& ciphertext, uint32_t numIterations = 1, uint32_t precision = 0) const { return m_scheme->EvalBootstrap(ciphertext, numIterations, precision); } /** * @brief Evaluates the slots-encoding variant of CKKS bootstrapping (SlotsToCoeffs first, then modulus raise, * CoeffsToSlots and the approximate modular reduction over the message values). EvalBootstrap dispatches * here automatically when EvalBootstrapSetup was called with BTSlotsEncoding = true. Supported only in * CKKS. * * @param ciphertext Input ciphertext. * @param numIterations Number of Meta-BTS iterations to improve precision (1 or 2). * @param precision Initial bootstrapping precision (set to 0 for default; tune experimentally). * @return Refreshed ciphertext. */ Ciphertext EvalBootstrapStCFirst(ConstCiphertext& ciphertext, uint32_t numIterations = 1, uint32_t precision = 0) const { return m_scheme->EvalBootstrapStCFirst(ciphertext, numIterations, precision); } /** * @brief Precomputes the encoding/decoding plaintexts for FE functional bootstrapping. Supported only in * CKKS, with HYBRID key switching and FirstModSize == ScalingModSize + 1. * * Shares the bootstrapping precomputation slot for @p slots with EvalBootstrapSetup and EvalFBTSetup, so * a context can hold the precomputation for only one of them per slot count. * * @param levelBudget Levels spent on CoeffsToSlots and SlotsToCoeffs. * @param dim1 Baby-step dimensions for the two linear transforms (0 = choose automatically). * @param slots Number of slots to be bootstrapped (0 = full packing). */ void EvalFEFuncBootstrapSetup(const std::vector& levelBudget = {5, 4}, const std::vector& dim1 = {0, 0}, uint32_t slots = 0) { GetScheme()->EvalFEFuncBootstrapSetup(*this, levelBudget, dim1, slots); } /** * @brief Refreshes a ciphertext and evaluates a function on it in one pass, by evaluating the function's * Fourier extension over the bootstrapped message. Supported only in CKKS. * * The message is embedded into half of the series period (t = m/2), so the input must lie in [-1/2, 1/2) * and @p coefficients must be the Fourier extension of the target function over that domain. The result * is 2*Re(c_0 + sum_{j>=1} c_j exp(2*Pi*i*j*t)) and is therefore always real-valued: with CKKSDataType * COMPLEX the imaginary part of each input slot is discarded and the output slots have zero imaginary * part. Use CKKSDataType REAL unless complex intermediates are needed elsewhere in the computation. * * @param ciphertext Input ciphertext, with slot values in [-1/2, 1/2). * @param coefficients Fourier coefficients c_j of the target function, c_0 first. * @return Refreshed ciphertext holding the function values. */ Ciphertext EvalFEFuncBootstrap(ConstCiphertext& ciphertext, const std::vector>& coefficients) const { ValidateCiphertext(ciphertext); return GetScheme()->EvalFEFuncBootstrap(ciphertext, coefficients); } /** * @brief Runs the function-independent part of FE functional bootstrapping and returns the powers of the * complex exponential, so that several functions can be evaluated on one bootstrapped ciphertext. * Supported only in CKKS. * * The refresh itself - SlotsToCoeffs, modulus raise, CoeffsToSlots and the complex exponential - happens * here and dominates the cost; each subsequent EvalFEFuncBootstrapWithPrecomp only evaluates a series. * The Paterson-Stockmeyer shape is fixed by @p coefficients, which must have degree at least 5, so pass * the longest series of the family: every series later evaluated against these powers may have at most * the degree of @p coefficients rounded up to the shape's capacity (EvalFEFuncBootstrapWithPrecomp * reports the exact bound if it is exceeded). A series below degree 5 is evaluated straight from the * power basis and is accepted as long as its degree does not exceed the number of powers the shape holds. * * @param ciphertext Input ciphertext, with slot values in [-1/2, 1/2). * @param coefficients Fourier coefficients of the longest series to be evaluated, c_0 first. * @return Powers of the complex exponential, to be passed to EvalFEFuncBootstrapWithPrecomp. */ std::shared_ptr> EvalFEFuncBootstrapPrecompute( ConstCiphertext& ciphertext, const std::vector>& coefficients) const { ValidateCiphertext(ciphertext); return GetScheme()->EvalFEFuncBootstrapPrecompute(ciphertext, coefficients); } /** * @brief Evaluates one function's Fourier series against powers from EvalFEFuncBootstrapPrecompute. * Supported only in CKKS. * * @param powers Powers returned by EvalFEFuncBootstrapPrecompute. * @param coefficients Fourier coefficients c_j of this function, c_0 first. * @return Refreshed ciphertext holding the function values. */ Ciphertext EvalFEFuncBootstrapWithPrecomp(const std::shared_ptr>& powers, const std::vector>& coefficients) const { ValidateSeriesPowers(powers); return GetScheme()->EvalFEFuncBootstrapWithPrecomp(powers, coefficients); } /** * @brief Sets up CKKS functional bootstrapping of a look-up table (see CKKS_FUNCTIONAL_BOOTSTRAPING.md): * computes its depth and precomputes the homomorphic encoding and decoding matrices with the required * scalings folded in. Supported only in CKKS with HYBRID key switching (64-bit build). Shares the * precomputation entry for @p numSlots with EvalBootstrapSetup and EvalFEFuncBootstrapSetup. * * @param coeffs Trigonometric Hermite interpolation coefficients of the look-up table * (complex, or integer for a Boolean function of order 1); only their number * is used here. * @param numSlots Number of slots to be bootstrapped (0 = full packing). * @param PIn Plaintext modulus of the RLWE input. * @param POut Plaintext modulus of the RLWE output. * @param Bigq Ciphertext modulus of the RLWE scheme the input is converted from. * @param pubKey Public key (its element parameters define the modulus chain). * @param dim1 Baby-step dimensions for CoeffsToSlots and SlotsToCoeffs (0 = automatic). * @param levelBudget Levels spent on CoeffsToSlots and SlotsToCoeffs (each in [1, log2(slots)]). * @param lvlsAfterBoot Number of levels remaining after functional bootstrapping. * @param depthLeveledComputation Depth reserved for a leveled computation between EvalFBTNoDecoding and * EvalHomDecoding. * @param order Order of the trigonometric Hermite interpolation (1, 2 or 3). */ template void EvalFBTSetup(const std::vector& coeffs, uint32_t numSlots, const BigInteger& PIn, const BigInteger& POut, const BigInteger& Bigq, const PublicKey& pubKey, const std::vector& dim1, const std::vector& levelBudget, uint32_t lvlsAfterBoot = 0, uint32_t depthLeveledComputation = 0, size_t order = 1) { m_scheme->EvalFBTSetup(*this, coeffs, numSlots, PIn, POut, Bigq, pubKey, dim1, levelBudget, lvlsAfterBoot, depthLeveledComputation, order); } /** * @brief Functional bootstrapping of a look-up table on a CKKS ciphertext imported from RLWE * (SchemeletRLWEMP::ConvertRLWEToCKKS): modulus raise, CoeffsToSlots, complex exponential (or cosine * for a Boolean function of order 1), trigonometric Hermite series, SlotsToCoeffs and scalings. * Supported only in CKKS. * * @param ciphertext Input ciphertext holding the RLWE input in its coefficients. * @param coeffs Trigonometric Hermite interpolation coefficients of the look-up table (complex, or * integer for a Boolean function of order 1), scaled to magnitude at most one. * @param digitBitSize Bit size of the look-up table input (log2 of the input plaintext modulus). * @param initialScaling Scale of the imported message (the RLWE ciphertext modulus of the input). * @param postScaling Integer the result is multiplied by after SlotsToCoeffs (typically the scale the * coefficients were divided by; values of at most 1 are ignored). * @param levelToReduce Number of levels to drop before SlotsToCoeffs. * @param order Order of the trigonometric Hermite interpolation (1, 2 or 3). * @return Ciphertext holding the look-up table outputs in its coefficients, ready for * SchemeletRLWEMP::ConvertCKKSToRLWE. */ template Ciphertext EvalFBT(ConstCiphertext& ciphertext, const std::vector& coeffs, uint32_t digitBitSize, const BigInteger& initialScaling, uint64_t postScaling, uint32_t levelToReduce = 0, size_t order = 1) { return m_scheme->EvalFBT(ciphertext, coeffs, digitBitSize, initialScaling, postScaling, levelToReduce, order); } /** * @brief Functional bootstrapping of a look-up table without the final homomorphic decoding: the outputs stay * in the slots so that a leveled computation can be applied before EvalHomDecoding. Supported only in * CKKS. * * @param ciphertext Input ciphertext holding the RLWE input in its coefficients. * @param coeffs Trigonometric Hermite interpolation coefficients of the look-up table. * @param digitBitSize Bit size of the look-up table input (log2 of the input plaintext modulus). * @param initialScaling Scale of the imported message (the RLWE ciphertext modulus of the input). * @param order Order of the trigonometric Hermite interpolation (1, 2 or 3). * @return Ciphertext holding the look-up table outputs in its slots. */ template Ciphertext EvalFBTNoDecoding(ConstCiphertext& ciphertext, const std::vector& coeffs, uint32_t digitBitSize, const BigInteger& initialScaling, size_t order = 1) { return m_scheme->EvalFBTNoDecoding(ciphertext, coeffs, digitBitSize, initialScaling, order); } /** * @brief Homomorphic decoding step of functional bootstrapping: drops levels if requested, applies * SlotsToCoeffs, multiplies by postScaling and rescales to noise scale degree 1. Supported only in CKKS. * * @param ciphertext Ciphertext with the look-up table outputs in its slots. * @param postScaling Integer the result is multiplied by after SlotsToCoeffs (values of at most 1 are * ignored). * @param levelToReduce Number of levels to drop before SlotsToCoeffs. * @return Ciphertext holding the outputs in its coefficients, at noise scale degree 1. */ Ciphertext EvalHomDecoding(ConstCiphertext& ciphertext, uint64_t postScaling, uint32_t levelToReduce = 0) { return m_scheme->EvalHomDecoding(ciphertext, postScaling, levelToReduce); } /** * @brief Precomputes the complex-exponential powers for multi-value bootstrapping (modulus raise, * CoeffsToSlots, complex exponential and its powers). Every look-up table later evaluated on the * result (EvalMVB, EvalMVBNoDecoding) must be interpolated with the same shape (same plaintext modulus * and order) as @p coeffs, since those determine which powers are computed. Supported only in CKKS. * * @param ciphertext Input ciphertext holding the RLWE input in its coefficients. * @param coeffs Trigonometric Hermite interpolation coefficients of one of the look-up tables. * @param digitBitSize Bit size of the look-up table input (log2 of the input plaintext modulus). * @param initialScaling Scale of the imported message (the RLWE ciphertext modulus of the input). * @param order Order of the trigonometric Hermite interpolation (1, 2 or 3). * @return Powers of the complex exponential. */ template std::shared_ptr> EvalMVBPrecompute(ConstCiphertext& ciphertext, const std::vector& coeffs, uint32_t digitBitSize, const BigInteger& initialScaling, size_t order = 1) { return m_scheme->EvalMVBPrecompute(ciphertext, coeffs, digitBitSize, initialScaling, order); } /** * @brief Multi-value bootstrapping: evaluates the trigonometric Hermite series of a look-up table on the * powers from EvalMVBPrecompute and applies the homomorphic decoding. Supported only in CKKS. * * @param ciphertexts Powers of the complex exponential from EvalMVBPrecompute. * @param coeffs Trigonometric Hermite interpolation coefficients of the look-up table (same shape as * in EvalMVBPrecompute). * @param digitBitSize Bit size of the look-up table input (log2 of the input plaintext modulus). * @param postScaling Integer the result is multiplied by after SlotsToCoeffs (values of at most 1 are * ignored). * @param levelToReduce Number of levels to drop before SlotsToCoeffs. * @param order Order of the trigonometric Hermite interpolation (1, 2 or 3). * @return Ciphertext holding the look-up table outputs in its coefficients, at noise scale degree 1. */ template Ciphertext EvalMVB(const std::shared_ptr> ciphertexts, const std::vector& coeffs, uint32_t digitBitSize, const uint64_t postScaling, uint32_t levelToReduce = 0, size_t order = 1) { return m_scheme->EvalMVB(ciphertexts, coeffs, digitBitSize, postScaling, levelToReduce, order); } /** * @brief Multi-value bootstrapping without the final homomorphic decoding: the look-up table outputs stay in * the slots so that a leveled computation can be applied before EvalHomDecoding. Supported only in * CKKS. * * @param ciphertexts Powers of the complex exponential from EvalMVBPrecompute. * @param coeffs Trigonometric Hermite interpolation coefficients of the look-up table (same shape as in * EvalMVBPrecompute). * @param digitBitSize Bit size of the look-up table input (log2 of the input plaintext modulus). * @param order Order of the trigonometric Hermite interpolation (1, 2 or 3). * @return Ciphertext holding the look-up table outputs in its slots. */ template Ciphertext EvalMVBNoDecoding(const std::shared_ptr> ciphertexts, const std::vector& coeffs, uint32_t digitBitSize, size_t order = 1) { return m_scheme->EvalMVBNoDecoding(ciphertexts, coeffs, digitBitSize, order); } /** * @brief Evaluates a trigonometric Hermite series: approximates exp(i*Pi/2*x) with the given Chebyshev series * over [a, b], squares it twice to get exp(2*Pi*i*x), evaluates the Hermite series in the power basis of * the exponential and returns its real part. The exponential can be cached in the bootstrapping * precomputation for the ciphertext's slot count and reused. Supported only in CKKS. * * @param ciphertext Input ciphertext. * @param coefficientsCheb Chebyshev coefficients of exp(i*Pi/2*x) over [a, b]. * @param a Lower bound of the Chebyshev interpolation interval. * @param b Upper bound of the Chebyshev interpolation interval. * @param coefficientsHerm Hermite series coefficients in the power basis of the exponential, divided by 2. * @param precomp 0 or 1: compute the exponential and cache it in the first or second slot; 2: reuse * the first cached exponential; any other value: reuse the second one. * @return The real part of the evaluated series. */ template Ciphertext EvalHermiteTrigSeries(ConstCiphertext& ciphertext, const std::vector>& coefficientsCheb, double a, double b, const std::vector& coefficientsHerm, size_t precomp = 0) { return m_scheme->EvalHermiteTrigSeries(ciphertext, coefficientsCheb, a, b, coefficientsHerm, precomp); } /** * @brief Gets the correction factor of CKKS bootstrapping (the number of bits the message is scaled down by in * total before the approximate modular reduction to improve precision), as set by EvalBootstrapSetup. * Supported only in CKKS. * * @return The correction factor. */ uint32_t GetCKKSBootCorrectionFactor() { return m_scheme->GetCKKSBootCorrectionFactor(); } /** * @brief Sets the correction factor of CKKS bootstrapping. Supported only in CKKS. * * @param cf The correction factor. */ void SetCKKSBootCorrectionFactor(uint32_t cf) { return m_scheme->SetCKKSBootCorrectionFactor(cf); } //------------------------------------------------------------------------------ // Scheme switching Methods //------------------------------------------------------------------------------ /** * Scheme switching between CKKS and FHEW functionality * There are three methods that have to be called in this specific order: * 1. EvalCKKStoFHEWSetup: generates a FHEW cryptocontext and returns the key, computes and encodes * the coefficients for encoding and decoding and stores the necessary parameters * 2. EvalCKKStoFHEWKeyGen: computes and stores the keys for rotations and conjugation * 3. EvalCKKStoFHEW: returns the FHEW/CGGI ciphertext * 1'. EvalFHEWtoCKKSSetup: takes in the CKKS cryptocontext and sets the parameters * 2'. EvalFHEWtoCKKSKeyGen: computes and stores the switching key and the keys for rotations and conjugation * 3'. EvalFHEWtoCKKS: returns the CKKS ciphertext * 1''. EvalSchemeSwitchingSetup: generates a FHEW cryptocontext and returns the key, computes and encodes * the coefficients for encoding and decoding and stores the necessary parameters * 2''. EvalSchemeSwitchingKeyGen: computes and stores the switching key and the keys for rotations and conjugation * 3''. EvalCompareSchemeSwitching/EvalFuncSchemeSwitching: returns the CKKS ciphertext of the function specified */ /** * @brief Sets all parameters for switching from CKKS to FHEW. * * @param params Parameters for CKKS-to-FHEW scheme switching. * @return FHEW secret key. * * @note TODO: Add overload for pre-generated BinFHEContext. */ LWEPrivateKey EvalCKKStoFHEWSetup(SchSwchParams params) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); SetParamsFromCKKSCryptocontext(params); return m_scheme->EvalCKKStoFHEWSetup(params); } /** * @brief Generates keys for CKKS-to-FHEW scheme switching: rotation keys, conjugation keys, and switching key. * * @param keyPair CKKS key pair. * @param lwesk FHEW secret key. */ void EvalCKKStoFHEWKeyGen(const KeyPair& keyPair, ConstLWEPrivateKey& lwesk) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateKey(keyPair.secretKey); if (!lwesk) OPENFHE_THROW("FHEW private key passed to EvalCKKStoFHEWKeyGen is null"); auto evalKeys = m_scheme->EvalCKKStoFHEWKeyGen(keyPair, lwesk); CryptoContextImpl::InsertEvalAutomorphismKey(evalKeys, keyPair.secretKey->GetKeyTag()); } /** * @brief Performs precomputations for CKKS homomorphic decoding. Allows setting a custom scale factor. Given as * a separate method than EvalCKKStoFHEWSetup to allow the user to specify a scale that depends on * the CKKS and FHEW cryptocontexts * * @param scale Scaling factor for the linear transform matrix. */ void EvalCKKStoFHEWPrecompute(double scale = 1.0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); m_scheme->EvalCKKStoFHEWPrecompute(*this, scale); } /** * @brief Switches a CKKS ciphertext to a vector of FHEW ciphertexts. * * @param ciphertext Input CKKS ciphertext. * @param numCtxts Number of coefficients to extract (defaults to number of slots if 0). * @return Vector of LWE ciphertexts. */ std::vector> EvalCKKStoFHEW(ConstCiphertext& ciphertext, uint32_t numCtxts = 0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); if (ciphertext == nullptr) OPENFHE_THROW("ciphertext passed to EvalCKKStoFHEW is empty"); return m_scheme->EvalCKKStoFHEW(ciphertext, numCtxts); } /** * @brief Sets parameters for switching from FHEW to CKKS. Requires existing CKKS context. * * @param ccLWE Source FHEW crypto context. * @param numSlotsCKKS Number of slots in resulting CKKS ciphertext. * @param logQ Ciphertext modulus size in FHEW (for high precision). */ void EvalFHEWtoCKKSSetup(const std::shared_ptr& ccLWE, uint32_t numSlotsCKKS = 0, uint32_t logQ = 25) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); m_scheme->EvalFHEWtoCKKSSetup(*this, ccLWE, numSlotsCKKS, logQ); } /** * @brief Generates keys for switching from FHEW to CKKS. * * @param keyPair CKKS key pair. * @param lwesk FHEW secret key. * @param numSlots Number of slots for CKKS encryption. * @param numCtxts Number of LWE ciphertext values to encrypt. * @param dim1 Baby-step parameter for linear transform. * @param L Target level for homomorphic decoding matrix. */ void EvalFHEWtoCKKSKeyGen(const KeyPair& keyPair, ConstLWEPrivateKey& lwesk, uint32_t numSlots = 0, uint32_t numCtxts = 0, uint32_t dim1 = 0, uint32_t L = 0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateKey(keyPair.secretKey); auto evalKeys = m_scheme->EvalFHEWtoCKKSKeyGen(keyPair, lwesk, numSlots, numCtxts, dim1, L); CryptoContextImpl::InsertEvalAutomorphismKey(evalKeys, keyPair.secretKey->GetKeyTag()); } /** * @brief Switches a vector of FHEW ciphertexts to a single CKKS ciphertext. * * @param LWECiphertexts Input vector of FHEW ciphertexts. * @param numCtxts Number of values to encode. * @param numSlots Number of CKKS slots to use. * @param p Plaintext modulus (default = 4). * @param pmin Minimum expected plaintext value (default = 0.0). * @param pmax Maximum expected plaintext value (default = 2.0). * @param dim1 Baby-step parameter (used in argmin). * @return CKKS ciphertext encoding the input LWE messages. */ Ciphertext EvalFHEWtoCKKS(std::vector>& LWECiphertexts, uint32_t numCtxts = 0, uint32_t numSlots = 0, uint32_t p = 4, double pmin = 0.0, double pmax = 2.0, uint32_t dim1 = 0) const { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); return m_scheme->EvalFHEWtoCKKS(LWECiphertexts, numCtxts, numSlots, p, pmin, pmax, dim1); } /** * @brief Sets scheme switching parameters using the current CKKS crypto context. * * @param params Scheme switching parameter object to populate. */ void SetParamsFromCKKSCryptocontext(SchSwchParams& params) { const auto cryptoParams = std::dynamic_pointer_cast(m_params); if (!cryptoParams) OPENFHE_THROW("Invalid crypto parameters: expected CryptoParametersCKKSRNS"); params.SetInitialCKKSModulus(cryptoParams->GetElementParams()->GetParams()[0]->GetModulus()); params.SetRingDimension(GetRingDimension()); // TODO (dsuponit): is this correct - PlaintextModulus used as scalingModSize? params.SetScalingModSize(GetEncodingParams()->GetPlaintextModulus()); params.SetBatchSize(GetEncodingParams()->GetBatchSize()); params.SetParamsFromCKKSCryptocontextCalled(); } /** * @brief Sets parameters for switching between CKKS and FHEW. * * @param params Scheme switching parameter object. * @return FHEW secret key. * * @note TODO: Add overload for pre-generated BinFHEContext. */ LWEPrivateKey EvalSchemeSwitchingSetup(SchSwchParams& params) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); SetParamsFromCKKSCryptocontext(params); return m_scheme->EvalSchemeSwitchingSetup(params); } /** * @brief Generates keys for switching between CKKS and FHEW. * * @param keyPair CKKS key pair. * @param lwesk FHEW secret key. */ void EvalSchemeSwitchingKeyGen(const KeyPair& keyPair, ConstLWEPrivateKey& lwesk) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateKey(keyPair.secretKey); auto evalKeys = m_scheme->EvalSchemeSwitchingKeyGen(keyPair, lwesk); CryptoContextImpl::InsertEvalAutomorphismKey(evalKeys, keyPair.secretKey->GetKeyTag()); } /** * @brief Performs precomputations for scheme switching in CKKS-to-FHEW comparison. * Given as a separate method than EvalSchemeSwitchingSetup to allow the user to specify a scale. * * @param pLWE Target plaintext modulus for FHEW ciphertexts. * @param scaleSign Scaling factor for CKKS ciphertexts before switching. * @param unit Indicates if input messages are normalized to unit circle. */ void EvalCompareSwitchPrecompute(uint32_t pLWE = 0, double scaleSign = 1.0, bool unit = false) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); m_scheme->EvalCompareSwitchPrecompute(*this, pLWE, scaleSign, unit); } /** * @brief Compares two CKKS ciphertexts using FHEW-based scheme switching and returns CKKS result. * * @param ciphertext1 First input CKKS ciphertext. * @param ciphertext2 Second input CKKS ciphertext. * @param numCtxts Number of coefficients to extract. * @param numSlots Number of slots to encode in the result. * @param pLWE Target plaintext modulus for FHEW ciphertexts. * @param scaleSign Scaling factor for CKKS ciphertexts before switching. * @param unit Indicates if input messages are normalized to unit circle. * @return CKKS ciphertext encoding sign comparison result. */ Ciphertext EvalCompareSchemeSwitching(ConstCiphertext& ciphertext1, ConstCiphertext& ciphertext2, uint32_t numCtxts = 0, uint32_t numSlots = 0, uint32_t pLWE = 0, double scaleSign = 1.0, bool unit = false) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateCiphertext(ciphertext1); ValidateCiphertext(ciphertext2); return m_scheme->EvalCompareSchemeSwitching(ciphertext1, ciphertext2, numCtxts, numSlots, pLWE, scaleSign, unit); } /** * @brief Computes minimum and index of the first packed values using scheme switching. * * @param ciphertext Input CKKS ciphertext. * @param publicKey CKKS public key. * @param numValues Number of values to compare (we assume that numValues is a power of two). * @param numSlots Number of output slots. * @param pLWE Target plaintext modulus for FHEW. * @param scaleSign Scaling factor before switching to FHEW. The resulting FHEW ciphertexts will encrypt values modulo pLWE, * so scaleSign should account for this pLWE and is given here only if the homomorphic decoding matrix is * not scaled with the desired values * @return A vector of two CKKS ciphertexts: [min, argmin]. The ciphertexts have junk after the first slot in the first ciphertext * and after numValues in the second ciphertext if oneHot=true and after the first slot if oneHot=false. */ std::vector> EvalMinSchemeSwitching(ConstCiphertext& ciphertext, PublicKey& publicKey, uint32_t numValues = 0, uint32_t numSlots = 0, uint32_t pLWE = 0, double scaleSign = 1.0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateCiphertext(ciphertext); return m_scheme->EvalMinSchemeSwitching(ciphertext, publicKey, numValues, numSlots, pLWE, scaleSign); } /** * @brief Computes minimum and index using more FHEW operations than CKKS with higher precision, but slower than EvalMinSchemeSwitching. * * @param ciphertext Input CKKS ciphertext. * @param publicKey CKKS public key. * @param numValues Number of packed values to compare. * @param numSlots Number of slots in the output ciphertexts. * @param pLWE Target plaintext modulus for FHEW ciphertexts. * @param scaleSign Scaling factor before switching to FHEW. * @return A vector with two CKKS ciphertexts: [min, argmin]. */ std::vector> EvalMinSchemeSwitchingAlt(ConstCiphertext& ciphertext, PublicKey& publicKey, uint32_t numValues = 0, uint32_t numSlots = 0, uint32_t pLWE = 0, double scaleSign = 1.0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateCiphertext(ciphertext); return m_scheme->EvalMinSchemeSwitchingAlt(ciphertext, publicKey, numValues, numSlots, pLWE, scaleSign); } /** * @brief Computes maximum and index from the first packed values using scheme switching. * * @param ciphertext Input CKKS ciphertext. * @param publicKey CKKS public key. * @param numValues Number of values to compare (we assume that numValues is a power of two). * @param numSlots Number of output slots. * @param pLWE Target plaintext modulus for FHEW. * @param scaleSign Scaling factor before switching to FHEW. * @return A vector of two CKKS ciphertexts: [max, argmax]. The ciphertexts have junk after the first slot in the first ciphertext * and after numValues in the second ciphertext if oneHot=true and after the first slot if oneHot=false. */ std::vector> EvalMaxSchemeSwitching(ConstCiphertext& ciphertext, PublicKey& publicKey, uint32_t numValues = 0, uint32_t numSlots = 0, uint32_t pLWE = 0, double scaleSign = 1.0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateCiphertext(ciphertext); return m_scheme->EvalMaxSchemeSwitching(ciphertext, publicKey, numValues, numSlots, pLWE, scaleSign); } /** * @brief Computes max and index via scheme switching, with more FHEW operations for better precision than EvalMaxSchemeSwitching. * * @param ciphertext Input CKKS ciphertext. * @param publicKey CKKS public key. * @param numValues Number of values to compare. * @param numSlots Number of output slots. * @param pLWE Target plaintext modulus for FHEW. * @param scaleSign Scaling factor before switching to FHEW. * @return A vector of two CKKS ciphertexts: [max, argmax]. */ std::vector> EvalMaxSchemeSwitchingAlt(ConstCiphertext& ciphertext, PublicKey& publicKey, uint32_t numValues = 0, uint32_t numSlots = 0, uint32_t pLWE = 0, double scaleSign = 1.0) { VerifyCKKSScheme(__func__); VerifyCKKSRealDataType(__func__); ValidateCiphertext(ciphertext); return m_scheme->EvalMaxSchemeSwitchingAlt(ciphertext, publicKey, numValues, numSlots, pLWE, scaleSign); } /** * @brief Returns the BinFHE context used for scheme switching. * @return BinFHE context. */ std::shared_ptr GetBinCCForSchemeSwitch() const { return m_scheme->GetBinCCForSchemeSwitch(); } /** * @brief Sets the BinFHE context to be used for scheme switching. * @param ccLWE BinFHE context. */ void SetBinCCForSchemeSwitch(std::shared_ptr ccLWE) { m_scheme->SetBinCCForSchemeSwitch(ccLWE); } /** * @brief Gets the FHEW-to-CKKS scheme switching key ciphertext. * @return Switching key ciphertext. */ Ciphertext GetSwkFC() const { return m_scheme->GetSwkFC(); } /** * @brief Sets the FHEW-to-CKKS scheme switching key ciphertext. * @param FHEWtoCKKSswk Switching key ciphertext. */ void SetSwkFC(Ciphertext FHEWtoCKKSswk) { m_scheme->SetSwkFC(FHEWtoCKKSswk); } /** * @brief Gets indices that do not have automorphism keys for the given secret key tag in the key map * * @param keyTag secret key tag * @param indices set of specific indices to check the key map against * @return indices that do not have automorphism keys associated with */ static std::set GetEvalAutomorphismNoKeyIndices(const std::string& keyTag, const std::set& indices) { std::set existingIndices{CryptoContextImpl::GetExistingEvalAutomorphismKeyIndices(keyTag)}; // if no index found for the given keyTag, then the entire set "indices" is returned return (existingIndices.empty()) ? indices : CryptoContextImpl::GetUniqueValues(existingIndices, indices); } /** * @brief Returns automorphism indices for all existing evaluation keys. * @param keyTag Secret key tag. * @return Set of indices found for the given key tag. Empty if none exist. */ static std::set GetExistingEvalAutomorphismKeyIndices(const std::string& keyTag); /** * @brief Compares two sets and returns unique values from the second set. * @param oldValues First set to compare against. * @param newValues Second set to extract unique values from. * @return Set of values present in newValues but not in oldValues. */ static std::set GetUniqueValues(const std::set& oldValues, const std::set& newValues); template void save(Archive& ar, std::uint32_t const version) const { ar(cereal::make_nvp("cc", m_params)); ar(cereal::make_nvp("kt", m_scheme)); ar(cereal::make_nvp("si", m_schemeId)); } template void load(Archive& ar, std::uint32_t const version) { if (version > CryptoContextImpl::SerializedVersion()) { OPENFHE_THROW("serialized object version " + std::to_string(version) + " is from a later version of the library"); } ar(cereal::make_nvp("cc", m_params)); ar(cereal::make_nvp("kt", m_scheme)); ar(cereal::make_nvp("si", m_schemeId)); SetKSTechniqueInScheme(); // NOTE: a pointer to this object will be wrapped in a shared_ptr, and is a // "CryptoContext". OpenFHE relies on the notion that identical // CryptoContextImpls are not duplicated in memory Once we deserialize this // object, we must check to see if there is a matching object for this // object that's already existing in memory if it DOES exist, use it. If it // does NOT exist, add this to the cache of all contexts } std::string SerializedObjectName() const override { return "CryptoContext"; } static uint32_t SerializedVersion() { return 1; } }; // Member function specializations. Their implementations are in cryptocontext.cpp /// @cond INTERNAL template <> DecryptResult CryptoContextImpl::MultipartyDecryptFusion( const std::vector>& partialCiphertextVec, Plaintext* plaintext) const; template <> std::unordered_map CryptoContextImpl::ShareKeys(const PrivateKey& sk, uint32_t N, uint32_t threshold, uint32_t index, const std::string& shareType) const; /// @endcond } // namespace lbcrypto #endif // SRC_PKE_INCLUDE_CRYPTOCONTEXT_H_