1 // @file pubkeylp.h -- Public key type for lattice crypto operations.
2 // @author TPOC: contact@palisade-crypto.org
3 //
4 // @copyright Copyright (c) 2019, New Jersey Institute of Technology (NJIT)
5 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 1. Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution. THIS SOFTWARE IS
13 // PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
14 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
15 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
16 // EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
17 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 
24 #ifndef LBCRYPTO_CRYPTO_PUBKEYLP_H
25 #define LBCRYPTO_CRYPTO_PUBKEYLP_H
26 
27 #include <iomanip>
28 #include <limits>
29 #include <map>
30 #include <memory>
31 #include <string>
32 #include <utility>
33 #include <vector>
34 
35 #include "lattice/elemparams.h"
36 #include "lattice/ilparams.h"
37 
38 #include "lattice/ildcrtparams.h"
39 #include "lattice/ilelement.h"
40 #include "utils/caller_info.h"
41 #include "utils/hashutil.h"
42 #include "utils/inttypes.h"
43 
44 #include "math/distrgen.h"
45 
46 #include "encoding/encodingparams.h"
47 
48 /**
49  * @namespace lbcrypto
50  * The namespace of lbcrypto
51  */
52 namespace lbcrypto {
53 
54 /* This struct holds the different options for
55  * key switching algorithms that are supported
56  * by the library.
57  *
58  */
59 enum KeySwitchTechnique { BV, GHS, HYBRID };
60 
61 /* This struct holds the different options for
62  * mod switching algorithms that are supported
63  * by the library.
64  *
65  */
66 enum ModSwitchMethod { MANUAL, AUTO };
67 
68 // forward declarations, used to resolve circular header dependencies
69 template <typename Element>
70 class CiphertextImpl;
71 
72 template <typename Element>
73 class LPCryptoParameters;
74 
75 template <typename Element>
76 class LPCryptoParametersBFV;
77 
78 template <typename Element>
79 class CryptoObject;
80 
81 struct EncryptResult {
EncryptResultEncryptResult82   EncryptResult() : isValid(false), numBytesEncrypted(0) {}
83 
EncryptResultEncryptResult84   explicit EncryptResult(size_t len) : isValid(true), numBytesEncrypted(len) {}
85 
86   bool isValid;  // whether the encryption was successful
87   // count of the number of plaintext bytes that were encrypted
88   usint numBytesEncrypted;
89 };
90 
91 /**
92  * @brief Decryption result.  This represents whether the decryption of a
93  * cipheretext was performed correctly.
94  *
95  * This is intended to eventually incorporate information about the amount of
96  * padding in a decoded ciphertext, to ensure that the correct amount of
97  * padding is stripped away. It is intended to provided a very simple kind of
98  * checksum eventually. This notion of a decoding output is inherited from the
99  * crypto++ library. It is also intended to be used in a recover and restart
100  * robust functionality if not all ciphertext is recieved over a lossy
101  * channel, so that if all information is eventually recieved,
102  * decoding/decryption can be performed eventually. This is intended to be
103  * returned with the output of a decryption operation.
104  */
105 struct DecryptResult {
106   /**
107    * Constructor that initializes all message lengths to 0.
108    */
DecryptResultDecryptResult109   DecryptResult() : isValid(false), messageLength(0) {}
110 
111   /**
112    * Constructor that initializes all message lengths.
113    * @param len the new length.
114    */
DecryptResultDecryptResult115   explicit DecryptResult(size_t len) : isValid(true), messageLength(len) {}
116 
117   bool isValid;        /**< whether the decryption was successful */
118   usint messageLength; /**< the length of the decrypted plaintext message */
119 };
120 
121 /**
122  * @brief Abstract interface class for LP Keys
123  *
124  * @tparam Element a ring element.
125  */
126 template <class Element>
127 class LPKey : public CryptoObject<Element>, public Serializable {
128  public:
129   explicit LPKey(CryptoContext<Element> cc, const string &id = "")
130       : CryptoObject<Element>(cc, id) {}
131 
LPKey(shared_ptr<CryptoObject<Element>> co)132   explicit LPKey(shared_ptr<CryptoObject<Element>> co)
133       : CryptoObject<Element>(co) {}
134 
~LPKey()135   virtual ~LPKey() {}
136 
137   template <class Archive>
save(Archive & ar,std::uint32_t const version)138   void save(Archive &ar, std::uint32_t const version) const {
139     ar(::cereal::base_class<CryptoObject<Element>>(this));
140   }
141 
142   template <class Archive>
load(Archive & ar,std::uint32_t const version)143   void load(Archive &ar, std::uint32_t const version) {
144     ar(::cereal::base_class<CryptoObject<Element>>(this));
145   }
146 };
147 
148 template <typename Element>
149 class LPPublicKeyImpl;
150 
151 template <typename Element>
152 using LPPublicKey = shared_ptr<LPPublicKeyImpl<Element>>;
153 
154 /**
155  * @brief Class for LP public keys
156  * @tparam Element a ring element.
157  */
158 template <typename Element>
159 class LPPublicKeyImpl : public LPKey<Element> {
160  public:
161   /**
162    * Basic constructor
163    *
164    * @param cc - CryptoContext
165    * @param id - key identifier
166    */
167   explicit LPPublicKeyImpl(CryptoContext<Element> cc = 0, const string &id = "")
168       : LPKey<Element>(cc, id) {}
169 
170   /**
171    * Copy constructor
172    *
173    *@param &rhs LPPublicKeyImpl to copy from
174    */
LPPublicKeyImpl(const LPPublicKeyImpl<Element> & rhs)175   explicit LPPublicKeyImpl(const LPPublicKeyImpl<Element> &rhs)
176       : LPKey<Element>(rhs.GetCryptoContext(), rhs.GetKeyTag()) {
177     m_h = rhs.m_h;
178   }
179 
180   /**
181    * Move constructor
182    *
183    *@param &rhs LPPublicKeyImpl to move from
184    */
LPPublicKeyImpl(LPPublicKeyImpl<Element> && rhs)185   explicit LPPublicKeyImpl(LPPublicKeyImpl<Element> &&rhs)
186       : LPKey<Element>(rhs.GetCryptoContext(), rhs.GetKeyTag()) {
187     m_h = std::move(rhs.m_h);
188   }
189 
190   operator bool() const {
191     return static_cast<bool>(this->context) && m_h.size() != 0;
192   }
193 
194   /**
195    * Assignment Operator.
196    *
197    * @param &rhs LPPublicKeyImpl to copy from
198    */
199   const LPPublicKeyImpl<Element> &operator=(
200       const LPPublicKeyImpl<Element> &rhs) {
201     CryptoObject<Element>::operator=(rhs);
202     this->m_h = rhs.m_h;
203     return *this;
204   }
205 
206   /**
207    * Move Assignment Operator.
208    *
209    * @param &rhs LPPublicKeyImpl to copy from
210    */
211   const LPPublicKeyImpl<Element> &operator=(LPPublicKeyImpl<Element> &&rhs) {
212     CryptoObject<Element>::operator=(rhs);
213     m_h = std::move(rhs.m_h);
214     return *this;
215   }
216 
217   // @Get Properties
218 
219   /**
220    * Gets the computed public key
221    * @return the public key element.
222    */
GetPublicElements()223   const std::vector<Element> &GetPublicElements() const { return this->m_h; }
224 
225   // @Set Properties
226 
227   /**
228    * Sets the public key vector of Element.
229    * @param &element is the public key Element vector to be copied.
230    */
SetPublicElements(const std::vector<Element> & element)231   void SetPublicElements(const std::vector<Element> &element) { m_h = element; }
232 
233   /**
234    * Sets the public key vector of Element.
235    * @param &&element is the public key Element vector to be moved.
236    */
SetPublicElements(std::vector<Element> && element)237   void SetPublicElements(std::vector<Element> &&element) {
238     m_h = std::move(element);
239   }
240 
241   /**
242    * Sets the public key Element at index idx.
243    * @param &element is the public key Element to be copied.
244    */
SetPublicElementAtIndex(usint idx,const Element & element)245   void SetPublicElementAtIndex(usint idx, const Element &element) {
246     m_h.insert(m_h.begin() + idx, element);
247   }
248 
249   /**
250    * Sets the public key Element at index idx.
251    * @param &&element is the public key Element to be moved.
252    */
SetPublicElementAtIndex(usint idx,Element && element)253   void SetPublicElementAtIndex(usint idx, Element &&element) {
254     m_h.insert(m_h.begin() + idx, std::move(element));
255   }
256 
257   bool operator==(const LPPublicKeyImpl &other) const {
258     if (!CryptoObject<Element>::operator==(other)) {
259       return false;
260     }
261 
262     if (m_h.size() != other.m_h.size()) {
263       return false;
264     }
265 
266     for (size_t i = 0; i < m_h.size(); i++) {
267       if (m_h[i] != other.m_h[i]) {
268         return false;
269       }
270     }
271 
272     return true;
273   }
274 
275   bool operator!=(const LPPublicKeyImpl &other) const {
276     return !(*this == other);
277   }
278 
279   template <class Archive>
save(Archive & ar,std::uint32_t const version)280   void save(Archive &ar, std::uint32_t const version) const {
281     ar(::cereal::base_class<LPKey<Element>>(this));
282     ar(::cereal::make_nvp("h", m_h));
283   }
284 
285   template <class Archive>
load(Archive & ar,std::uint32_t const version)286   void load(Archive &ar, std::uint32_t const version) {
287     if (version > SerializedVersion()) {
288       PALISADE_THROW(deserialize_error,
289                      "serialized object version " + std::to_string(version) +
290                          " is from a later version of the library");
291     }
292     ar(::cereal::base_class<LPKey<Element>>(this));
293     ar(::cereal::make_nvp("h", m_h));
294   }
295 
SerializedObjectName()296   std::string SerializedObjectName() const { return "PublicKey"; }
SerializedVersion()297   static uint32_t SerializedVersion() { return 1; }
298 
299  private:
300   std::vector<Element> m_h;
301 };
302 
303 template <typename Element>
304 class LPEvalKeyImpl;
305 
306 template <typename Element>
307 using LPEvalKey = shared_ptr<LPEvalKeyImpl<Element>>;
308 
309 /**
310  * @brief Abstract interface for LP evaluation/proxy keys
311  * @tparam Element a ring element.
312  */
313 template <class Element>
314 class LPEvalKeyImpl : public LPKey<Element> {
315  public:
316   /**
317    * Basic constructor for setting crypto params
318    *
319    * @param &cryptoParams is the reference to cryptoParams
320    */
321 
322   explicit LPEvalKeyImpl(CryptoContext<Element> cc = 0) : LPKey<Element>(cc) {}
323 
~LPEvalKeyImpl()324   virtual ~LPEvalKeyImpl() {}
325 
326   /**
327    * Setter function to store Relinearization Element Vector A.
328    * Throws exception, to be overridden by derived class.
329    *
330    * @param &a is the Element vector to be copied.
331    */
332 
SetAVector(const std::vector<Element> & a)333   virtual void SetAVector(const std::vector<Element> &a) {
334     PALISADE_THROW(not_implemented_error,
335                    "SetAVector copy operation not supported");
336   }
337 
338   /**
339    * Setter function to store Relinearization Element Vector A.
340    * Throws exception, to be overridden by derived class.
341    *
342    * @param &&a is the Element vector to be moved.
343    */
344 
SetAVector(std::vector<Element> && a)345   virtual void SetAVector(std::vector<Element> &&a) {
346     PALISADE_THROW(not_implemented_error,
347                    "SetAVector move operation not supported");
348   }
349 
350   /**
351    * Getter function to access Relinearization Element Vector A.
352    * Throws exception, to be overridden by derived class.
353    *
354    * @return Element vector A.
355    */
356 
GetAVector()357   virtual const std::vector<Element> &GetAVector() const {
358     PALISADE_THROW(not_implemented_error, "GetAVector operation not supported");
359   }
360 
361   /**
362    * Setter function to store Relinearization Element Vector B.
363    * Throws exception, to be overridden by derived class.
364    *
365    * @param &b is the Element vector to be copied.
366    */
367 
SetBVector(const std::vector<Element> & b)368   virtual void SetBVector(const std::vector<Element> &b) {
369     PALISADE_THROW(not_implemented_error,
370                    "SetBVector copy operation not supported");
371   }
372 
373   /**
374    * Setter function to store Relinearization Element Vector B.
375    * Throws exception, to be overridden by derived class.
376    *
377    * @param &&b is the Element vector to be moved.
378    */
379 
SetBVector(std::vector<Element> && b)380   virtual void SetBVector(std::vector<Element> &&b) {
381     PALISADE_THROW(not_implemented_error,
382                    "SetBVector move operation not supported");
383   }
384 
385   /**
386    * Getter function to access Relinearization Element Vector B.
387    * Throws exception, to be overridden by derived class.
388    *
389    * @return  Element vector B.
390    */
391 
GetBVector()392   virtual const std::vector<Element> &GetBVector() const {
393     PALISADE_THROW(not_implemented_error, "GetBVector operation not supported");
394   }
395 
396   /**
397    * Setter function to store key switch Element.
398    * Throws exception, to be overridden by derived class.
399    *
400    * @param &a is the Element to be copied.
401    */
402 
SetA(const Element & a)403   virtual void SetA(const Element &a) {
404     PALISADE_THROW(not_implemented_error, "SetA copy operation not supported");
405   }
406 
407   /**
408    * Setter function to store key switch Element.
409    * Throws exception, to be overridden by derived class.
410    *
411    * @param &&a is the Element to be moved.
412    */
SetA(Element && a)413   virtual void SetA(Element &&a) {
414     PALISADE_THROW(not_implemented_error, "SetA move operation not supported");
415   }
416 
417   /**
418    * Getter function to access key switch Element.
419    * Throws exception, to be overridden by derived class.
420    *
421    * @return  Element.
422    */
423 
GetA()424   virtual const Element &GetA() const {
425     PALISADE_THROW(not_implemented_error, "GetA operation not supported");
426   }
427 
428   /**
429    * Setter function to store key switch Element.
430    * Throws exception, to be overridden by derived class.
431    *
432    * @param &a is the Element to be copied.
433    */
434 
SetAinDCRT(const DCRTPoly & a)435   virtual void SetAinDCRT(const DCRTPoly &a) {
436     PALISADE_THROW(not_implemented_error,
437                    "SetAinDCRT copy operation not supported");
438   }
439 
440   /**
441    * Setter function to store key switch Element.
442    * Throws exception, to be overridden by derived class.
443    *
444    * @param &&a is the Element to be moved.
445    */
SetAinDCRT(DCRTPoly && a)446   virtual void SetAinDCRT(DCRTPoly &&a) {
447     PALISADE_THROW(not_implemented_error,
448                    "SetAinDCRT move operation not supported");
449   }
450 
451   /**
452    * Getter function to access key switch Element.
453    * Throws exception, to be overridden by derived class.
454    *
455    * @return  Element.
456    */
457 
GetAinDCRT()458   virtual const DCRTPoly &GetAinDCRT() const {
459     PALISADE_THROW(not_implemented_error, "GetAinDCRT operation not supported");
460   }
461 
462   /**
463    * Setter function to store key switch Element.
464    * Throws exception, to be overridden by derived class.
465    *
466    * @param &b is the Element to be copied.
467    */
468 
SetBinDCRT(const DCRTPoly & b)469   virtual void SetBinDCRT(const DCRTPoly &b) {
470     PALISADE_THROW(not_implemented_error,
471                    "SetAinDCRT copy operation not supported");
472   }
473 
474   /**
475    * Setter function to store key switch Element.
476    * Throws exception, to be overridden by derived class.
477    *
478    * @param &&b is the Element to be moved.
479    */
SetBinDCRT(DCRTPoly && b)480   virtual void SetBinDCRT(DCRTPoly &&b) {
481     PALISADE_THROW(not_implemented_error,
482                    "SetAinDCRT move operation not supported");
483   }
484 
485   /**
486    * Getter function to access key switch Element.
487    * Throws exception, to be overridden by derived class.
488    *
489    * @return  Element.
490    */
491 
GetBinDCRT()492   virtual const DCRTPoly &GetBinDCRT() const {
493     PALISADE_THROW(not_implemented_error, "GetAinDCRT operation not supported");
494   }
495 
ClearKeys()496   virtual void ClearKeys() {
497     PALISADE_THROW(not_implemented_error,
498                    "ClearKeys operation is not supported");
499   }
500 
501   friend bool operator==(const LPEvalKeyImpl &a, const LPEvalKeyImpl &b) {
502     return a.key_compare(b);
503   }
504 
505   friend bool operator!=(const LPEvalKeyImpl &a, LPEvalKeyImpl &b) {
506     return !(a == b);
507   }
508 
key_compare(const LPEvalKeyImpl & other)509   virtual bool key_compare(const LPEvalKeyImpl &other) const { return false; }
510 
511   template <class Archive>
save(Archive & ar,std::uint32_t const version)512   void save(Archive &ar, std::uint32_t const version) const {
513     ar(::cereal::base_class<LPKey<Element>>(this));
514   }
515 
516   template <class Archive>
load(Archive & ar,std::uint32_t const version)517   void load(Archive &ar, std::uint32_t const version) {
518     ar(::cereal::base_class<LPKey<Element>>(this));
519   }
SerializedObjectName()520   std::string SerializedObjectName() const { return "EvalKey"; }
521 };
522 
523 template <typename Element>
524 class LPEvalKeyRelinImpl;
525 
526 template <typename Element>
527 using LPEvalKeyRelin = shared_ptr<LPEvalKeyRelinImpl<Element>>;
528 
529 /**
530  * @brief Concrete class for Relinearization keys of RLWE scheme
531  * @tparam Element a ring element.
532  */
533 template <class Element>
534 class LPEvalKeyRelinImpl : public LPEvalKeyImpl<Element> {
535  public:
536   /**
537    * Basic constructor for setting crypto params
538    *
539    * @param &cryptoParams is the reference to cryptoParams
540    */
541   explicit LPEvalKeyRelinImpl(CryptoContext<Element> cc = 0)
542       : LPEvalKeyImpl<Element>(cc) {}
543 
~LPEvalKeyRelinImpl()544   virtual ~LPEvalKeyRelinImpl() {}
545 
546   /**
547    * Copy constructor
548    *
549    *@param &rhs key to copy from
550    */
LPEvalKeyRelinImpl(const LPEvalKeyRelinImpl<Element> & rhs)551   explicit LPEvalKeyRelinImpl(const LPEvalKeyRelinImpl<Element> &rhs)
552       : LPEvalKeyImpl<Element>(rhs.GetCryptoContext()) {
553     m_rKey = rhs.m_rKey;
554   }
555 
556   /**
557    * Move constructor
558    *
559    *@param &rhs key to move from
560    */
LPEvalKeyRelinImpl(LPEvalKeyRelinImpl<Element> && rhs)561   explicit LPEvalKeyRelinImpl(LPEvalKeyRelinImpl<Element> &&rhs)
562       : LPEvalKeyImpl<Element>(rhs.GetCryptoContext()) {
563     m_rKey = std::move(rhs.m_rKey);
564   }
565 
566   operator bool() const {
567     return static_cast<bool>(this->context) && m_rKey.size() != 0;
568   }
569 
570   /**
571    * Assignment Operator.
572    *
573    * @param &rhs key to copy from
574    */
575   const LPEvalKeyRelinImpl<Element> &operator=(
576       const LPEvalKeyRelinImpl<Element> &rhs) {
577     this->context = rhs.context;
578     this->m_rKey = rhs.m_rKey;
579     return *this;
580   }
581 
582   /**
583    * Move Assignment Operator.
584    *
585    * @param &rhs key to move from
586    */
587   const LPEvalKeyRelinImpl<Element> &operator=(
588       LPEvalKeyRelinImpl<Element> &&rhs) {
589     this->context = rhs.context;
590     rhs.context = 0;
591     m_rKey = std::move(rhs.m_rKey);
592     return *this;
593   }
594 
595   /**
596    * Setter function to store Relinearization Element Vector A.
597    * Overrides base class implementation.
598    *
599    * @param &a is the Element vector to be copied.
600    */
SetAVector(const std::vector<Element> & a)601   virtual void SetAVector(const std::vector<Element> &a) {
602     m_rKey.insert(m_rKey.begin() + 0, a);
603   }
604 
605   /**
606    * Setter function to store Relinearization Element Vector A.
607    * Overrides base class implementation.
608    *
609    * @param &&a is the Element vector to be moved.
610    */
SetAVector(std::vector<Element> && a)611   virtual void SetAVector(std::vector<Element> &&a) {
612     m_rKey.insert(m_rKey.begin() + 0, std::move(a));
613   }
614 
615   /**
616    * Getter function to access Relinearization Element Vector A.
617    * Overrides base class implementation.
618    *
619    * @return Element vector A.
620    */
GetAVector()621   virtual const std::vector<Element> &GetAVector() const {
622     return m_rKey.at(0);
623   }
624 
625   /**
626    * Setter function to store Relinearization Element Vector B.
627    * Overrides base class implementation.
628    *
629    * @param &b is the Element vector to be copied.
630    */
SetBVector(const std::vector<Element> & b)631   virtual void SetBVector(const std::vector<Element> &b) {
632     m_rKey.insert(m_rKey.begin() + 1, b);
633   }
634 
635   /**
636    * Setter function to store Relinearization Element Vector B.
637    * Overrides base class implementation.
638    *
639    * @param &&b is the Element vector to be moved.
640    */
SetBVector(std::vector<Element> && b)641   virtual void SetBVector(std::vector<Element> &&b) {
642     m_rKey.insert(m_rKey.begin() + 1, std::move(b));
643   }
644 
645   /**
646    * Getter function to access Relinearization Element Vector B.
647    * Overrides base class implementation.
648    *
649    * @return Element vector B.
650    */
GetBVector()651   virtual const std::vector<Element> &GetBVector() const {
652     return m_rKey.at(1);
653   }
654 
655   /**
656    * Setter function to store key switch Element.
657    * Throws exception, to be overridden by derived class.
658    *
659    * @param &a is the Element to be copied.
660    */
661 
SetAinDCRT(const DCRTPoly & a)662   virtual void SetAinDCRT(const DCRTPoly &a) {
663     m_dcrtKeys.insert(m_dcrtKeys.begin() + 0, a);
664   }
665 
666   /**
667    * Setter function to store key switch Element.
668    * Throws exception, to be overridden by derived class.
669    *
670    * @param &&a is the Element to be moved.
671    */
SetAinDCRT(DCRTPoly && a)672   virtual void SetAinDCRT(DCRTPoly &&a) {
673     m_dcrtKeys.insert(m_dcrtKeys.begin() + 0, std::move(a));
674   }
675 
676   /**
677    * Getter function to access key switch Element.
678    * Throws exception, to be overridden by derived class.
679    *
680    * @return  Element.
681    */
682 
GetAinDCRT()683   virtual const DCRTPoly &GetAinDCRT() const { return m_dcrtKeys.at(0); }
684 
685   /**
686    * Setter function to store key switch Element.
687    * Throws exception, to be overridden by derived class.
688    *
689    * @param &b is the Element to be copied.
690    */
691 
SetBinDCRT(const DCRTPoly & b)692   virtual void SetBinDCRT(const DCRTPoly &b) {
693     m_dcrtKeys.insert(m_dcrtKeys.begin() + 1, b);
694   }
695 
696   /**
697    * Setter function to store key switch Element.
698    * Throws exception, to be overridden by derived class.
699    *
700    * @param &&b is the Element to be moved.
701    */
SetBinDCRT(DCRTPoly && b)702   virtual void SetBinDCRT(DCRTPoly &&b) {
703     m_dcrtKeys.insert(m_dcrtKeys.begin() + 1, std::move(b));
704   }
705 
706   /**
707    * Getter function to access key switch Element.
708    * Throws exception, to be overridden by derived class.
709    *
710    * @return  Element.
711    */
712 
GetBinDCRT()713   virtual const DCRTPoly &GetBinDCRT() const { return m_dcrtKeys.at(1); }
714 
ClearKeys()715   virtual void ClearKeys() {
716     m_rKey.clear();
717     m_dcrtKeys.clear();
718   }
719 
720 
key_compare(const LPEvalKeyImpl<Element> & other)721   bool key_compare(const LPEvalKeyImpl<Element> &other) const {
722     const auto &oth = static_cast<const LPEvalKeyRelinImpl<Element> &>(other);
723 
724     if (!CryptoObject<Element>::operator==(other)) return false;
725 
726     if (this->m_rKey.size() != oth.m_rKey.size()) return false;
727     for (size_t i = 0; i < this->m_rKey.size(); i++) {
728       if (this->m_rKey[i].size() != oth.m_rKey[i].size()) return false;
729       for (size_t j = 0; j < this->m_rKey[i].size(); j++) {
730         if (this->m_rKey[i][j] != oth.m_rKey[i][j]) return false;
731       }
732     }
733     return true;
734   }
735 
736   template <class Archive>
save(Archive & ar,std::uint32_t const version)737   void save(Archive &ar, std::uint32_t const version) const {
738     ar(::cereal::base_class<LPEvalKeyImpl<Element>>(this));
739     ar(::cereal::make_nvp("k", m_rKey));
740   }
741 
742   template <class Archive>
load(Archive & ar,std::uint32_t const version)743   void load(Archive &ar, std::uint32_t const version) {
744     if (version > SerializedVersion()) {
745       PALISADE_THROW(deserialize_error,
746                      "serialized object version " + std::to_string(version) +
747                          " is from a later version of the library");
748     }
749     ar(::cereal::base_class<LPEvalKeyImpl<Element>>(this));
750     ar(::cereal::make_nvp("k", m_rKey));
751   }
SerializedObjectName()752   std::string SerializedObjectName() const { return "EvalKeyRelin"; }
SerializedVersion()753   static uint32_t SerializedVersion() { return 1; }
754 
755  private:
756   // private member to store vector of vector of Element.
757   std::vector<std::vector<Element>> m_rKey;
758 
759   // Used for GHS key switching
760   std::vector<DCRTPoly> m_dcrtKeys;
761 };
762 
763 template <typename Element>
764 class LPPrivateKeyImpl;
765 
766 template <typename Element>
767 using LPPrivateKey = shared_ptr<LPPrivateKeyImpl<Element>>;
768 
769 /**
770  * @brief Class fpr LP Private keys
771  * @tparam Element a ring element.
772  */
773 template <class Element>
774 class LPPrivateKeyImpl : public LPKey<Element> {
775  public:
776   /**
777    * Construct in context
778    */
779 
780   explicit LPPrivateKeyImpl(CryptoContext<Element> cc = 0)
781       : LPKey<Element>(cc, GenerateUniqueKeyID()) {}
782 
783   /**
784    * Copy constructor
785    *@param &rhs the LPPrivateKeyImpl to copy from
786    */
LPPrivateKeyImpl(const LPPrivateKeyImpl<Element> & rhs)787   explicit LPPrivateKeyImpl(const LPPrivateKeyImpl<Element> &rhs)
788       : LPKey<Element>(rhs.GetCryptoContext(), rhs.GetKeyTag()) {
789     this->m_sk = rhs.m_sk;
790   }
791 
792   /**
793    * Move constructor
794    *@param &rhs the LPPrivateKeyImpl to move from
795    */
LPPrivateKeyImpl(LPPrivateKeyImpl<Element> && rhs)796   explicit LPPrivateKeyImpl(LPPrivateKeyImpl<Element> &&rhs)
797       : LPKey<Element>(rhs.GetCryptoContext(), rhs.GetKeyTag()) {
798     this->m_sk = std::move(rhs.m_sk);
799   }
800 
801   operator bool() const { return static_cast<bool>(this->context); }
802 
803   /**
804    * Assignment Operator.
805    *
806    * @param &rhs LPPrivateKeyto assign from.
807    * @return the resulting LPPrivateKeyImpl
808    */
809   const LPPrivateKeyImpl<Element> &operator=(
810       const LPPrivateKeyImpl<Element> &rhs) {
811     CryptoObject<Element>::operator=(rhs);
812     this->m_sk = rhs.m_sk;
813     return *this;
814   }
815 
816   /**
817    * Move Assignment Operator.
818    *
819    * @param &rhs LPPrivateKeyImpl to assign from.
820    * @return the resulting LPPrivateKeyImpl
821    */
822   const LPPrivateKeyImpl<Element> &operator=(LPPrivateKeyImpl<Element> &&rhs) {
823     CryptoObject<Element>::operator=(rhs);
824     this->m_sk = std::move(rhs.m_sk);
825     return *this;
826   }
827 
828   /**
829    * Implementation of the Get accessor for private element.
830    * @return the private element.
831    */
GetPrivateElement()832   const Element &GetPrivateElement() const { return m_sk; }
833 
834   /**
835    * Set accessor for private element.
836    * @private &x private element to set to.
837    */
SetPrivateElement(const Element & x)838   void SetPrivateElement(const Element &x) { m_sk = x; }
839 
840   /**
841    * Set accessor for private element.
842    * @private &x private element to set to.
843    */
SetPrivateElement(Element && x)844   void SetPrivateElement(Element &&x) { m_sk = std::move(x); }
845 
846   bool operator==(const LPPrivateKeyImpl &other) const {
847     return CryptoObject<Element>::operator==(other) && m_sk == other.m_sk;
848   }
849 
850   bool operator!=(const LPPrivateKeyImpl &other) const {
851     return !(*this == other);
852   }
853 
854   template <class Archive>
save(Archive & ar,std::uint32_t const version)855   void save(Archive &ar, std::uint32_t const version) const {
856     ar(::cereal::base_class<LPKey<Element>>(this));
857     ar(::cereal::make_nvp("s", m_sk));
858   }
859 
860   template <class Archive>
load(Archive & ar,std::uint32_t const version)861   void load(Archive &ar, std::uint32_t const version) {
862     if (version > SerializedVersion()) {
863       PALISADE_THROW(deserialize_error,
864                      "serialized object version " + std::to_string(version) +
865                          " is from a later version of the library");
866     }
867     ar(::cereal::base_class<LPKey<Element>>(this));
868     ar(::cereal::make_nvp("s", m_sk));
869   }
870 
SerializedObjectName()871   std::string SerializedObjectName() const { return "PrivateKey"; }
SerializedVersion()872   static uint32_t SerializedVersion() { return 1; }
873 
874  private:
875   Element m_sk;
876 };
877 
878 template <class Element>
879 class LPKeyPair {
880  public:
881   LPPublicKey<Element> publicKey;
882   LPPrivateKey<Element> secretKey;
883 
LPKeyPair(LPPublicKey<Element> a,LPPrivateKey<Element> b)884   LPKeyPair(LPPublicKey<Element> a, LPPrivateKey<Element> b)
885       : publicKey(a), secretKey(b) {}
886 
887   LPKeyPair(LPPublicKeyImpl<Element> *a = nullptr,
888             LPPrivateKeyImpl<Element> *b = nullptr)
publicKey(a)889       : publicKey(a), secretKey(b) {}
890 
good()891   bool good() { return publicKey && secretKey; }
892 };
893 
894 /**
895  * @brief Abstract interface for parameter generation algorithm
896  * @tparam Element a ring element.
897  */
898 template <class Element>
899 class LPParameterGenerationAlgorithm {
900  public:
~LPParameterGenerationAlgorithm()901   virtual ~LPParameterGenerationAlgorithm() {}
902 
903   /**
904    * Method for computing all derived parameters based on chosen primitive
905    * parameters
906    *
907    * @param *cryptoParams the crypto parameters object to be populated with
908    * parameters.
909    * @param evalAddCount number of EvalAdds assuming no EvalMult and KeySwitch
910    * operations are performed.
911    * @param evalMultCount number of EvalMults assuming no EvalAdd and
912    * KeySwitch operations are performed.
913    * @param keySwitchCount number of KeySwitch operations assuming no EvalAdd
914    * and EvalMult operations are performed.
915    * @param dcrtBits number of bits in each CRT modulus*
916    * @param n ring dimension in case the user wants to use a custom ring
917    * dimension
918    */
919   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
920                          int32_t evalAddCount = 0, int32_t evalMultCount = 0,
921                          int32_t keySwitchCount = 0, size_t dcrtBits = 0,
922                          uint32_t n = 0) const = 0;
923 
924   /**
925    * Method for computing all derived parameters based on chosen primitive
926    * parameters. This is intended for CKKS and DCRTPoly.
927    *
928    * @param *cryptoParams the crypto parameters object to be populated with
929    * parameters.
930    * @param cyclOrder the cyclotomic order.
931    * @param numPrimes number of modulus towers to support.
932    * @param scaleExp the bit-width for plaintexts and DCRTPoly's.
933    * @param relinWindow the relinearization window
934    * @param mode
935    * @param ksTech the key switching technique used (e.g., BV or GHS)
936    * @param firstModSize the bit-size of the first modulus
937    * @param rsTech the rescaling technique used (e.g., APPROXRESCALE or
938    * EXACTRESCALE)
939    */
ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,usint cyclOrder,usint numPrimes,usint scaleExp,usint relinWindow,MODE mode,KeySwitchTechnique ksTech,usint firstModSize,RescalingTechnique rsTech)940   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
941                          usint cyclOrder, usint numPrimes, usint scaleExp,
942                          usint relinWindow, MODE mode,
943                          KeySwitchTechnique ksTech, usint firstModSize,
944                          RescalingTechnique rsTech) const {
945     PALISADE_THROW(
946         config_error,
947         "This signature for ParamsGen is not supported for this scheme.");
948   }
949 
950   /**
951    * Method for computing all derived parameters based on chosen primitive
952    * parameters.
953    *
954    * @param *cryptoParams the crypto parameters object to be populated with
955    * parameters.
956    * @param cyclOrder the cyclotomic order.
957    * @param numPrimes number of modulus towers to support.
958    * @param scaleExp the bit-width for plaintexts and DCRTPoly's.
959    * @param relinWindow the relinearization window
960    * @param mode
961    * @param ksTech the key switching technique used (e.g., BV or GHS)
962    * @param firstModSize the bit-size of the first modulus
963    * @param rsTech the rescaling technique used (e.g., APPROXRESCALE or
964    * EXACTRESCALE)
965    */
966   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
967                          usint cyclOrder, usint numPrimes, usint scaleExp,
968                          usint relinWindow, MODE mode,
969                          KeySwitchTechnique ksTech = BV,
970                          usint firstModSize = 60,
971                          RescalingTechnique = APPROXRESCALE,
972                          uint32_t numLargeDigits = 4) const {
973     PALISADE_THROW(
974         config_error,
975         "This signature for ParamsGen is not supported for this scheme.");
976   }
977 
978   /**
979    * Method for computing all derived parameters based on chosen primitive
980    * parameters. This is intended for BGVrns
981    * @param *cryptoParams the crypto parameters object to be populated with
982    * parameters.
983    * @param cyclOrder the cyclotomic order.
984    * @param numPrimes number of modulus towers to support.
985    * @param relinWindow the relinearization window
986    * @param mode
987    * @param ksTech the key switching technique used (e.g., BV or GHS)
988    * @param firstModSize the bit-size of the first modulus
989    * @param dcrtBits the bit-width of moduli.
990    */
991   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
992                          usint cyclOrder, usint ptm, usint numPrimes,
993                          usint relinWindow, MODE mode,
994                          KeySwitchTechnique ksTech = BV,
995                          usint firstModSize = 60, usint dcrtBits = 60,
996                          uint32_t numLargeDigits = 4) const {
997     PALISADE_THROW(
998         not_implemented_error,
999         "This signature for ParamsGen is not supported for this scheme.");
1000   }
1001 
1002   template <class Archive>
save(Archive & ar,std::uint32_t const version)1003   void save(Archive &ar, std::uint32_t const version) const {}
1004 
1005   template <class Archive>
load(Archive & ar,std::uint32_t const version)1006   void load(Archive &ar, std::uint32_t const version) {}
1007 
SerializedObjectName()1008   std::string SerializedObjectName() const { return "ParamsGen"; }
1009 };
1010 
1011 /**
1012  * @brief Abstract interface for encryption algorithm
1013  * @tparam Element a ring element.
1014  */
1015 template <class Element>
1016 class LPEncryptionAlgorithm {
1017  public:
~LPEncryptionAlgorithm()1018   virtual ~LPEncryptionAlgorithm() {}
1019 
1020   /**
1021    * Method for encrypting plaintext using LBC
1022    *
1023    * @param&publicKey public key used for encryption.
1024    * @param plaintext copy of the plaintext element. NOTE a copy is passed!
1025    * That is NOT an error!
1026    * @param doEncryption encrypts if true, embeds (encodes) the plaintext into
1027    * cryptocontext if false
1028    * @param *ciphertext ciphertext which results from encryption.
1029    */
1030   virtual Ciphertext<Element> Encrypt(const LPPublicKey<Element> publicKey,
1031                                       Element plaintext) const = 0;
1032 
1033   /**
1034    * Method for encrypting plaintex using LBC
1035    *
1036    * @param privateKey private key used for encryption.
1037    * @param plaintext copy of the plaintext input. NOTE a copy is passed! That
1038    * is NOT an error!
1039    * @param doEncryption encrypts if true, embeds (encodes) the plaintext into
1040    * cryptocontext if false
1041    * @param *ciphertext ciphertext which results from encryption.
1042    */
1043   virtual Ciphertext<Element> Encrypt(const LPPrivateKey<Element> privateKey,
1044                                       Element plaintext) const = 0;
1045 
1046   /**
1047    * Method for decrypting plaintext using LBC
1048    *
1049    * @param &privateKey private key used for decryption.
1050    * @param &ciphertext ciphertext id decrypted.
1051    * @param *plaintext the plaintext output.
1052    * @return the decoding result.
1053    */
1054   virtual DecryptResult Decrypt(const LPPrivateKey<Element> privateKey,
1055                                 ConstCiphertext<Element> ciphertext,
1056                                 NativePoly *plaintext) const = 0;
1057 
1058   /**
1059    * Method for decrypting plaintext using LBC
1060    *
1061    * @param &privateKey private key used for decryption.
1062    * @param &ciphertext ciphertext id decrypted.
1063    * @param *plaintext the plaintext output.
1064    * @return the decoding result.
1065    */
Decrypt(const LPPrivateKey<Element> privateKey,ConstCiphertext<Element> ciphertext,Poly * plaintext)1066   virtual DecryptResult Decrypt(const LPPrivateKey<Element> privateKey,
1067                                 ConstCiphertext<Element> ciphertext,
1068                                 Poly *plaintext) const {
1069     PALISADE_THROW(config_error, "Decryption to Poly is not supported");
1070   }
1071 
1072   /**
1073    * Function to generate public and private keys
1074    *
1075    * @param &publicKey private key used for decryption.
1076    * @param &privateKey private key used for decryption.
1077    * @return function ran correctly.
1078    */
1079   virtual LPKeyPair<Element> KeyGen(CryptoContext<Element> cc,
1080                                     bool makeSparse = false) = 0;
1081 };
1082 
1083 /**
1084  * @brief Abstract interface for Leveled SHE operations
1085  * @tparam Element a ring element.
1086  */
1087 template <class Element>
1088 class LPLeveledSHEAlgorithm {
1089  public:
~LPLeveledSHEAlgorithm()1090   virtual ~LPLeveledSHEAlgorithm() {}
1091 
1092   /**
1093    * Method for In-place Modulus Reduction.
1094    *
1095    * @param &cipherText Ciphertext to perform mod reduce on.
1096    * @param levels the number of towers to drop.
1097    */
1098   virtual void ModReduceInPlace(Ciphertext<Element> &ciphertext,
1099                                 size_t levels = 1) const = 0;
1100 
1101   /**
1102    * Method for Modulus Reduction.
1103    *
1104    * @param &cipherText Ciphertext to perform mod reduce on.
1105    * @param levels the number of towers to drop.
1106    */
1107   virtual Ciphertext<Element> ModReduce(ConstCiphertext<Element> ciphertext,
1108                                         size_t levels = 1) const {
1109     auto rv = ciphertext->Clone();
1110     ModReduceInPlace(rv, levels);
1111     return rv;
1112   }
1113 
1114   /**
1115    * Method for rescaling.
1116    *
1117    * @param cipherText is the ciphertext to perform modreduce on.
1118    * @param levels the number of towers to drop.
1119    * @return ciphertext after the modulus reduction performed.
1120    */
1121   virtual Ciphertext<Element> ModReduceInternal(
1122       ConstCiphertext<Element> ciphertext, size_t levels = 1) const {
1123     PALISADE_THROW(config_error,
1124                    "ModReduceInternal is not supported for this scheme");
1125   }
1126 
1127   /**
1128    * Method for rescaling in-place.
1129    *
1130    * @param cipherText is the ciphertext to perform modreduce on.
1131    * @param levels the number of towers to drop.
1132    * @details \p cipherText will have modulus reduction performed in-place.
1133    */
1134   virtual void ModReduceInternalInPlace(Ciphertext<Element> &ciphertext,
1135                                         size_t levels = 1) const {
1136     PALISADE_THROW(config_error,
1137                    "ModReduceInternalInPlace is not supported for this scheme");
1138   }
1139 
1140   virtual Ciphertext<Element> Compress(ConstCiphertext<Element> ciphertext,
1141                                        size_t towersLeft = 1) const {
1142     PALISADE_THROW(config_error, "Compress is not supported for this scheme");
1143   }
1144 
1145   /**
1146    * Method for Composed EvalMult
1147    *
1148    * @param &cipherText1 ciphertext1, first input ciphertext to perform
1149    * multiplication on.
1150    * @param &cipherText2 cipherText2, second input ciphertext to perform
1151    * multiplication on.
1152    * @param &quadKeySwitchHint is for resultant quadratic secret key after
1153    * multiplication to the secret key of the particular level.
1154    * @param &cipherTextResult is the resulting ciphertext that can be
1155    * decrypted with the secret key of the particular level.
1156    */
1157   virtual Ciphertext<Element> ComposedEvalMult(
1158       ConstCiphertext<Element> cipherText1,
1159       ConstCiphertext<Element> cipherText2,
1160       const LPEvalKey<Element> quadKeySwitchHint) const = 0;
1161 
1162   /**
1163    * Method for Level Reduction from sk -> sk1. This method peforms a
1164    * keyswitch on the ciphertext and then performs a modulus reduction.
1165    *
1166    * @param &cipherText1 is the original ciphertext to be key switched and mod
1167    * reduced.
1168    * @param &linearKeySwitchHint is the linear key switch hint to perform the
1169    * key switch operation.
1170    * @param &cipherTextResult is the resulting ciphertext.
1171    */
1172   virtual Ciphertext<Element> LevelReduce(
1173       ConstCiphertext<Element> cipherText1,
1174       const LPEvalKey<Element> linearKeySwitchHint, size_t levels) const = 0;
1175 
1176   /**
1177    * Method for Level Reduction in the CKKS scheme. It just drops "levels"
1178    * number of the towers of the ciphertext without changing the underlying
1179    * plaintext.
1180    *
1181    * @param cipherText1 is the original ciphertext to be level reduced.
1182    * @param linearKeySwitchHint not used in the CKKS scheme.
1183    * @param levels the number of towers to drop.
1184    * @return resulting ciphertext.
1185    */
LevelReduceInternal(ConstCiphertext<Element> cipherText1,const LPEvalKey<Element> linearKeySwitchHint,size_t levels)1186   virtual Ciphertext<Element> LevelReduceInternal(
1187       ConstCiphertext<Element> cipherText1,
1188       const LPEvalKey<Element> linearKeySwitchHint, size_t levels) const {
1189     PALISADE_THROW(config_error,
1190                    "LevelReduceInternal is not supported for this scheme");
1191   }
1192 
1193   /**
1194    * Method for in-place Level Reduction in the CKKS scheme. It just drops
1195    * "levels" number of the towers of the ciphertext without changing the
1196    * underlying plaintext.
1197    *
1198    * @param cipherText1 is the ciphertext to be level reduced in-place
1199    * @param linearKeySwitchHint not used in the CKKS scheme.
1200    * @param levels the number of towers to drop.
1201    */
LevelReduceInternalInPlace(Ciphertext<Element> & cipherText1,const LPEvalKey<Element> linearKeySwitchHint,size_t levels)1202   virtual void LevelReduceInternalInPlace(
1203       Ciphertext<Element> &cipherText1,
1204       const LPEvalKey<Element> linearKeySwitchHint, size_t levels) const {
1205     PALISADE_THROW(
1206         config_error,
1207         "LevelReduceInternalInPlace is not supported for this scheme");
1208   }
1209 
1210   /**
1211    * Method for polynomial evaluation for polynomials represented as power
1212    * series.
1213    *
1214    * @param &cipherText input ciphertext
1215    * @param &coefficients is the vector of coefficients in the polynomial; the
1216    * size of the vector is the degree of the polynomial + 1
1217    * @return the result of polynomial evaluation.
1218    */
EvalPoly(ConstCiphertext<Element> cipherText,const std::vector<double> & coefficients)1219   virtual Ciphertext<Element> EvalPoly(
1220       ConstCiphertext<Element> cipherText,
1221       const std::vector<double> &coefficients) const {
1222     PALISADE_THROW(config_error, "EvalPoly is not supported for the scheme.");
1223   }
1224 
1225   template <class Archive>
save(Archive & ar,std::uint32_t const version)1226   void save(Archive &ar, std::uint32_t const version) const {}
1227 
1228   template <class Archive>
load(Archive & ar,std::uint32_t const version)1229   void load(Archive &ar, std::uint32_t const version) {}
1230 
SerializedObjectName()1231   std::string SerializedObjectName() const { return "LeveledSHE"; }
1232 };
1233 
1234 /**
1235  * @brief Abstract interface class for LBC PRE algorithms
1236  * @tparam Element a ring element.
1237  */
1238 template <class Element>
1239 class LPPREAlgorithm {
1240  public:
~LPPREAlgorithm()1241   virtual ~LPPREAlgorithm() {}
1242 
1243   /**
1244    * Virtual function to generate 1..log(q) encryptions for each bit of the
1245    * original private key Variant that uses the public key for the new secret
1246    * key.
1247    *
1248    * @param &newKey public key for the new secret key.
1249    * @param &origPrivateKey original private key used for decryption.
1250    * @param *evalKey the evaluation key.
1251    * @return the re-encryption key.
1252    */
1253   virtual LPEvalKey<Element> ReKeyGen(
1254       const LPPublicKey<Element> newKey,
1255       const LPPrivateKey<Element> origPrivateKey) const = 0;
1256 
1257   /**
1258    * Virtual function to define the interface for re-encypting ciphertext
1259    * using the array generated by ProxyGen
1260    *
1261    * @param &evalKey proxy re-encryption key.
1262    * @param &ciphertext the input ciphertext.
1263    * @param publicKey the public key of the recipient of the re-encrypted
1264    * ciphertext.
1265    * @param *newCiphertext the new ciphertext.
1266    */
1267   virtual Ciphertext<Element> ReEncrypt(
1268       const LPEvalKey<Element> evalKey, ConstCiphertext<Element> ciphertext,
1269       const LPPublicKey<Element> publicKey = nullptr) const = 0;
1270 };
1271 
1272 /**
1273  * @brief Abstract interface class for LBC Multiparty algorithms based on
1274  * threshold FHE.  A version of this multiparty scheme built on the BGV scheme
1275  * is seen here:
1276  *   - Asharov G., Jain A., López-Alt A., Tromer E., Vaikuntanathan V., Wichs
1277  * D. (2012) Multiparty Computation with Low Communication, Computation and
1278  * Interaction via Threshold FHE. In: Pointcheval D., Johansson T. (eds)
1279  * Advances in Cryptology – EUROCRYPT 2012. EUROCRYPT 2012. Lecture Notes in
1280  * Computer Science, vol 7237. Springer, Berlin, Heidelberg
1281  *
1282  * During offline key generation, this multiparty scheme relies on the clients
1283  * coordinating their public key generation.  To do this, a single client
1284  * generates a public-secret key pair. This public key is shared with other
1285  * keys which use an element in the public key to generate their own public
1286  * keys. The clients generate a shared key pair using a scheme-specific
1287  * approach, then generate re-encryption keys.  Re-encryption keys are
1288  * uploaded to the server. Clients encrypt data with their public keys and
1289  * send the encrypted data server. The data is re-encrypted.  Computations are
1290  * then run on the data. The result is sent to each of the clients. One client
1291  * runs a "Leader" multiparty decryption operation with its own secret key.
1292  * All other clients run a regular "Main" multiparty decryption with their own
1293  * secret key. The resulting partially decrypted ciphertext are then fully
1294  * decrypted with the decryption fusion algorithms.
1295  *
1296  * @tparam Element a ring element.
1297  */
1298 template <class Element>
1299 class LPMultipartyAlgorithm {
1300  public:
~LPMultipartyAlgorithm()1301   virtual ~LPMultipartyAlgorithm() {}
1302 
1303   /**
1304    * Threshold FHE: Generation of a public key derived
1305    * from a previous joined public key (for prior secret shares) and the secret
1306    * key share of the current party.
1307    *
1308    * @param cc cryptocontext for the keys to be generated.
1309    * @param pk1 joined public key from prior parties.
1310    * @param makeSparse set to true if ring reduce by a factor of 2 is to be
1311    * used. NOT SUPPORTED BY ANY SCHEME ANYMORE.
1312    * @param fresh set to true if proxy re-encryption is used in the multi-party
1313    * protocol or star topology is used
1314    * @return key pair including the secret share for the current party and
1315    * joined public key
1316    */
1317   virtual LPKeyPair<Element> MultipartyKeyGen(CryptoContext<Element> cc,
1318                                               const LPPublicKey<Element> pk1,
1319                                               bool makeSparse = false,
1320                                               bool fresh = false) = 0;
1321 
1322   /**
1323    * Threshold FHE: Generates a public key from a vector of secret shares.
1324    * ONLY FOR DEBUGGIN PURPOSES. SHOULD NOT BE USED IN PRODUCTION.
1325    *
1326    * @param cc cryptocontext for the keys to be generated.
1327    * @param secretkeys secrete key shares.
1328    * @param makeSparse set to true if ring reduce by a factor of 2 is to be
1329    * used. NOT SUPPORTED BY ANY SCHEME ANYMORE.
1330    * @return key pair including the private for the current party and joined
1331    * public key
1332    */
1333   virtual LPKeyPair<Element> MultipartyKeyGen(
1334       CryptoContext<Element> cc,
1335       const vector<LPPrivateKey<Element>> &secretKeys,
1336       bool makeSparse = false) = 0;
1337 
1338   /**
1339    * Threshold FHE: "Partial" decryption computed by all parties except for the
1340    * lead one
1341    *
1342    * @param privateKey secret key share used for decryption.
1343    * @param ciphertext ciphertext that is being decrypted.
1344    */
1345   virtual Ciphertext<Element> MultipartyDecryptMain(
1346       const LPPrivateKey<Element> privateKey,
1347       ConstCiphertext<Element> ciphertext) const = 0;
1348 
1349   /**
1350    * Threshold FHE: Method for decryption operation run by the lead decryption
1351    * client
1352    *
1353    * @param privateKey secret key share used for decryption.
1354    * @param ciphertext ciphertext id decrypted.
1355    */
1356   virtual Ciphertext<Element> MultipartyDecryptLead(
1357       const LPPrivateKey<Element> privateKey,
1358       ConstCiphertext<Element> ciphertext) const = 0;
1359 
1360   /**
1361    * Threshold FHE: Method for combining the partially decrypted ciphertexts
1362    * and getting the final decryption in the clear as a NativePoly.
1363    *
1364    * @param &ciphertextVec vector of "partial" decryptions.
1365    * @param *plaintext the plaintext output as a NativePoly.
1366    * @return the decoding result.
1367    */
1368   virtual DecryptResult MultipartyDecryptFusion(
1369       const vector<Ciphertext<Element>> &ciphertextVec,
1370       NativePoly *plaintext) const = 0;
1371 
1372   /**
1373    * Threshold FHE: Method for combining the partially decrypted ciphertexts
1374    * and getting the final decryption in the clear as a Poly.
1375    *
1376    * @param &ciphertextVec vector of "partial" decryptions.
1377    * @param *plaintext the plaintext output as a Poly.
1378    * @return the decoding result.
1379    */
MultipartyDecryptFusion(const vector<Ciphertext<Element>> & ciphertextVec,Poly * plaintext)1380   virtual DecryptResult MultipartyDecryptFusion(
1381       const vector<Ciphertext<Element>> &ciphertextVec, Poly *plaintext) const {
1382     PALISADE_THROW(config_error, "Decryption to Poly is not supported");
1383   }
1384 
1385   /**
1386    * Threshold FHE: Generates a joined evaluation key
1387    * from the current secret share and a prior joined
1388    * evaluation key
1389    *
1390    * @param originalPrivateKey secret key transformed from.
1391    * @param newPrivateKey secret key transformed to.
1392    * @param ek the prior joined evaluation key.
1393    * @return the new joined evaluation key.
1394    */
MultiKeySwitchGen(const LPPrivateKey<Element> originalPrivateKey,const LPPrivateKey<Element> newPrivateKey,const LPEvalKey<Element> ek)1395   virtual LPEvalKey<Element> MultiKeySwitchGen(
1396       const LPPrivateKey<Element> originalPrivateKey,
1397       const LPPrivateKey<Element> newPrivateKey,
1398       const LPEvalKey<Element> ek) const {
1399     PALISADE_THROW(not_implemented_error,
1400                    "MultiKeySwitchGen multi-party capability is not supported "
1401                    "for this scheme");
1402   }
1403 
1404   /**
1405    * Threshold FHE: Generates joined automorphism keys
1406    * from the current secret share and prior joined
1407    * automorphism keys
1408    *
1409    * @param privateKey secret key share.
1410    * @param eAuto a dictionary with prior joined automorphism keys.
1411    * @param &indexList a vector of automorphism indices.
1412    * @return a dictionary with new joined automorphism keys.
1413    */
1414   virtual std::shared_ptr<std::map<usint, LPEvalKey<Element>>>
MultiEvalAutomorphismKeyGen(const LPPrivateKey<Element> privateKey,const shared_ptr<std::map<usint,LPEvalKey<Element>>> eAuto,const std::vector<usint> & indexList)1415   MultiEvalAutomorphismKeyGen(
1416       const LPPrivateKey<Element> privateKey,
1417       const shared_ptr<std::map<usint, LPEvalKey<Element>>> eAuto,
1418       const std::vector<usint> &indexList) const {
1419     PALISADE_THROW(not_implemented_error,
1420                    "MultiEvalAutomorphismKeyGen multi-party capability is not "
1421                    "supported for this scheme");
1422   }
1423 
1424   /**
1425    * Threshold FHE: Generates joined summation evaluation keys
1426    * from the current secret share and prior joined
1427    * summation keys
1428    *
1429    * @param privateKey secret key share.
1430    * @param eSum a dictionary with prior joined summation keys.
1431    * @return new joined summation keys.
1432    */
MultiEvalSumKeyGen(const LPPrivateKey<Element> privateKey,const shared_ptr<std::map<usint,LPEvalKey<Element>>> eSum)1433   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> MultiEvalSumKeyGen(
1434       const LPPrivateKey<Element> privateKey,
1435       const shared_ptr<std::map<usint, LPEvalKey<Element>>> eSum) const {
1436     PALISADE_THROW(not_implemented_error,
1437                    "MultiEvalSumKeyGen multi-party capability is not supported "
1438                    "for this scheme");
1439   }
1440 
1441   /**
1442    * Threshold FHE: Adds two prior public keys
1443    *
1444    * @param evalKey1 first public key.
1445    * @param evalKey2 second public key.
1446    * @return the new joined key.
1447    */
MultiAddPubKeys(LPPublicKey<Element> pubKey1,LPPublicKey<Element> pubKey2)1448   virtual LPPublicKey<Element> MultiAddPubKeys(
1449       LPPublicKey<Element> pubKey1, LPPublicKey<Element> pubKey2) const {
1450     if (!pubKey1)
1451       PALISADE_THROW(config_error, "Input first public key is nullptr");
1452     if (!pubKey2)
1453       PALISADE_THROW(config_error, "Input second public key is nullptr");
1454 
1455     LPPublicKey<Element> pubKey(
1456         new LPPublicKeyImpl<Element>(pubKey1->GetCryptoContext()));
1457 
1458     if (pubKey1->GetPublicElements()[1] != pubKey2->GetPublicElements()[1])
1459       PALISADE_THROW(type_error,
1460                      "MultiAddPubKeys: public keys are not compatible");
1461 
1462     const Element &a = pubKey1->GetPublicElements()[1];
1463 
1464     const Element &b1 = pubKey1->GetPublicElements()[0];
1465     const Element &b2 = pubKey2->GetPublicElements()[0];
1466 
1467     pubKey->SetPublicElementAtIndex(0, std::move(b1 + b2));
1468     pubKey->SetPublicElementAtIndex(1, a);
1469 
1470     return pubKey;
1471   }
1472 
1473   /**
1474    * Threshold FHE: Adds two prior evaluation keys
1475    *
1476    * @param evalKey1 first evaluation key.
1477    * @param evalKey2 second evaluation key.
1478    * @return the new joined key.
1479    */
MultiAddEvalKeys(LPEvalKey<Element> evalKey1,LPEvalKey<Element> evalKey2)1480   virtual LPEvalKey<Element> MultiAddEvalKeys(
1481       LPEvalKey<Element> evalKey1, LPEvalKey<Element> evalKey2) const {
1482     if (!evalKey1)
1483       PALISADE_THROW(config_error, "Input first evaluation key is nullptr");
1484     if (!evalKey2)
1485       PALISADE_THROW(config_error, "Input second evaluation key is nullptr");
1486 
1487     LPEvalKey<Element> evalKeySum(
1488         new LPEvalKeyRelinImpl<Element>(evalKey1->GetCryptoContext()));
1489 
1490     const std::vector<Element> &a = evalKey1->GetAVector();
1491 
1492     const std::vector<Element> &b1 = evalKey1->GetBVector();
1493     const std::vector<Element> &b2 = evalKey2->GetBVector();
1494 
1495     std::vector<Element> b;
1496 
1497     for (usint i = 0; i < a.size(); i++) {
1498       b.push_back(b1[i] + b2[i]);
1499     }
1500 
1501     evalKeySum->SetAVector(a);
1502     evalKeySum->SetBVector(std::move(b));
1503 
1504     return evalKeySum;
1505   }
1506 
1507   /**
1508    * Threshold FHE: Generates a partial evaluation key for homomorphic
1509    * multiplication based on the current secret share and an existing partial
1510    * evaluation key
1511    *
1512    * @param evalKey prior evaluation key.
1513    * @param sk current secret share.
1514    * @return the new joined key.
1515    */
MultiMultEvalKey(LPEvalKey<Element> evalKey,LPPrivateKey<Element> sk)1516   virtual LPEvalKey<Element> MultiMultEvalKey(LPEvalKey<Element> evalKey,
1517                                               LPPrivateKey<Element> sk) const {
1518     PALISADE_THROW(not_implemented_error,
1519                    "MultiMultEvalKey multi-party capability is not supported "
1520                    "for this scheme");
1521   }
1522 
1523   /**
1524    * Threshold FHE: Adds two prior evaluation key sets for summation
1525    *
1526    * @param es1 first summation key set.
1527    * @param es2 second summation key set.
1528    * @return the new joined key set for summation.
1529    */
MultiAddEvalSumKeys(const shared_ptr<std::map<usint,LPEvalKey<Element>>> es1,const shared_ptr<std::map<usint,LPEvalKey<Element>>> es2)1530   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> MultiAddEvalSumKeys(
1531       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es1,
1532       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es2) const {
1533     if (!es1)
1534       PALISADE_THROW(config_error, "Input first evaluation key map is nullptr");
1535     if (!es2)
1536       PALISADE_THROW(config_error,
1537                      "Input second evaluation key map is nullptr");
1538 
1539     auto evalSumKeys = std::make_shared<std::map<usint, LPEvalKey<Element>>>();
1540 
1541     for (auto it = es1->begin(); it != es1->end(); ++it) {
1542       auto it2 = es2->find(it->first);
1543       if (it2 != es2->end())
1544         (*evalSumKeys)[it->first] = MultiAddEvalKeys(it->second, it2->second);
1545     }
1546 
1547     return evalSumKeys;
1548   }
1549 
1550   /**
1551    * Threshold FHE: Adds two prior evaluation key sets for automorphisms
1552    *
1553    * @param es1 first automorphism key set.
1554    * @param es2 second automorphism key set.
1555    * @return the new joined key set for summation.
1556    */
1557   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
MultiAddEvalAutomorphismKeys(const shared_ptr<std::map<usint,LPEvalKey<Element>>> es1,const shared_ptr<std::map<usint,LPEvalKey<Element>>> es2)1558   MultiAddEvalAutomorphismKeys(
1559       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es1,
1560       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es2) const {
1561     if (!es1)
1562       PALISADE_THROW(config_error, "Input first evaluation key map is nullptr");
1563     if (!es2)
1564       PALISADE_THROW(config_error,
1565                      "Input second evaluation key map is nullptr");
1566 
1567     auto evalAutomorphismKeys =
1568         std::make_shared<std::map<usint, LPEvalKey<Element>>>();
1569 
1570     for (auto it = es1->begin(); it != es1->end(); ++it) {
1571       auto it2 = es2->find(it->first);
1572       if (it2 != es2->end())
1573         (*evalAutomorphismKeys)[it->first] =
1574             MultiAddEvalKeys(it->second, it2->second);
1575     }
1576 
1577     return evalAutomorphismKeys;
1578   }
1579 
1580   /**
1581    * Threshold FHE: Adds two  partial evaluation keys for multiplication
1582    *
1583    * @param evalKey1 first evaluation key.
1584    * @param evalKey2 second evaluation key.
1585    * @return the new joined key.
1586    */
MultiAddEvalMultKeys(LPEvalKey<Element> evalKey1,LPEvalKey<Element> evalKey2)1587   virtual LPEvalKey<Element> MultiAddEvalMultKeys(
1588       LPEvalKey<Element> evalKey1, LPEvalKey<Element> evalKey2) const {
1589     if (!evalKey1)
1590       PALISADE_THROW(config_error, "Input first evaluation key is nullptr");
1591     if (!evalKey2)
1592       PALISADE_THROW(config_error, "Input second evaluation key is nullptr");
1593 
1594     LPEvalKey<Element> evalKeySum(
1595         new LPEvalKeyRelinImpl<Element>(evalKey1->GetCryptoContext()));
1596 
1597     const std::vector<Element> &a1 = evalKey1->GetAVector();
1598     const std::vector<Element> &a2 = evalKey2->GetAVector();
1599 
1600     const std::vector<Element> &b1 = evalKey1->GetBVector();
1601     const std::vector<Element> &b2 = evalKey2->GetBVector();
1602 
1603     std::vector<Element> a;
1604     std::vector<Element> b;
1605 
1606     for (usint i = 0; i < a1.size(); i++) {
1607       a.push_back(a1[i] + a2[i]);
1608       b.push_back(b1[i] + b2[i]);
1609     }
1610 
1611     evalKeySum->SetAVector(std::move(a));
1612 
1613     evalKeySum->SetBVector(std::move(b));
1614 
1615     return evalKeySum;
1616   }
1617 
1618   /**
1619    * Threshold FHE: Generates evaluation keys for a list of indices for a
1620    * multi-party setting Currently works only for power-of-two and cyclic-group
1621    * cyclotomics
1622    *
1623    * @param secretShare secret share
1624    * @param partial evaluation key set from other party (parties)
1625    * @param indexList list of indices to be computed
1626    * @return returns the joined evaluation keys
1627    */
1628   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
MultiEvalAtIndexKeyGen(const LPPrivateKey<Element> secretShare,const shared_ptr<std::map<usint,LPEvalKey<Element>>> eAuto,const std::vector<int32_t> & indexList)1629   MultiEvalAtIndexKeyGen(
1630       const LPPrivateKey<Element> secretShare,
1631       const shared_ptr<std::map<usint, LPEvalKey<Element>>> eAuto,
1632       const std::vector<int32_t> &indexList) const {
1633     if (!secretShare)
1634       PALISADE_THROW(config_error, "Input private key is nullptr");
1635     if (!eAuto)
1636       PALISADE_THROW(config_error, "Input evaluation key map is nullptr");
1637     if (!indexList.size())
1638       PALISADE_THROW(config_error, "Input index vector is empty");
1639     const auto cryptoParams = secretShare->GetCryptoParameters();
1640     const auto encodingParams = cryptoParams->GetEncodingParams();
1641     const auto elementParams = cryptoParams->GetElementParams();
1642     uint32_t m = elementParams->GetCyclotomicOrder();
1643 
1644     std::vector<uint32_t> autoIndices(indexList.size());
1645 
1646     if (IsPowerOfTwo(m)) {  // power-of-two cyclotomics
1647       for (size_t i = 0; i < indexList.size(); i++) {
1648         auto ccInst = secretShare->GetCryptoContext();
1649         // CKKS Packing
1650         if (ccInst->getSchemeId() == "CKKS")
1651           autoIndices[i] = FindAutomorphismIndex2nComplex(indexList[i], m);
1652         else
1653           autoIndices[i] = FindAutomorphismIndex2n(indexList[i], m);
1654       }
1655 
1656     } else {  // cyclic groups
1657       for (size_t i = 0; i < indexList.size(); i++)
1658         autoIndices[i] = FindAutomorphismIndexCyclic(
1659             indexList[i], m, encodingParams->GetPlaintextGenerator());
1660     }
1661 
1662     return MultiEvalAutomorphismKeyGen(secretShare, eAuto, autoIndices);
1663   }
1664 
1665   template <class Archive>
save(Archive & ar,std::uint32_t const version)1666   void save(Archive &ar, std::uint32_t const version) const {}
1667 
1668   template <class Archive>
load(Archive & ar,std::uint32_t const version)1669   void load(Archive &ar, std::uint32_t const version) {}
1670 
SerializedObjectName()1671   std::string SerializedObjectName() const { return "MultiParty"; }
1672 };
1673 
1674 /**
1675  * @brief Abstract interface class for LBC SHE algorithms
1676  * @tparam Element a ring element.
1677  */
1678 template <class Element>
1679 class LPSHEAlgorithm {
1680  public:
~LPSHEAlgorithm()1681   virtual ~LPSHEAlgorithm() {}
1682 
1683   /**
1684    * Virtual function to define the interface for homomorphic addition of
1685    * ciphertexts.
1686    *
1687    * @param ciphertext1 the input ciphertext.
1688    * @param ciphertext2 the input ciphertext.
1689    * @return the new ciphertext.
1690    */
EvalAdd(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2)1691   virtual Ciphertext<Element> EvalAdd(
1692       ConstCiphertext<Element> ciphertext1,
1693       ConstCiphertext<Element> ciphertext2) const {
1694     auto rv = ciphertext1->Clone();
1695     EvalAddInPlace(rv, ciphertext2);
1696     return rv;
1697   }
1698 
1699   /**
1700    * Virtual function to define the interface for in-place homomorphic addition
1701    * of ciphertexts.
1702    *
1703    * @param ciphertext1 the input/output ciphertext.
1704    * @param ciphertext2 the input ciphertext.
1705    */
1706   virtual void EvalAddInPlace(Ciphertext<Element> &ciphertext1,
1707                               ConstCiphertext<Element> ciphertext2) const = 0;
1708 
1709   /**
1710    * Virtual function to define the interface for homomorphic addition of
1711    * ciphertexts. This is the mutable version - input ciphertexts may change
1712    * (automatically rescaled, or towers dropped).
1713    *
1714    * @param ciphertext1 the input ciphertext.
1715    * @param ciphertext2 the input ciphertext.
1716    * @return the new ciphertext.
1717    */
EvalAddMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2)1718   virtual Ciphertext<Element> EvalAddMutable(
1719       Ciphertext<Element> &ciphertext1,
1720       Ciphertext<Element> &ciphertext2) const {
1721     PALISADE_THROW(not_implemented_error,
1722                    "EvalAddMutable is not implemented for this scheme");
1723   }
1724 
1725   /**
1726    * Virtual function to define the interface for homomorphic addition of
1727    * ciphertexts.
1728    *
1729    * @param ciphertext the input ciphertext.
1730    * @param plaintext the input plaintext.
1731    * @return the new ciphertext.
1732    */
1733   virtual Ciphertext<Element> EvalAdd(ConstCiphertext<Element> ciphertext,
1734                                       ConstPlaintext plaintext) const = 0;
1735 
1736   /**
1737    * Virtual function to define the interface for homomorphic addition of
1738    * ciphertexts. This is the mutable version - input ciphertext may change
1739    * (automatically rescaled, or towers dropped).
1740    *
1741    * @param ciphertext the input ciphertext.
1742    * @param plaintext the input plaintext.
1743    * @return the new ciphertext.
1744    */
EvalAddMutable(Ciphertext<Element> & ciphertext,Plaintext plaintext)1745   virtual Ciphertext<Element> EvalAddMutable(Ciphertext<Element> &ciphertext,
1746                                              Plaintext plaintext) const {
1747     PALISADE_THROW(not_implemented_error,
1748                    "EvalAddMutable is not implemented for this scheme");
1749   }
1750 
1751   /**
1752    * Virtual function to define the adding of a scalar to a ciphertext
1753    *
1754    * @param ciphertext the input ciphertext.
1755    * @param constant the input constant.
1756    * @return the new ciphertext.
1757    */
EvalAdd(ConstCiphertext<Element> ciphertext,double constant)1758   virtual Ciphertext<Element> EvalAdd(ConstCiphertext<Element> ciphertext,
1759                                       double constant) const {
1760     PALISADE_THROW(not_implemented_error,
1761                    "Scalar addition is not implemented for this scheme");
1762   }
1763 
1764   /**
1765    * Virtual function for computing the linear weighted sum of a
1766    * vector of ciphertexts.
1767    *
1768    * @param ciphertexts vector of input ciphertexts.
1769    * @param constants vector containing double weights.
1770    * @return A ciphertext containing the linear weighted sum.
1771    */
EvalLinearWSum(vector<Ciphertext<Element>> ciphertexts,vector<double> constants)1772   virtual Ciphertext<Element> EvalLinearWSum(
1773       vector<Ciphertext<Element>> ciphertexts, vector<double> constants) const {
1774     std::string errMsg = "EvalLinearWSum is not implemented for this scheme.";
1775     PALISADE_THROW(not_implemented_error, errMsg);
1776   }
1777 
1778   /**
1779    * Function for computing the linear weighted sum of a
1780    * vector of ciphertexts. This is a mutable method,
1781    * meaning that the level/depth of input ciphertexts may change.
1782    *
1783    * @param ciphertexts vector of input ciphertexts.
1784    * @param constants vector containing double weights.
1785    * @return A ciphertext containing the linear weighted sum.
1786    */
EvalLinearWSumMutable(vector<Ciphertext<Element>> ciphertexts,vector<double> constants)1787   virtual Ciphertext<Element> EvalLinearWSumMutable(
1788       vector<Ciphertext<Element>> ciphertexts, vector<double> constants) const {
1789     std::string errMsg =
1790         "EvalLinearWSumMutable is not implemented for this scheme.";
1791     PALISADE_THROW(not_implemented_error, errMsg);
1792   }
1793 
1794   /**
1795    * Virtual function to define the interface for homomorphic subtraction of
1796    * ciphertexts.
1797    *
1798    * @param ciphertext1 the input ciphertext.
1799    * @param ciphertext2 the input ciphertext.
1800    * @return the new ciphertext.
1801    */
1802   virtual Ciphertext<Element> EvalSub(
1803       ConstCiphertext<Element> ciphertext1,
1804       ConstCiphertext<Element> ciphertext2) const = 0;
1805 
1806   /**
1807    * Virtual function to define the interface for homomorphic subtraction of
1808    * ciphertexts. This is the mutable version - input ciphertext may change
1809    * (automatically rescaled, or towers dropped).
1810    *
1811    * @param ciphertext1 the input ciphertext.
1812    * @param ciphertext2 the input ciphertext.
1813    * @return the new ciphertext.
1814    */
EvalSubMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2)1815   virtual Ciphertext<Element> EvalSubMutable(
1816       Ciphertext<Element> &ciphertext1,
1817       Ciphertext<Element> &ciphertext2) const {
1818     PALISADE_THROW(not_implemented_error,
1819                    "EvalSubMutable is not implemented for this scheme");
1820   }
1821 
1822   /**
1823    * Virtual function to define the interface for homomorphic subtraction of
1824    * ciphertexts.
1825    *
1826    * @param ciphertext the input ciphertext.
1827    * @param plaintext the input plaintext.
1828    * @return the new ciphertext.
1829    */
1830   virtual Ciphertext<Element> EvalSub(ConstCiphertext<Element> ciphertext,
1831                                       ConstPlaintext plaintext) const = 0;
1832 
1833   /**
1834    * Virtual function to define the interface for homomorphic subtraction of
1835    * ciphertexts. This is the mutable version - input ciphertext may change
1836    * (automatically rescaled, or towers dropped).
1837    *
1838    * @param ciphertext the input ciphertext.
1839    * @param plaintext the input plaintext.
1840    * @return the new ciphertext.
1841    */
EvalSubMutable(Ciphertext<Element> & ciphertext,Plaintext plaintext)1842   virtual Ciphertext<Element> EvalSubMutable(Ciphertext<Element> &ciphertext,
1843                                              Plaintext plaintext) const {
1844     PALISADE_THROW(not_implemented_error,
1845                    "EvalSubMutable is not implemented for this scheme");
1846   }
1847 
1848   /**
1849    * Virtual function to define the subtraction of a scalar from a ciphertext
1850    *
1851    * @param ciphertext the input ciphertext.
1852    * @param constant the input constant.
1853    * @return the new ciphertext.
1854    */
EvalSub(ConstCiphertext<Element> ciphertext,double constant)1855   virtual Ciphertext<Element> EvalSub(ConstCiphertext<Element> ciphertext,
1856                                       double constant) const {
1857     PALISADE_THROW(not_implemented_error,
1858                    "Scalar subtraction is not implemented for this scheme");
1859   }
1860 
1861   /**
1862    * Virtual function to define the interface for multiplicative homomorphic
1863    * evaluation of ciphertext.
1864    *
1865    * @param ciphertext1 the input ciphertext.
1866    * @param ciphertext2 the input ciphertext.
1867    * @return the new ciphertext.
1868    */
1869   virtual Ciphertext<Element> EvalMult(
1870       ConstCiphertext<Element> ciphertext1,
1871       ConstCiphertext<Element> ciphertext2) const = 0;
1872 
1873   /**
1874    * Virtual function to define the interface for multiplicative homomorphic
1875    * evaluation of ciphertext. This is the mutable version - input ciphertexts
1876    * may change (automatically rescaled, or towers dropped).
1877    *
1878    * @param ciphertext1 the input ciphertext.
1879    * @param ciphertext2 the input ciphertext.
1880    * @return the new ciphertext.
1881    */
EvalMultMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2)1882   virtual Ciphertext<Element> EvalMultMutable(
1883       Ciphertext<Element> &ciphertext1,
1884       Ciphertext<Element> &ciphertext2) const {
1885     PALISADE_THROW(not_implemented_error,
1886                    "EvalMultMutable is not implemented for this scheme");
1887   }
1888 
1889   /**
1890    * Virtual function to define the interface for multiplication of ciphertext
1891    * by plaintext.
1892    *
1893    * @param ciphertext the input ciphertext.
1894    * @param plaintext the input plaintext.
1895    * @return the new ciphertext.
1896    */
1897   virtual Ciphertext<Element> EvalMult(ConstCiphertext<Element> ciphertext,
1898                                        ConstPlaintext plaintext) const = 0;
1899 
1900   /**
1901    * Virtual function to define the interface for multiplication of ciphertext
1902    * by plaintext. This is the mutable version - input ciphertext may change
1903    * (automatically rescaled, or towers dropped).
1904    *
1905    * @param ciphertext the input ciphertext.
1906    * @param plaintext the input plaintext.
1907    * @return the new ciphertext.
1908    */
EvalMultMutable(Ciphertext<Element> & ciphertext,Plaintext plaintext)1909   virtual Ciphertext<Element> EvalMultMutable(Ciphertext<Element> &ciphertext,
1910                                               Plaintext plaintext) const {
1911     PALISADE_THROW(not_implemented_error,
1912                    "EvalMultMutable is not implemented for this scheme");
1913   }
1914 
1915   /**
1916    * Virtual function to define the multiplication of a ciphertext by a
1917    * constant
1918    *
1919    * @param ciphertext the input ciphertext.
1920    * @param constant the input constant.
1921    * @return the new ciphertext.
1922    */
EvalMult(ConstCiphertext<Element> ciphertext,double constant)1923   virtual Ciphertext<Element> EvalMult(ConstCiphertext<Element> ciphertext,
1924                                        double constant) const {
1925     PALISADE_THROW(not_implemented_error,
1926                    "Scalar multiplication is not implemented for this scheme");
1927   }
1928 
1929   /**
1930    * Virtual function to define the multiplication of a ciphertext by a
1931    * constant. This is the mutable version - input ciphertext may change
1932    * (automatically rescaled, or towers dropped).
1933    *
1934    * @param ciphertext the input ciphertext.
1935    * @param constant the input constant.
1936    * @return the new ciphertext.
1937    */
EvalMultMutable(Ciphertext<Element> & ciphertext,double constant)1938   virtual Ciphertext<Element> EvalMultMutable(Ciphertext<Element> &ciphertext,
1939                                               double constant) const {
1940     PALISADE_THROW(not_implemented_error,
1941                    "EvalMultMutable is not implemented for this scheme");
1942   }
1943 
1944   /**
1945    * Virtual function to define the interface for multiplicative homomorphic
1946    * evaluation of ciphertext using the evaluation key.
1947    *
1948    * @param &ciphertext1 first input ciphertext.
1949    * @param &ciphertext2 second input ciphertext.
1950    * @param &ek is the evaluation key to make the newCiphertext decryptable by
1951    * the same secret key as that of ciphertext1 and ciphertext2.
1952    * @return the new ciphertext.
1953    */
1954   virtual Ciphertext<Element> EvalMult(ConstCiphertext<Element> ciphertext1,
1955                                        ConstCiphertext<Element> ciphertext2,
1956                                        const LPEvalKey<Element> ek) const = 0;
1957 
1958   /**
1959    * Virtual function to define the interface for multiplicative homomorphic
1960    * evaluation of ciphertext using the evaluation key. This is the mutable
1961    * version - input ciphertext may change (automatically rescaled, or towers
1962    * dropped).
1963    *
1964    * @param &ciphertext1 first input ciphertext.
1965    * @param &ciphertext2 second input ciphertext.
1966    * @param &ek is the evaluation key to make the newCiphertext decryptable by
1967    * the same secret key as that of ciphertext1 and ciphertext2.
1968    * @return the new ciphertext.
1969    */
EvalMultMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2,const LPEvalKey<Element> ek)1970   virtual Ciphertext<Element> EvalMultMutable(
1971       Ciphertext<Element> &ciphertext1, Ciphertext<Element> &ciphertext2,
1972       const LPEvalKey<Element> ek) const {
1973     PALISADE_THROW(not_implemented_error,
1974                    "EvalMultMutable is not implemented for this scheme");
1975   }
1976 
1977   /**
1978    * Virtual function for evaluating multiplication of a ciphertext list which
1979    * each multiplication is followed by relinearization operation.
1980    *
1981    * @param cipherTextList  is the ciphertext list.
1982    * @param evalKeys is the evaluation key to make the newCiphertext
1983    *  decryptable by the same secret key as that of ciphertext list.
1984    * @param *newCiphertext the new resulting ciphertext.
1985    */
EvalMultMany(const vector<Ciphertext<Element>> & cipherTextList,const vector<LPEvalKey<Element>> & evalKeys)1986   virtual Ciphertext<Element> EvalMultMany(
1987       const vector<Ciphertext<Element>> &cipherTextList,
1988       const vector<LPEvalKey<Element>> &evalKeys) const {
1989     // default implementation if you don't have one in your scheme
1990     // TODO: seems that we can simply call EvalAddMany() here.
1991     // TODO: see EvalAddMany() below
1992     if (cipherTextList.size() < 1)
1993       PALISADE_THROW(config_error,
1994                      "Input ciphertext vector size should be 1 or more");
1995 
1996     const size_t inSize = cipherTextList.size();
1997     const size_t lim = inSize * 2 - 2;
1998     vector<Ciphertext<Element>> cipherTextResults;
1999     cipherTextResults.resize(inSize - 1);
2000     size_t ctrIndex = 0;
2001 
2002     for (size_t i = 0; i < lim; i = i + 2) {
2003       cipherTextResults[ctrIndex++] = this->EvalMult(
2004           i < inSize ? cipherTextList[i] : cipherTextResults[i - inSize],
2005           i + 1 < inSize ? cipherTextList[i + 1]
2006                          : cipherTextResults[i + 1 - inSize]);
2007     }
2008 
2009     return cipherTextResults.back();
2010   }
2011 
2012   /**
2013    * Virtual function for evaluating addition of a list of ciphertexts.
2014    *
2015    * @param ctList  is the ciphertext list.
2016    * @param *newCiphertext the new resulting ciphertext.
2017    */
EvalAddMany(const vector<Ciphertext<Element>> & ctList)2018   virtual Ciphertext<Element> EvalAddMany(
2019       const vector<Ciphertext<Element>> &ctList) const {
2020     // default implementation if you don't have one in your scheme
2021     if (ctList.size() < 1)
2022       PALISADE_THROW(config_error,
2023                      "Input ciphertext vector size should be 1 or more");
2024 
2025     const size_t inSize = ctList.size();
2026     const size_t lim = inSize * 2 - 2;
2027     vector<Ciphertext<Element>> cipherTextResults;
2028     cipherTextResults.resize(inSize - 1);
2029     size_t ctrIndex = 0;
2030 
2031     for (size_t i = 0; i < lim; i = i + 2) {
2032       cipherTextResults[ctrIndex++] = this->EvalAdd(
2033           i < inSize ? ctList[i] : cipherTextResults[i - inSize],
2034           i + 1 < inSize ? ctList[i + 1] : cipherTextResults[i + 1 - inSize]);
2035     }
2036 
2037     return cipherTextResults.back();
2038   }
2039 
2040   /**
2041    * Virtual function for evaluating addition of a list of ciphertexts.
2042    * This version uses no additional space, other than the vector provided.
2043    *
2044    * @param ctList  is the ciphertext list.
2045    * @param *newCiphertext the new resulting ciphertext.
2046    */
EvalAddManyInPlace(vector<Ciphertext<Element>> & ctList)2047   virtual Ciphertext<Element> EvalAddManyInPlace(
2048       vector<Ciphertext<Element>> &ctList) const {
2049     // default implementation if you don't have one in your scheme
2050     if (ctList.size() < 1)
2051       PALISADE_THROW(config_error,
2052                      "Input ciphertext vector size should be 1 or more");
2053 
2054     for (size_t j = 1; j < ctList.size(); j = j * 2) {
2055       for (size_t i = 0; i < ctList.size(); i = i + 2 * j) {
2056         if ((i + j) < ctList.size()) {
2057           if (ctList[i] != nullptr && ctList[i + j] != nullptr) {
2058             ctList[i] = EvalAdd(ctList[i], ctList[i + j]);
2059           } else if (ctList[i] == nullptr && ctList[i + j] != nullptr) {
2060             ctList[i] = ctList[i + j];
2061           }  // In all remaining cases (ctList[i+j]), ctList[i] needs to
2062              // remain unchanged.
2063         }
2064       }
2065     }
2066 
2067     Ciphertext<Element> result(
2068         std::make_shared<CiphertextImpl<Element>>(*(ctList[0])));
2069 
2070     return result;
2071   }
2072 
2073   /**
2074    * Virtual function to define the interface for multiplicative homomorphic
2075    * evaluation of ciphertext using the evaluation key.
2076    *
2077    * @param ct1 first input ciphertext.
2078    * @param ct2 second input ciphertext.
2079    * @param ek is the evaluation key to make the newCiphertext
2080    *  decryptable by the same secret key as that of ciphertext1 and
2081    * ciphertext2.
2082    * @param *newCiphertext the new resulting ciphertext.
2083    */
2084   virtual Ciphertext<Element> EvalMultAndRelinearize(
2085       ConstCiphertext<Element> ct1, ConstCiphertext<Element> ct2,
2086       const vector<LPEvalKey<Element>> &ek) const = 0;
2087 
2088   /**
2089    * Virtual function to do relinearization
2090    *
2091    * @param ciphertext input ciphertext.
2092    * @param ek are the evaluation keys to make the newCiphertext
2093    *  decryptable by the same secret key as that of ciphertext1 and
2094    * ciphertext2.
2095    * @return the new resulting ciphertext.
2096    */
Relinearize(ConstCiphertext<Element> ciphertext,const vector<LPEvalKey<Element>> & ek)2097   virtual Ciphertext<Element> Relinearize(
2098       ConstCiphertext<Element> ciphertext,
2099       const vector<LPEvalKey<Element>> &ek) const {
2100     PALISADE_THROW(config_error, "Relinearize operation not supported");
2101   }
2102 
2103   /**
2104    * Virtual function to do in-place relinearization
2105    *
2106    * @param &ciphertext input ciphertext.
2107    * @param ek are the evaluation keys
2108    * @return the new resulting ciphertext.
2109    */
RelinearizeInPlace(Ciphertext<Element> & ciphertext,const vector<LPEvalKey<Element>> & ek)2110   virtual void RelinearizeInPlace(
2111       Ciphertext<Element> &ciphertext,
2112       const vector<LPEvalKey<Element>> &ek) const {
2113     PALISADE_THROW(config_error, "RelinearizeInPlace operation not supported");
2114   }
2115 
2116   /**
2117    * Virtual function to define the interface for homomorphic negation of
2118    * ciphertext.
2119    *
2120    * @param &ciphertext the input ciphertext.
2121    * @param *newCiphertext the new ciphertext.
2122    */
2123   virtual Ciphertext<Element> EvalNegate(
2124       ConstCiphertext<Element> ciphertext) const = 0;
2125 
2126   /**
2127    * Function to add random noise to all plaintext slots except for the first
2128    * one; used in EvalInnerProduct
2129    *
2130    * @param &ciphertext the input ciphertext.
2131    * @return modified ciphertext
2132    */
AddRandomNoise(ConstCiphertext<Element> ciphertext)2133   virtual Ciphertext<Element> AddRandomNoise(
2134       ConstCiphertext<Element> ciphertext) const {
2135     if (!ciphertext)
2136       PALISADE_THROW(config_error, "Input ciphertext is nullptr");
2137 
2138     std::uniform_real_distribution<double> distribution(0.0, 1.0);
2139 
2140     string kID = ciphertext->GetKeyTag();
2141     const auto cryptoParams = ciphertext->GetCryptoParameters();
2142     const auto encodingParams = cryptoParams->GetEncodingParams();
2143     const auto elementParams = cryptoParams->GetElementParams();
2144 
2145     usint n = elementParams->GetRingDimension();
2146 
2147     auto cc = ciphertext->GetCryptoContext();
2148 
2149     Plaintext plaintext;
2150 
2151     if (ciphertext->GetEncodingType() == CKKSPacked) {
2152       std::vector<std::complex<double>> randomIntVector(n);
2153 
2154       // first plaintext slot does not need to change
2155       randomIntVector[0].real(0);
2156 
2157       for (usint i = 0; i < n - 1; i++) {
2158         randomIntVector[i + 1].real(
2159             distribution(PseudoRandomNumberGenerator::GetPRNG()));
2160       }
2161 
2162       plaintext =
2163           cc->MakeCKKSPackedPlaintext(randomIntVector, ciphertext->GetDepth());
2164 
2165     } else {
2166       DiscreteUniformGenerator dug;
2167       dug.SetModulus(encodingParams->GetPlaintextModulus());
2168       BigVector randomVector = dug.GenerateVector(n - 1);
2169 
2170       std::vector<int64_t> randomIntVector(n);
2171 
2172       // first plaintext slot does not need to change
2173       randomIntVector[0] = 0;
2174 
2175       for (usint i = 0; i < n - 1; i++) {
2176         randomIntVector[i + 1] = randomVector[i].ConvertToInt();
2177       }
2178 
2179       plaintext = cc->MakePackedPlaintext(randomIntVector);
2180     }
2181 
2182     plaintext->Encode();
2183     plaintext->GetElement<Element>().SetFormat(EVALUATION);
2184 
2185     auto ans = EvalAdd(ciphertext, plaintext);
2186 
2187     return ans;
2188   }
2189 
2190   /**
2191    * Method for KeySwitchGen
2192    *
2193    * @param &originalPrivateKey Original private key used for encryption.
2194    * @param &newPrivateKey New private key to generate the keyswitch hint.
2195    * @param *KeySwitchHint is where the resulting keySwitchHint will be
2196    * placed.
2197    */
2198   virtual LPEvalKey<Element> KeySwitchGen(
2199       const LPPrivateKey<Element> originalPrivateKey,
2200       const LPPrivateKey<Element> newPrivateKey) const = 0;
2201 
2202   /**
2203    * Method for KeySwitch
2204    *
2205    * @param &keySwitchHint Hint required to perform the ciphertext switching.
2206    * @param &cipherText Original ciphertext to perform switching on.
2207    */
2208 
2209   virtual void KeySwitchInPlace(const LPEvalKey<Element> keySwitchHint,
2210                                 Ciphertext<Element> &cipherText) const = 0;
2211 
KeySwitch(const LPEvalKey<Element> keySwitchHint,ConstCiphertext<Element> cipherText)2212   virtual Ciphertext<Element> KeySwitch(
2213       const LPEvalKey<Element> keySwitchHint,
2214       ConstCiphertext<Element> cipherText) const {
2215     auto ret = cipherText->Clone();
2216     KeySwitchInPlace(keySwitchHint, ret);
2217     return ret;
2218   }
2219 
2220   /**
2221    * Virtual function to define the interface for generating a evaluation key
2222    * which is used after each multiplication.
2223    *
2224    * @param &ciphertext1 first input ciphertext.
2225    * @param &ciphertext2 second input ciphertext.
2226    * @param &ek is the evaluation key to make the newCiphertext decryptable by
2227    * the same secret key as that of ciphertext1 and ciphertext2.
2228    * @param *newCiphertext the new resulting ciphertext.
2229    */
2230   virtual LPEvalKey<Element> EvalMultKeyGen(
2231       const LPPrivateKey<Element> originalPrivateKey) const = 0;
2232 
2233   /**
2234    * Virtual function to define the interface for generating a evaluation key
2235    * which is used after each multiplication for depth more than 2.
2236    *
2237    * @param &originalPrivateKey Original private key used for encryption.
2238    * @param *evalMultKeys the resulting evalution key vector list.
2239    */
2240   virtual vector<LPEvalKey<Element>> EvalMultKeysGen(
2241       const LPPrivateKey<Element> originalPrivateKey) const = 0;
2242 
2243   /**
2244    * Virtual function to generate all isomorphism keys for a given private key
2245    *
2246    * @param publicKey encryption key for the new ciphertext.
2247    * @param origPrivateKey original private key used for decryption.
2248    * @param indexList list of automorphism indices to be computed
2249    * @return returns the evaluation keys
2250    */
2251   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
2252   EvalAutomorphismKeyGen(const LPPublicKey<Element> publicKey,
2253                          const LPPrivateKey<Element> origPrivateKey,
2254                          const std::vector<usint> &indexList) const = 0;
2255 
2256   /**
2257    * Virtual function for the precomputation step of hoisted
2258    * automorphisms.
2259    *
2260    * @param ct the input ciphertext on which to do the precomputation (digit
2261    * decomposition)
2262    */
EvalFastRotationPrecompute(ConstCiphertext<Element> cipherText)2263   virtual shared_ptr<vector<Element>> EvalFastRotationPrecompute(
2264       ConstCiphertext<Element> cipherText) const {
2265     std::string errMsg =
2266         "LPSHEAlgorithm::EvalFastRotationPrecompute is not implemented for "
2267         "this Scheme.";
2268     PALISADE_THROW(not_implemented_error, errMsg);
2269   }
2270 
2271   /**
2272    * Virtual function for the automorphism and key switching step of
2273    * hoisted automorphisms.
2274    *
2275    * @param ct the input ciphertext to perform the automorphism on
2276    * @param index the index of the rotation. Positive indices correspond to
2277    * left rotations and negative indices correspond to right rotations.
2278    * @param m is the cyclotomic order
2279    * @param digits the digit decomposition created by
2280    * EvalFastRotationPrecompute at the precomputation step.
2281    */
EvalFastRotation(ConstCiphertext<Element> cipherText,const usint index,const usint m,const shared_ptr<vector<Element>> digits)2282   virtual Ciphertext<Element> EvalFastRotation(
2283       ConstCiphertext<Element> cipherText, const usint index, const usint m,
2284       const shared_ptr<vector<Element>> digits) const {
2285     std::string errMsg =
2286         "LPSHEAlgorithm::EvalFastRotation is not implemented for this "
2287         "Scheme.";
2288     PALISADE_THROW(not_implemented_error, errMsg);
2289   }
2290 
2291   /**
2292    * Generates evaluation keys for a list of indices
2293    * Currently works only for power-of-two and cyclic-group cyclotomics
2294    *
2295    * @param publicKey encryption key for the new ciphertext.
2296    * @param origPrivateKey original private key used for decryption.
2297    * @param indexList list of indices to be computed
2298    * @return returns the evaluation keys
2299    */
EvalAtIndexKeyGen(const LPPublicKey<Element> publicKey,const LPPrivateKey<Element> origPrivateKey,const std::vector<int32_t> & indexList)2300   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalAtIndexKeyGen(
2301       const LPPublicKey<Element> publicKey,
2302       const LPPrivateKey<Element> origPrivateKey,
2303       const std::vector<int32_t> &indexList) const {
2304     /*
2305      * we don't validate publicKey as it is needed by NTRU-based scheme only
2306      * NTRU-based scheme only and it is checked for null later.
2307      */
2308     if (!origPrivateKey)
2309       PALISADE_THROW(config_error, "Input private key is nullptr");
2310     const auto cryptoParams = origPrivateKey->GetCryptoParameters();
2311     const auto encodingParams = cryptoParams->GetEncodingParams();
2312     const auto elementParams = cryptoParams->GetElementParams();
2313     uint32_t m = elementParams->GetCyclotomicOrder();
2314 
2315     std::vector<uint32_t> autoIndices(indexList.size());
2316 
2317     if (IsPowerOfTwo(m)) {  // power-of-two cyclotomics
2318       for (size_t i = 0; i < indexList.size(); i++) {
2319         auto ccInst = origPrivateKey->GetCryptoContext();
2320         // CKKS Packing
2321         if (ccInst->getSchemeId() == "CKKS")
2322           autoIndices[i] = FindAutomorphismIndex2nComplex(indexList[i], m);
2323         else
2324           autoIndices[i] = FindAutomorphismIndex2n(indexList[i], m);
2325       }
2326 
2327     } else {  // cyclic groups
2328       for (size_t i = 0; i < indexList.size(); i++)
2329         autoIndices[i] = FindAutomorphismIndexCyclic(
2330             indexList[i], m, encodingParams->GetPlaintextGenerator());
2331     }
2332 
2333     if (publicKey)
2334       // NTRU-based scheme
2335       return EvalAutomorphismKeyGen(publicKey, origPrivateKey, autoIndices);
2336     else
2337       // RLWE-based scheme
2338       return EvalAutomorphismKeyGen(origPrivateKey, autoIndices);
2339   }
2340 
2341   /**
2342    * Virtual function for evaluating automorphism of ciphertext at index i
2343    *
2344    * @param ciphertext the input ciphertext.
2345    * @param i automorphism index
2346    * @param &evalKeys - reference to the vector of evaluation keys generated
2347    * by EvalAutomorphismKeyGen.
2348    * @return resulting ciphertext
2349    */
2350   virtual Ciphertext<Element> EvalAutomorphism(
2351       ConstCiphertext<Element> ciphertext, usint i,
2352       const std::map<usint, LPEvalKey<Element>> &evalKeys,
2353       CALLER_INFO_ARGS_HDR) const = 0;
2354 
2355   /**
2356    * Moves i-th slot to slot 0
2357    *
2358    * @param ciphertext.
2359    * @param i the index.
2360    * @param &evalAtIndexKeys - reference to the map of evaluation keys
2361    * generated by EvalAtIndexKeyGen.
2362    * @return resulting ciphertext
2363    */
EvalAtIndex(ConstCiphertext<Element> ciphertext,int32_t index,const std::map<usint,LPEvalKey<Element>> & evalAtIndexKeys)2364   virtual Ciphertext<Element> EvalAtIndex(
2365       ConstCiphertext<Element> ciphertext, int32_t index,
2366       const std::map<usint, LPEvalKey<Element>> &evalAtIndexKeys) const {
2367     if (!ciphertext)
2368       PALISADE_THROW(config_error, "Input ciphertext is nullptr");
2369     if (!evalAtIndexKeys.size())
2370       PALISADE_THROW(config_error, "Input index map is empty");
2371     const auto cryptoParams = ciphertext->GetCryptoParameters();
2372     const auto encodingParams = cryptoParams->GetEncodingParams();
2373     const auto elementParams = cryptoParams->GetElementParams();
2374     uint32_t m = elementParams->GetCyclotomicOrder();
2375 
2376     uint32_t autoIndex;
2377 
2378     // power-of-two cyclotomics
2379     if (IsPowerOfTwo(m)) {
2380       if (ciphertext->GetEncodingType() == CKKSPacked)
2381         autoIndex = FindAutomorphismIndex2nComplex(index, m);
2382       else
2383         autoIndex = FindAutomorphismIndex2n(index, m);
2384     } else {  // cyclic-group cyclotomics
2385       autoIndex = FindAutomorphismIndexCyclic(
2386           index, m, encodingParams->GetPlaintextGenerator());
2387     }
2388 
2389     return EvalAutomorphism(ciphertext, autoIndex, evalAtIndexKeys);
2390   }
2391 
2392   /**
2393    * Virtual function to generate automophism keys for a given private key;
2394    * Uses the private key for encryption
2395    *
2396    * @param privateKey private key.
2397    * @param indexList list of automorphism indices to be computed
2398    * @return returns the evaluation keys
2399    */
2400   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
2401   EvalAutomorphismKeyGen(const LPPrivateKey<Element> privateKey,
2402                          const std::vector<usint> &indexList) const = 0;
2403 
2404   /**
2405    * Virtual function to generate the automorphism keys for EvalSum; works
2406    * only for packed encoding
2407    *
2408    * @param privateKey private key.
2409    * @return returns the evaluation keys
2410    */
EvalSumKeyGen(const LPPrivateKey<Element> privateKey,const LPPublicKey<Element> publicKey)2411   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalSumKeyGen(
2412       const LPPrivateKey<Element> privateKey,
2413       const LPPublicKey<Element> publicKey) const {
2414     if (!privateKey)
2415       PALISADE_THROW(config_error, "Input private key is nullptr");
2416     /*
2417      * we don't validate publicKey as it is needed by NTRU-based scheme only
2418      * NTRU-based scheme only and it is checked for null later.
2419      */
2420     const auto cryptoParams = privateKey->GetCryptoParameters();
2421     const auto encodingParams = cryptoParams->GetEncodingParams();
2422     const auto elementParams = cryptoParams->GetElementParams();
2423 
2424     usint batchSize = encodingParams->GetBatchSize();
2425     usint m = elementParams->GetCyclotomicOrder();
2426 
2427     // stores automorphism indices needed for EvalSum
2428     std::vector<usint> indices;
2429 
2430     if (IsPowerOfTwo(m)) {
2431       auto ccInst = privateKey->GetCryptoContext();
2432       // CKKS Packing
2433       if (ccInst->getSchemeId() == "CKKS")
2434         indices = GenerateIndices2nComplex(batchSize, m);
2435       else
2436         indices = GenerateIndices_2n(batchSize, m);
2437     } else {  // Arbitrary cyclotomics
2438       usint g = encodingParams->GetPlaintextGenerator();
2439       for (int i = 0; i < floor(log2(batchSize)); i++) {
2440         indices.push_back(g);
2441         g = (g * g) % m;
2442       }
2443     }
2444 
2445     if (publicKey)  // NTRU-based scheme
2446       return EvalAutomorphismKeyGen(publicKey, privateKey, indices);
2447 
2448     // Regular RLWE scheme
2449     return EvalAutomorphismKeyGen(privateKey, indices);
2450   }
2451 
2452   /**
2453    * Virtual function to generate the automorphism keys for EvalSumRows; works
2454    * only for packed encoding
2455    *
2456    * @param privateKey private key.
2457    * @param publicKey public key.
2458    * @param rowSize size of rows in the matrix
2459    * @param subringDim subring dimension (set to cyclotomic order if set to 0)
2460    * @return returns the evaluation keys
2461    */
2462   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalSumRowsKeyGen(
2463       const LPPrivateKey<Element> privateKey,
2464       const LPPublicKey<Element> publicKey, usint rowSize,
2465       usint subringDim = 0) const {
2466     if (!privateKey)
2467       PALISADE_THROW(config_error, "Input private key is nullptr");
2468     const auto cryptoParams = privateKey->GetCryptoParameters();
2469     const auto encodingParams = cryptoParams->GetEncodingParams();
2470     const auto elementParams = cryptoParams->GetElementParams();
2471 
2472     usint m =
2473         (subringDim == 0) ? elementParams->GetCyclotomicOrder() : subringDim;
2474 
2475     // stores automorphism indices needed for EvalSum
2476     std::vector<usint> indices;
2477 
2478     if (IsPowerOfTwo(m)) {
2479       auto ccInst = privateKey->GetCryptoContext();
2480       // CKKS Packing
2481       if (ccInst->getSchemeId() == "CKKS")
2482         indices = GenerateIndices2nComplexRows(rowSize, m);
2483       else
2484         PALISADE_THROW(config_error,
2485                        "Matrix summation of row-vectors is only supported for "
2486                        "CKKSPackedEncoding.");
2487 
2488     } else {  // Arbitrary cyclotomics
2489       PALISADE_THROW(config_error,
2490                      "Matrix summation of row-vectors is not supported for "
2491                      "arbitrary cyclotomics.");
2492     }
2493 
2494     if (publicKey)
2495       // NTRU-based scheme
2496       return EvalAutomorphismKeyGen(publicKey, privateKey, indices);
2497     else
2498       // Regular RLWE scheme
2499       return EvalAutomorphismKeyGen(privateKey, indices);
2500   }
2501 
2502   /**
2503    * Virtual function to generate the automorphism keys for EvalSumCols; works
2504    * only for packed encoding
2505    *
2506    * @param privateKey private key.
2507    * @param publicKey public key.
2508    * @param rowSize size of rows in the matrix
2509    * @param colSize size of columns in the matrix
2510    * @return returns the evaluation keys
2511    */
EvalSumColsKeyGen(const LPPrivateKey<Element> privateKey,const LPPublicKey<Element> publicKey)2512   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalSumColsKeyGen(
2513       const LPPrivateKey<Element> privateKey,
2514       const LPPublicKey<Element> publicKey) const {
2515     if (!privateKey)
2516       PALISADE_THROW(config_error, "Input private key is nullptr");
2517     const auto cryptoParams = privateKey->GetCryptoParameters();
2518     const auto encodingParams = cryptoParams->GetEncodingParams();
2519     const auto elementParams = cryptoParams->GetElementParams();
2520 
2521     usint batchSize = encodingParams->GetBatchSize();
2522     usint m = elementParams->GetCyclotomicOrder();
2523 
2524     auto ccInst = privateKey->GetCryptoContext();
2525     // CKKS Packing
2526     if (ccInst->getSchemeId() == "CKKS") {
2527       // stores automorphism indices needed for EvalSum
2528       std::vector<usint> indices;
2529 
2530       if (IsPowerOfTwo(m)) {
2531         indices = GenerateIndices2nComplexCols(batchSize, m);
2532       } else {  // Arbitrary cyclotomics
2533         PALISADE_THROW(config_error,
2534                        "Matrix summation of column-vectors is not supported "
2535                        "for arbitrary cyclotomics.");
2536       }
2537 
2538       if (publicKey)
2539         // NTRU-based scheme
2540         return EvalAutomorphismKeyGen(publicKey, privateKey, indices);
2541       else
2542         // Regular RLWE scheme
2543         return EvalAutomorphismKeyGen(privateKey, indices);
2544     } else {
2545       PALISADE_THROW(config_error,
2546                      "Matrix summation of column-vectors is only supported for "
2547                      "CKKSPackedEncoding.");
2548     }
2549   }
2550 
2551   /**
2552    * Sums all elements in log (batch size) time - works only with packed
2553    * encoding
2554    *
2555    * @param ciphertext the input ciphertext.
2556    * @param batchSize size of the batch to be summed up
2557    * @param &evalKeys - reference to the map of evaluation keys generated by
2558    * EvalAutomorphismKeyGen.
2559    * @return resulting ciphertext
2560    */
EvalSum(ConstCiphertext<Element> ciphertext,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalKeys)2561   virtual Ciphertext<Element> EvalSum(
2562       ConstCiphertext<Element> ciphertext, usint batchSize,
2563       const std::map<usint, LPEvalKey<Element>> &evalKeys) const {
2564     if (!ciphertext)
2565       PALISADE_THROW(config_error, "Input ciphertext is nullptr");
2566     if (!evalKeys.size())
2567       PALISADE_THROW(config_error, "Input index map is empty");
2568     const shared_ptr<LPCryptoParameters<Element>> cryptoParams =
2569         ciphertext->GetCryptoParameters();
2570     Ciphertext<Element> newCiphertext(
2571         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
2572 
2573     const auto encodingParams = cryptoParams->GetEncodingParams();
2574     const auto elementParams = cryptoParams->GetElementParams();
2575 
2576     usint m = elementParams->GetCyclotomicOrder();
2577 
2578     if ((encodingParams->GetBatchSize() == 0)) {
2579       PALISADE_THROW(
2580           config_error,
2581           "EvalSum: Packed encoding parameters 'batch size' is not set; "
2582           "Please "
2583           "check the EncodingParams passed to the crypto context.");
2584     } else {
2585       if (IsPowerOfTwo(m)) {
2586         if (ciphertext->GetEncodingType() == CKKSPacked)
2587           newCiphertext =
2588               EvalSum2nComplex(batchSize, m, evalKeys, newCiphertext);
2589         else
2590           newCiphertext = EvalSum_2n(batchSize, m, evalKeys, newCiphertext);
2591 
2592       } else {  // Arbitrary cyclotomics
2593         if (encodingParams->GetPlaintextGenerator() == 0) {
2594           PALISADE_THROW(config_error,
2595                          "EvalSum: Packed encoding parameters 'plaintext "
2596                          "generator' is not set; Please check the "
2597                          "EncodingParams passed to the crypto context.");
2598         } else {
2599           usint g = encodingParams->GetPlaintextGenerator();
2600           for (int i = 0; i < floor(log2(batchSize)); i++) {
2601             auto ea = EvalAutomorphism(newCiphertext, g, evalKeys);
2602             newCiphertext = EvalAdd(newCiphertext, ea);
2603             g = (g * g) % m;
2604           }
2605         }
2606       }
2607     }
2608 
2609     return newCiphertext;
2610   }
2611 
2612   /**
2613    * Sums all elements over row-vectors in a matrix - works only with packed
2614    * encoding
2615    *
2616    * @param ciphertext the input ciphertext.
2617    * @param rowSize size of rows in the matrix
2618    * @param &evalKeys - reference to the map of evaluation keys generated by
2619    * @param subringDim the current cyclotomic order/subring dimension. If set to
2620    * 0, we use the full cyclotomic order. EvalAutomorphismKeyGen.
2621    * @return resulting ciphertext
2622    */
2623   virtual Ciphertext<Element> EvalSumRows(
2624       ConstCiphertext<Element> ciphertext, usint rowSize,
2625       const std::map<usint, LPEvalKey<Element>> &evalKeys,
2626       usint subringDim = 0) const {
2627     if (!ciphertext)
2628       PALISADE_THROW(config_error, "Input ciphertext is nullptr");
2629     if (!evalKeys.size())
2630       PALISADE_THROW(config_error, "Input index map is empty");
2631     const shared_ptr<LPCryptoParameters<Element>> cryptoParams =
2632         ciphertext->GetCryptoParameters();
2633     Ciphertext<Element> newCiphertext(
2634         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
2635 
2636     const auto encodingParams = cryptoParams->GetEncodingParams();
2637     const auto elementParams = cryptoParams->GetElementParams();
2638 
2639     usint m =
2640         (subringDim == 0) ? elementParams->GetCyclotomicOrder() : subringDim;
2641 
2642     if ((encodingParams->GetBatchSize() == 0)) {
2643       PALISADE_THROW(
2644           config_error,
2645           "EvalSum: Packed encoding parameters 'batch size' is not set; "
2646           "Please "
2647           "check the EncodingParams passed to the crypto context.");
2648     } else {
2649       if (IsPowerOfTwo(m)) {
2650         if (ciphertext->GetEncodingType() == CKKSPacked)
2651           newCiphertext =
2652               EvalSum2nComplexRows(rowSize, m, evalKeys, newCiphertext);
2653         else
2654           PALISADE_THROW(config_error,
2655                          "Matrix summation of row-vectors is only supported "
2656                          "for CKKS packed encoding.");
2657 
2658       } else {  // Arbitrary cyclotomics
2659         PALISADE_THROW(config_error,
2660                        "Matrix summation of row-vectors is not supported for "
2661                        "arbitrary cyclotomics.");
2662       }
2663     }
2664 
2665     return newCiphertext;
2666   }
2667 
2668   /**
2669    * Sums all elements over column-vectors in a matrix - works only with
2670    * packed encoding
2671    *
2672    * @param ciphertext the input ciphertext.
2673    * @param rowSize size of rows in the matrixs
2674    * @param &evalKeys - reference to the map of evaluation keys generated by
2675    * EvalAutomorphismKeyGen.
2676    * @return resulting ciphertext
2677    */
EvalSumCols(ConstCiphertext<Element> ciphertext,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalKeys,const std::map<usint,LPEvalKey<Element>> & rightEvalKeys)2678   virtual Ciphertext<Element> EvalSumCols(
2679       ConstCiphertext<Element> ciphertext, usint batchSize,
2680       const std::map<usint, LPEvalKey<Element>> &evalKeys,
2681       const std::map<usint, LPEvalKey<Element>> &rightEvalKeys) const {
2682     if (!ciphertext)
2683       PALISADE_THROW(config_error, "Input ciphertext is nullptr");
2684     if (!evalKeys.size())
2685       PALISADE_THROW(config_error, "Input evalKeys map is empty");
2686     if (!rightEvalKeys.size())
2687       PALISADE_THROW(config_error, "Input rightEvalKeys map is empty");
2688     const shared_ptr<LPCryptoParameters<Element>> cryptoParams =
2689         ciphertext->GetCryptoParameters();
2690     Ciphertext<Element> newCiphertext(
2691         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
2692 
2693     const auto encodingParams = cryptoParams->GetEncodingParams();
2694     const auto elementParams = cryptoParams->GetElementParams();
2695 
2696     usint m = elementParams->GetCyclotomicOrder();
2697 
2698     if ((encodingParams->GetBatchSize() == 0)) {
2699       PALISADE_THROW(
2700           config_error,
2701           "EvalSumCols: Packed encoding parameters 'batch size' is not set; "
2702           "Please check the EncodingParams passed to the crypto context.");
2703     } else {
2704       if (ciphertext->GetEncodingType() == CKKSPacked) {
2705         if (IsPowerOfTwo(m)) {
2706           newCiphertext =
2707               EvalSum2nComplex(batchSize, m, evalKeys, newCiphertext);
2708 
2709           std::vector<std::complex<double>> mask(m / 4);
2710           for (size_t i = 0; i < mask.size(); i++) {
2711             if (i % batchSize == 0)
2712               mask[i] = 1;
2713             else
2714               mask[i] = 0;
2715           }
2716 
2717           auto cc = ciphertext->GetCryptoContext();
2718 
2719           Plaintext plaintext = cc->MakeCKKSPackedPlaintext(mask, 1);
2720 
2721           newCiphertext = EvalMult(newCiphertext, plaintext);
2722 
2723           newCiphertext =
2724               EvalSum2nComplexCols(batchSize, m, rightEvalKeys, newCiphertext);
2725 
2726         } else {  // Arbitrary cyclotomics
2727           PALISADE_THROW(config_error,
2728                          "Matrix summation of column-vectors is not supported "
2729                          "for arbitrary cyclotomics.");
2730         }
2731       } else {
2732         PALISADE_THROW(config_error,
2733                        "Matrix summation of column-vectors is only supported "
2734                        "for CKKS packed encoding.");
2735       }
2736     }
2737 
2738     return newCiphertext;
2739   }
2740 
2741   /**
2742    * Evaluates inner product in batched encoding
2743    *
2744    * @param ciphertext1 first vector.
2745    * @param ciphertext2 second vector.
2746    * @param batchSize size of the batch to be summed up
2747    * @param &evalSumKeys - reference to the map of evaluation keys generated
2748    * by EvalAutomorphismKeyGen.
2749    * @param &evalMultKey - reference to the evaluation key generated by
2750    * EvalMultKeyGen.
2751    * @return resulting ciphertext
2752    */
EvalInnerProduct(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalSumKeys,const LPEvalKey<Element> evalMultKey)2753   virtual Ciphertext<Element> EvalInnerProduct(
2754       ConstCiphertext<Element> ciphertext1,
2755       ConstCiphertext<Element> ciphertext2, usint batchSize,
2756       const std::map<usint, LPEvalKey<Element>> &evalSumKeys,
2757       const LPEvalKey<Element> evalMultKey) const {
2758     Ciphertext<Element> result =
2759         EvalMult(ciphertext1, ciphertext2, evalMultKey);
2760 
2761     result = EvalSum(result, batchSize, evalSumKeys);
2762 
2763     // add a random number to all slots except for the first one so that no
2764     // information is leaked
2765     // if (ciphertext1->GetEncodingType() != CKKSPacked)
2766     //   result = AddRandomNoise(result);
2767     return result;
2768   }
2769 
2770   /**
2771    * Evaluates inner product in batched encoding
2772    *
2773    * @param ciphertext1 first vector.
2774    * @param plaintext plaintext.
2775    * @param batchSize size of the batch to be summed up
2776    * @param &evalSumKeys - reference to the map of evaluation keys generated
2777    * by EvalAutomorphismKeyGen.
2778    * @param &evalMultKey - reference to the evaluation key generated by
2779    * EvalMultKeyGen.
2780    * @return resulting ciphertext
2781    */
EvalInnerProduct(ConstCiphertext<Element> ciphertext1,ConstPlaintext plaintext,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalSumKeys)2782   virtual Ciphertext<Element> EvalInnerProduct(
2783       ConstCiphertext<Element> ciphertext1, ConstPlaintext plaintext,
2784       usint batchSize,
2785       const std::map<usint, LPEvalKey<Element>> &evalSumKeys) const {
2786     Ciphertext<Element> result = EvalMult(ciphertext1, plaintext);
2787 
2788     result = EvalSum(result, batchSize, evalSumKeys);
2789 
2790     // add a random number to all slots except for the first one so that no
2791     // information is leaked
2792     // if (ciphertext1->GetEncodingType() != CKKSPacked)
2793     //   result = AddRandomNoise(result);
2794     return result;
2795   }
2796 
2797   /**
2798    * Merges multiple ciphertexts with encrypted results in slot 0 into a
2799    * single ciphertext The slot assignment is done based on the order of
2800    * ciphertexts in the vector
2801    *
2802    * @param ciphertextVector vector of ciphertexts to be merged.
2803    * @param &evalKeys - reference to the map of evaluation keys generated by
2804    * EvalAutomorphismKeyGen.
2805    * @return resulting ciphertext
2806    */
EvalMerge(const vector<Ciphertext<Element>> & ciphertextVector,const std::map<usint,LPEvalKey<Element>> & evalKeys)2807   virtual Ciphertext<Element> EvalMerge(
2808       const vector<Ciphertext<Element>> &ciphertextVector,
2809       const std::map<usint, LPEvalKey<Element>> &evalKeys) const {
2810     if (ciphertextVector.size() == 0)
2811       PALISADE_THROW(math_error,
2812                      "EvalMerge: the vector of ciphertexts to be merged "
2813                      "cannot be empty");
2814 
2815     const shared_ptr<LPCryptoParameters<Element>> cryptoParams =
2816         ciphertextVector[0]->GetCryptoParameters();
2817     Ciphertext<Element> newCiphertext(
2818         std::make_shared<CiphertextImpl<Element>>(*(ciphertextVector[0])));
2819 
2820     auto cc = ciphertextVector[0]->GetCryptoContext();
2821 
2822     Plaintext plaintext;
2823     if (ciphertextVector[0]->GetEncodingType() == CKKSPacked) {
2824       std::vector<std::complex<double>> plaintextVector({{1, 0}, {0, 0}});
2825       plaintext = cc->MakeCKKSPackedPlaintext(plaintextVector);
2826     } else {
2827       std::vector<int64_t> plaintextVector = {1, 0};
2828       plaintext = cc->MakePackedPlaintext(plaintextVector);
2829     }
2830 
2831     newCiphertext = EvalMult(newCiphertext, plaintext);
2832 
2833     for (size_t i = 1; i < ciphertextVector.size(); i++) {
2834       newCiphertext = EvalAdd(
2835           newCiphertext, EvalAtIndex(EvalMult(ciphertextVector[i], plaintext),
2836                                      -(int32_t)i, evalKeys));
2837     }
2838 
2839     return newCiphertext;
2840   }
2841 
2842   /* Maintenance procedure used in the exact RNS variant of CKKS
2843    * @param c1 input ciphertext.
2844    * @param targetLevel The number of the level we want to take this
2845    * ciphertext to. Levels are numbered from 0 (all towers) to
2846    * GetNumberOfTowers()-1 (one remaining tower).
2847    * @return A ciphertext containing the same value as c1, but at level
2848    * targetLevel.
2849    */
AdjustLevelWithRescale(Ciphertext<Element> & c1,uint32_t targetLevel)2850   virtual Ciphertext<Element> AdjustLevelWithRescale(
2851       Ciphertext<Element> &c1, uint32_t targetLevel) const {
2852     std::string errMsg =
2853         "AdjustLevelWithoutRescale is not implemented for this scheme.";
2854     PALISADE_THROW(not_implemented_error, errMsg);
2855   }
2856 
2857  private:
GenerateIndices_2n(usint batchSize,usint m)2858   std::vector<usint> GenerateIndices_2n(usint batchSize, usint m) const {
2859     // stores automorphism indices needed for EvalSum
2860     std::vector<usint> indices;
2861 
2862     if (batchSize > 1) {
2863       usint g = 5;
2864       for (int i = 0; i < ceil(log2(batchSize)) - 1; i++) {
2865         indices.push_back(g);
2866         g = (g * g) % m;
2867       }
2868       if (2 * batchSize < m)
2869         indices.push_back(g);
2870       else
2871         indices.push_back(m - 1);
2872     }
2873 
2874     return indices;
2875   }
2876 
GenerateIndices2nComplex(usint batchSize,usint m)2877   std::vector<usint> GenerateIndices2nComplex(usint batchSize, usint m) const {
2878     // stores automorphism indices needed for EvalSum
2879     std::vector<usint> indices;
2880 
2881     // generator
2882     int32_t g = 5;
2883     usint gFinal = g;
2884 
2885     for (size_t j = 0; j < ceil(log2(batchSize)); j++) {
2886       indices.push_back(gFinal);
2887       g = (g * g) % m;
2888 
2889       gFinal = g;
2890     }
2891 
2892     return indices;
2893   }
2894 
GenerateIndices2nComplexRows(usint rowSize,usint m)2895   std::vector<usint> GenerateIndices2nComplexRows(usint rowSize,
2896                                                   usint m) const {
2897     // stores automorphism indices needed for EvalSum
2898     std::vector<usint> indices;
2899 
2900     usint colSize = m / (4 * rowSize);
2901 
2902     // generator
2903     int32_t g0 = 5;
2904     usint g = 0;
2905 
2906     int32_t f = (NativeInteger(g0).ModExp(rowSize, m)).ConvertToInt();
2907 
2908     for (size_t j = 0; j < ceil(log2(colSize)); j++) {
2909       g = f;
2910 
2911       indices.push_back(g);
2912 
2913       f = (f * f) % m;
2914     }
2915 
2916     return indices;
2917   }
2918 
GenerateIndices2nComplexCols(usint batchSize,usint m)2919   std::vector<usint> GenerateIndices2nComplexCols(usint batchSize,
2920                                                   usint m) const {
2921     // stores automorphism indices needed for EvalSum
2922     std::vector<usint> indices;
2923 
2924     // generator
2925     int32_t g = NativeInteger(5).ModInverse(m).ConvertToInt();
2926     usint gFinal = g;
2927 
2928     for (size_t j = 0; j < ceil(log2(batchSize)); j++) {
2929       indices.push_back(gFinal);
2930       g = (g * g) % m;
2931 
2932       gFinal = g;
2933     }
2934 
2935     return indices;
2936   }
2937 
EvalSum_2n(usint batchSize,usint m,const std::map<usint,LPEvalKey<Element>> & evalKeys,ConstCiphertext<Element> ciphertext)2938   Ciphertext<Element> EvalSum_2n(
2939       usint batchSize, usint m,
2940       const std::map<usint, LPEvalKey<Element>> &evalKeys,
2941       ConstCiphertext<Element> ciphertext) const {
2942     Ciphertext<Element> newCiphertext(
2943         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
2944 
2945     if (batchSize > 1) {
2946       usint g = 5;
2947       for (int i = 0; i < ceil(log2(batchSize)) - 1; i++) {
2948         newCiphertext = EvalAdd(newCiphertext,
2949                                 EvalAutomorphism(newCiphertext, g, evalKeys));
2950         g = (g * g) % m;
2951       }
2952       if (2 * batchSize < m)
2953         newCiphertext = EvalAdd(newCiphertext,
2954                                 EvalAutomorphism(newCiphertext, g, evalKeys));
2955       else
2956         newCiphertext = EvalAdd(
2957             newCiphertext, EvalAutomorphism(newCiphertext, m - 1, evalKeys));
2958     }
2959 
2960     return newCiphertext;
2961   }
2962 
EvalSum2nComplex(usint batchSize,usint m,const std::map<usint,LPEvalKey<Element>> & evalKeys,ConstCiphertext<Element> ciphertext)2963   Ciphertext<Element> EvalSum2nComplex(
2964       usint batchSize, usint m,
2965       const std::map<usint, LPEvalKey<Element>> &evalKeys,
2966       ConstCiphertext<Element> ciphertext) const {
2967     Ciphertext<Element> newCiphertext(
2968         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
2969 
2970     // generator
2971     int32_t g = 5;
2972     usint gFinal = g;
2973 
2974     for (int i = 0; i < ceil(log2(batchSize)); i++) {
2975       newCiphertext = EvalAdd(
2976           newCiphertext, EvalAutomorphism(newCiphertext, gFinal, evalKeys));
2977       g = (g * g) % m;
2978 
2979       gFinal = g;
2980     }
2981 
2982     return newCiphertext;
2983   }
2984 
EvalSum2nComplexRows(usint rowSize,usint m,const std::map<usint,LPEvalKey<Element>> & evalKeys,ConstCiphertext<Element> ciphertext)2985   Ciphertext<Element> EvalSum2nComplexRows(
2986       usint rowSize, usint m,
2987       const std::map<usint, LPEvalKey<Element>> &evalKeys,
2988       ConstCiphertext<Element> ciphertext) const {
2989     Ciphertext<Element> newCiphertext(
2990         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
2991 
2992     usint colSize = m / (4 * rowSize);
2993 
2994     // generator
2995     int32_t g0 = 5;
2996     usint g = 0;
2997     int32_t f = (NativeInteger(g0).ModExp(rowSize, m)).ConvertToInt();
2998 
2999     for (size_t j = 0; j < ceil(log2(colSize)); j++) {
3000       g = f;
3001 
3002       newCiphertext =
3003           EvalAdd(newCiphertext, EvalAutomorphism(newCiphertext, g, evalKeys));
3004 
3005       f = (f * f) % m;
3006     }
3007 
3008     return newCiphertext;
3009   }
3010 
EvalSum2nComplexCols(usint batchSize,usint m,const std::map<usint,LPEvalKey<Element>> & evalKeys,ConstCiphertext<Element> ciphertext)3011   Ciphertext<Element> EvalSum2nComplexCols(
3012       usint batchSize, usint m,
3013       const std::map<usint, LPEvalKey<Element>> &evalKeys,
3014       ConstCiphertext<Element> ciphertext) const {
3015     Ciphertext<Element> newCiphertext(
3016         std::make_shared<CiphertextImpl<Element>>(*ciphertext));
3017 
3018     // generator
3019     int32_t g = NativeInteger(5).ModInverse(m).ConvertToInt();
3020     usint gFinal = g;
3021 
3022     for (int i = 0; i < ceil(log2(batchSize)); i++) {
3023       newCiphertext = EvalAdd(
3024           newCiphertext, EvalAutomorphism(newCiphertext, gFinal, evalKeys));
3025       g = (g * g) % m;
3026 
3027       gFinal = g;
3028     }
3029 
3030     return newCiphertext;
3031   }
3032 };
3033 
3034 /**
3035  * @brief main implementation class to capture essential cryptoparameters of
3036  * any LBC system
3037  * @tparam Element a ring element.
3038  */
3039 template <typename Element>
3040 class LPCryptoParameters : public Serializable {
3041  public:
LPCryptoParameters()3042   LPCryptoParameters() {}
3043 
~LPCryptoParameters()3044   virtual ~LPCryptoParameters() {}
3045 
3046   /**
3047    * Returns the value of plaintext modulus p
3048    *
3049    * @return the plaintext modulus.
3050    */
GetPlaintextModulus()3051   virtual const PlaintextModulus &GetPlaintextModulus() const {
3052     return m_encodingParams->GetPlaintextModulus();
3053   }
3054 
3055   /**
3056    * Returns the reference to IL params
3057    *
3058    * @return the ring element parameters.
3059    */
GetElementParams()3060   virtual const shared_ptr<typename Element::Params> GetElementParams() const {
3061     return m_params;
3062   }
3063 
3064   /**
3065    * Returns the reference to encoding params
3066    *
3067    * @return the encoding parameters.
3068    */
GetEncodingParams()3069   virtual const EncodingParams GetEncodingParams() const {
3070     return m_encodingParams;
3071   }
3072 
3073   /**
3074    * Sets the value of plaintext modulus p
3075    */
SetPlaintextModulus(const PlaintextModulus & plaintextModulus)3076   virtual void SetPlaintextModulus(const PlaintextModulus &plaintextModulus) {
3077     m_encodingParams->SetPlaintextModulus(plaintextModulus);
3078   }
3079 
3080   virtual bool operator==(const LPCryptoParameters<Element> &cmp) const = 0;
3081   virtual bool operator!=(const LPCryptoParameters<Element> &cmp) const {
3082     return !(*this == cmp);
3083   }
3084 
3085   /**
3086    * Overload to allow printing of parameters to an iostream
3087    * NOTE that the implementation relies on calling the virtual
3088    * PrintParameters method
3089    * @param out - the stream to print to
3090    * @param item - reference to the item to print
3091    * @return the stream
3092    */
3093   friend std::ostream &operator<<(std::ostream &out,
3094                                   const LPCryptoParameters &item) {
3095     item.PrintParameters(out);
3096     return out;
3097   }
3098 
GetRelinWindow()3099   virtual usint GetRelinWindow() const { return 0; }
3100 
GetDepth()3101   virtual int GetDepth() const { return 0; }
GetMaxDepth()3102   virtual size_t GetMaxDepth() const { return 0; }
3103 
GetDiscreteGaussianGenerator()3104   virtual const typename Element::DggType &GetDiscreteGaussianGenerator()
3105       const {
3106     PALISADE_THROW(config_error, "No DGG Available for this parameter set");
3107   }
3108 
3109   /**
3110    * Sets the reference to element params
3111    */
SetElementParams(shared_ptr<typename Element::Params> params)3112   virtual void SetElementParams(shared_ptr<typename Element::Params> params) {
3113     m_params = params;
3114   }
3115 
3116   /**
3117    * Sets the reference to encoding params
3118    */
SetEncodingParams(EncodingParams encodingParams)3119   virtual void SetEncodingParams(EncodingParams encodingParams) {
3120     m_encodingParams = encodingParams;
3121   }
3122 
3123   template <class Archive>
save(Archive & ar,std::uint32_t const version)3124   void save(Archive &ar, std::uint32_t const version) const {
3125     ar(::cereal::make_nvp("elp", m_params));
3126     ar(::cereal::make_nvp("enp", m_encodingParams));
3127   }
3128 
3129   template <class Archive>
load(Archive & ar,std::uint32_t const version)3130   void load(Archive &ar, std::uint32_t const version) {
3131     if (version > SerializedVersion()) {
3132       PALISADE_THROW(deserialize_error,
3133                      "serialized object version " + std::to_string(version) +
3134                          " is from a later version of the library");
3135     }
3136     ar(::cereal::make_nvp("elp", m_params));
3137     ar(::cereal::make_nvp("enp", m_encodingParams));
3138   }
3139 
SerializedObjectName()3140   std::string SerializedObjectName() const { return "CryptoParameters"; }
SerializedVersion()3141   static uint32_t SerializedVersion() { return 1; }
3142 
3143  protected:
LPCryptoParameters(const PlaintextModulus & plaintextModulus)3144   explicit LPCryptoParameters(const PlaintextModulus &plaintextModulus) {
3145     m_encodingParams = std::make_shared<EncodingParamsImpl>(plaintextModulus);
3146   }
3147 
LPCryptoParameters(shared_ptr<typename Element::Params> params,const PlaintextModulus & plaintextModulus)3148   LPCryptoParameters(shared_ptr<typename Element::Params> params,
3149                      const PlaintextModulus &plaintextModulus) {
3150     m_params = params;
3151     m_encodingParams = std::make_shared<EncodingParamsImpl>(plaintextModulus);
3152   }
3153 
LPCryptoParameters(shared_ptr<typename Element::Params> params,EncodingParams encodingParams)3154   LPCryptoParameters(shared_ptr<typename Element::Params> params,
3155                      EncodingParams encodingParams) {
3156     m_params = params;
3157     m_encodingParams = encodingParams;
3158   }
3159 
LPCryptoParameters(LPCryptoParameters<Element> * from,shared_ptr<typename Element::Params> newElemParms)3160   LPCryptoParameters(LPCryptoParameters<Element> *from,
3161                      shared_ptr<typename Element::Params> newElemParms) {
3162     *this = *from;
3163     m_params = newElemParms;
3164   }
3165 
PrintParameters(std::ostream & out)3166   virtual void PrintParameters(std::ostream &out) const {
3167     out << "Element Parameters: " << *m_params << std::endl;
3168     out << "Encoding Parameters: " << *m_encodingParams << std::endl;
3169   }
3170 
3171  private:
3172   // element-specific parameters
3173   shared_ptr<typename Element::Params> m_params;
3174 
3175   // encoding-specific parameters
3176   EncodingParams m_encodingParams;
3177 };
3178 
3179 // forward decl so SchemeIdentifier works
3180 template <typename Element>
3181 class LPPublicKeyEncryptionScheme;
3182 
3183 template <typename Element>
3184 class PalisadeSchemeIdentifier {
3185   string schemeName;
3186   LPPublicKeyEncryptionScheme<Element> *(*schemeMaker)();
3187 
3188  public:
PalisadeSchemeIdentifier(string n,LPPublicKeyEncryptionScheme<Element> (* f)())3189   PalisadeSchemeIdentifier(string n,
3190                            LPPublicKeyEncryptionScheme<Element> (*f)())
3191       : schemeName(n), schemeMaker(f) {}
3192 
GetName()3193   const string &GetName() const { return schemeName; }
GetScheme()3194   LPPublicKeyEncryptionScheme<Element> *GetScheme() const {
3195     return (*schemeMaker)();
3196   }
3197 };
3198 
3199 /**
3200  * @brief Abstract interface for public key encryption schemes
3201  * @tparam Element a ring element.
3202  */
3203 template <typename Element>
3204 class LPPublicKeyEncryptionScheme {
3205  private:
CheckMultipartyDecryptCompatibility(ConstCiphertext<Element> & ciphertext,CALLER_INFO_ARGS_HDR)3206   inline void CheckMultipartyDecryptCompatibility(
3207       ConstCiphertext<Element> &ciphertext, CALLER_INFO_ARGS_HDR) const {
3208     if (ciphertext->GetElements().size() > 2) {
3209       std::string errorMsg(std::string("ciphertext's number of elements is [") +
3210                            std::to_string(ciphertext->GetElements().size()) +
3211                            "]. Must be 2 or less for Multiparty Decryption." +
3212                            CALLER_INFO);
3213       PALISADE_THROW(palisade_error, errorMsg);
3214     }
3215   }
3216 
3217  public:
LPPublicKeyEncryptionScheme()3218   LPPublicKeyEncryptionScheme() {}
3219 
~LPPublicKeyEncryptionScheme()3220   virtual ~LPPublicKeyEncryptionScheme() {}
3221 
3222   virtual bool operator==(const LPPublicKeyEncryptionScheme &sch) const = 0;
3223 
3224   virtual bool operator!=(const LPPublicKeyEncryptionScheme &sch) const {
3225     return !(*this == sch);
3226   }
3227 
3228   /**
3229    * Enable features with a bit mast of PKESchemeFeature codes
3230    * @param mask
3231    */
Enable(usint mask)3232   virtual void Enable(usint mask) {
3233     if (mask & ENCRYPTION) Enable(ENCRYPTION);
3234 
3235     if (mask & PRE) Enable(PRE);
3236 
3237     if (mask & SHE) Enable(SHE);
3238 
3239     if (mask & LEVELEDSHE) Enable(LEVELEDSHE);
3240 
3241     if (mask & MULTIPARTY) Enable(MULTIPARTY);
3242   }
3243 
GetEnabled()3244   virtual usint GetEnabled() const {
3245     usint flag = 0;
3246 
3247     if (m_algorithmEncryption != nullptr) flag |= ENCRYPTION;
3248     if (m_algorithmPRE != nullptr) flag |= PRE;
3249     if (m_algorithmSHE != nullptr) flag |= SHE;
3250     if (m_algorithmLeveledSHE != nullptr) flag |= LEVELEDSHE;
3251     if (m_algorithmMultiparty != nullptr) flag |= MULTIPARTY;
3252 
3253     return flag;
3254   }
3255 
3256   // instantiated in the scheme implementation class
3257   virtual void Enable(PKESchemeFeature feature) = 0;
3258 
3259   /////////////////////////////////////////
3260   // wrapper for LPParameterSelectionAlgorithm
3261   //
3262 
3263   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
3264                          int32_t evalAddCount = 0, int32_t evalMultCount = 0,
3265                          int32_t keySwitchCount = 0, size_t dcrtBits = 0,
3266                          uint32_t n = 0) const {
3267     if (m_algorithmParamsGen) {
3268       return m_algorithmParamsGen->ParamsGen(cryptoParams, evalAddCount,
3269                                              evalMultCount, keySwitchCount,
3270                                              dcrtBits, n);
3271     }
3272     PALISADE_THROW(not_implemented_error,
3273                    "Parameter generation operation has not been implemented");
3274   }
3275 
3276   /////////////////////////////////////////
3277   // the three functions below are wrappers for things in
3278   // LPEncryptionAlgorithm (ENCRYPT)
3279   //
3280 
Encrypt(const LPPublicKey<Element> publicKey,const Element & plaintext)3281   virtual Ciphertext<Element> Encrypt(const LPPublicKey<Element> publicKey,
3282                                       const Element &plaintext) const {
3283     if (m_algorithmEncryption) {
3284       return m_algorithmEncryption->Encrypt(publicKey, plaintext);
3285     } else {
3286       PALISADE_THROW(config_error, "Encrypt operation has not been enabled");
3287     }
3288   }
3289 
Encrypt(const LPPrivateKey<Element> privateKey,const Element & plaintext)3290   virtual Ciphertext<Element> Encrypt(const LPPrivateKey<Element> privateKey,
3291                                       const Element &plaintext) const {
3292     if (m_algorithmEncryption) {
3293       return m_algorithmEncryption->Encrypt(privateKey, plaintext);
3294     }
3295     PALISADE_THROW(config_error, "Encrypt operation has not been enabled");
3296   }
3297 
Decrypt(const LPPrivateKey<Element> privateKey,ConstCiphertext<Element> ciphertext,NativePoly * plaintext)3298   virtual DecryptResult Decrypt(const LPPrivateKey<Element> privateKey,
3299                                 ConstCiphertext<Element> ciphertext,
3300                                 NativePoly *plaintext) const {
3301     if (m_algorithmEncryption) {
3302       return m_algorithmEncryption->Decrypt(privateKey, ciphertext, plaintext);
3303     }
3304     PALISADE_THROW(config_error, "Decrypt operation has not been enabled");
3305   }
3306 
Decrypt(const LPPrivateKey<Element> privateKey,ConstCiphertext<Element> ciphertext,Poly * plaintext)3307   virtual DecryptResult Decrypt(const LPPrivateKey<Element> privateKey,
3308                                 ConstCiphertext<Element> ciphertext,
3309                                 Poly *plaintext) const {
3310     if (m_algorithmEncryption) {
3311       return m_algorithmEncryption->Decrypt(privateKey, ciphertext, plaintext);
3312     }
3313     PALISADE_THROW(config_error, "Decrypt operation has not been enabled");
3314   }
3315 
KeyGen(CryptoContext<Element> cc,bool makeSparse)3316   virtual LPKeyPair<Element> KeyGen(CryptoContext<Element> cc,
3317                                     bool makeSparse) {
3318     if (m_algorithmEncryption) {
3319       auto kp = m_algorithmEncryption->KeyGen(cc, makeSparse);
3320       kp.publicKey->SetKeyTag(kp.secretKey->GetKeyTag());
3321       return kp;
3322     }
3323     PALISADE_THROW(config_error, "KeyGen operation has not been enabled");
3324   }
3325 
3326   /////////////////////////////////////////
3327   // the three functions below are wrappers for things in LPPREAlgorithm (PRE)
3328   //
3329 
ReKeyGen(const LPPublicKey<Element> newKey,const LPPrivateKey<Element> origPrivateKey)3330   virtual LPEvalKey<Element> ReKeyGen(
3331       const LPPublicKey<Element> newKey,
3332       const LPPrivateKey<Element> origPrivateKey) const {
3333     if (m_algorithmPRE) {
3334       auto rk = m_algorithmPRE->ReKeyGen(newKey, origPrivateKey);
3335       rk->SetKeyTag(newKey->GetKeyTag());
3336       return rk;
3337     }
3338     PALISADE_THROW(config_error, "ReKeyGen operation has not been enabled");
3339   }
3340 
ReEncrypt(const LPEvalKey<Element> evalKey,ConstCiphertext<Element> ciphertext,const LPPublicKey<Element> publicKey)3341   virtual Ciphertext<Element> ReEncrypt(
3342       const LPEvalKey<Element> evalKey, ConstCiphertext<Element> ciphertext,
3343       const LPPublicKey<Element> publicKey) const {
3344     if (m_algorithmPRE) {
3345       auto ct = m_algorithmPRE->ReEncrypt(evalKey, ciphertext, publicKey);
3346       ct->SetKeyTag(evalKey->GetKeyTag());
3347       return ct;
3348     }
3349     PALISADE_THROW(config_error, "ReEncrypt operation has not been enabled");
3350   }
3351 
3352   /////////////////////////////////////////
3353   // the three functions below are wrappers for things in
3354   // LPMultipartyAlgorithm (Multiparty)
3355   //
3356 
3357   // Wrapper for Multiparty Key Gen
MultipartyKeyGen(CryptoContext<Element> cc,const LPPublicKey<Element> pk1,bool makeSparse,bool PRE)3358   virtual LPKeyPair<Element> MultipartyKeyGen(CryptoContext<Element> cc,
3359                                               const LPPublicKey<Element> pk1,
3360                                               bool makeSparse, bool PRE) {
3361     if (m_algorithmMultiparty) {
3362       if (!cc) PALISADE_THROW(config_error, "Input crypto context is nullptr");
3363       if (!pk1) PALISADE_THROW(config_error, "Input public key is empty");
3364       auto k =
3365           m_algorithmMultiparty->MultipartyKeyGen(cc, pk1, makeSparse, PRE);
3366       k.publicKey->SetKeyTag(k.secretKey->GetKeyTag());
3367       return k;
3368     }
3369     PALISADE_THROW(config_error,
3370                    "MultipartyKeyGen operation has not been enabled");
3371   }
3372 
3373   // Wrapper for Multiparty Key Gen
MultipartyKeyGen(CryptoContext<Element> cc,const vector<LPPrivateKey<Element>> & secretKeys,bool makeSparse)3374   virtual LPKeyPair<Element> MultipartyKeyGen(
3375       CryptoContext<Element> cc,
3376       const vector<LPPrivateKey<Element>> &secretKeys, bool makeSparse) {
3377     if (m_algorithmMultiparty) {
3378       if (!cc) PALISADE_THROW(config_error, "Input crypto context is nullptr");
3379       if (!secretKeys.size())
3380         PALISADE_THROW(config_error, "Input private key vector is empty");
3381       auto k =
3382           m_algorithmMultiparty->MultipartyKeyGen(cc, secretKeys, makeSparse);
3383       k.publicKey->SetKeyTag(k.secretKey->GetKeyTag());
3384       return k;
3385     }
3386     PALISADE_THROW(config_error,
3387                    "MultipartyKeyGen operation has not been enabled");
3388   }
3389 
MultipartyDecryptMain(const LPPrivateKey<Element> privateKey,ConstCiphertext<Element> ciphertext)3390   virtual Ciphertext<Element> MultipartyDecryptMain(
3391       const LPPrivateKey<Element> privateKey,
3392       ConstCiphertext<Element> ciphertext) const {
3393     if (m_algorithmMultiparty) {
3394       CheckMultipartyDecryptCompatibility(ciphertext);
3395       auto ct =
3396           m_algorithmMultiparty->MultipartyDecryptMain(privateKey, ciphertext);
3397       ct->SetKeyTag(privateKey->GetKeyTag());
3398       return ct;
3399     }
3400     PALISADE_THROW(config_error,
3401                    "MultipartyDecryptMain operation has not been enabled");
3402   }
3403 
MultipartyDecryptLead(const LPPrivateKey<Element> privateKey,ConstCiphertext<Element> ciphertext)3404   virtual Ciphertext<Element> MultipartyDecryptLead(
3405       const LPPrivateKey<Element> privateKey,
3406       ConstCiphertext<Element> ciphertext) const {
3407     if (m_algorithmMultiparty) {
3408       CheckMultipartyDecryptCompatibility(ciphertext);
3409       auto ct =
3410           m_algorithmMultiparty->MultipartyDecryptLead(privateKey, ciphertext);
3411       ct->SetKeyTag(privateKey->GetKeyTag());
3412       return ct;
3413     }
3414     PALISADE_THROW(config_error,
3415                    "MultipartyDecryptLead operation has not been enabled");
3416   }
3417 
MultipartyDecryptFusion(const vector<Ciphertext<Element>> & ciphertextVec,NativePoly * plaintext)3418   virtual DecryptResult MultipartyDecryptFusion(
3419       const vector<Ciphertext<Element>> &ciphertextVec,
3420       NativePoly *plaintext) const {
3421     if (m_algorithmMultiparty) {
3422       return m_algorithmMultiparty->MultipartyDecryptFusion(ciphertextVec,
3423                                                             plaintext);
3424     }
3425     PALISADE_THROW(config_error,
3426                    "MultipartyDecrypt operation has not been enabled");
3427   }
3428 
MultipartyDecryptFusion(const vector<Ciphertext<Element>> & ciphertextVec,Poly * plaintext)3429   virtual DecryptResult MultipartyDecryptFusion(
3430       const vector<Ciphertext<Element>> &ciphertextVec, Poly *plaintext) const {
3431     if (m_algorithmMultiparty) {
3432       return m_algorithmMultiparty->MultipartyDecryptFusion(ciphertextVec,
3433                                                             plaintext);
3434     }
3435     PALISADE_THROW(config_error,
3436                    "MultipartyDecrypt operation has not been enabled");
3437   }
3438 
MultiKeySwitchGen(const LPPrivateKey<Element> originalPrivateKey,const LPPrivateKey<Element> newPrivateKey,const LPEvalKey<Element> ek)3439   virtual LPEvalKey<Element> MultiKeySwitchGen(
3440       const LPPrivateKey<Element> originalPrivateKey,
3441       const LPPrivateKey<Element> newPrivateKey,
3442       const LPEvalKey<Element> ek) const {
3443     if (m_algorithmMultiparty) {
3444       if (!originalPrivateKey)
3445         PALISADE_THROW(config_error, "Input first private key is nullptr");
3446       if (!newPrivateKey)
3447         PALISADE_THROW(config_error, "Input second private key is nullptr");
3448       if (!ek) PALISADE_THROW(config_error, "Input evaluation key is nullptr");
3449       auto k = m_algorithmMultiparty->MultiKeySwitchGen(originalPrivateKey,
3450                                                         newPrivateKey, ek);
3451       k->SetKeyTag(newPrivateKey->GetKeyTag());
3452       return k;
3453     }
3454     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3455   }
3456 
3457   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
3458   MultiEvalAutomorphismKeyGen(
3459       const LPPrivateKey<Element> privateKey,
3460       const shared_ptr<std::map<usint, LPEvalKey<Element>>> eAuto,
3461       const std::vector<usint> &indexList, const std::string &keyId = "") {
3462     if (m_algorithmMultiparty) {
3463       if (!privateKey)
3464         PALISADE_THROW(config_error, "Input private key is nullptr");
3465       if (!eAuto)
3466         PALISADE_THROW(config_error, "Input evaluation key map is nullptr");
3467       if (!indexList.size())
3468         PALISADE_THROW(config_error, "Input index vector is empty");
3469       auto keys = m_algorithmMultiparty->MultiEvalAutomorphismKeyGen(
3470           privateKey, eAuto, indexList);
3471       for (auto it = keys->begin(); it != keys->end(); ++it) {
3472         if (it->second) {
3473           it->second->SetKeyTag(keyId);
3474         }
3475       }
3476       return keys;
3477     }
3478     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3479   }
3480 
3481   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
3482   MultiEvalAtIndexKeyGen(
3483       const LPPrivateKey<Element> privateKey,
3484       const shared_ptr<std::map<usint, LPEvalKey<Element>>> eAuto,
3485       const std::vector<int32_t> &indexList, const std::string &keyId = "") {
3486     if (m_algorithmMultiparty) {
3487       if (!privateKey)
3488         PALISADE_THROW(config_error, "Input private key is nullptr");
3489       if (!eAuto)
3490         PALISADE_THROW(config_error, "Input evaluation key map is nullptr");
3491       if (!indexList.size())
3492         PALISADE_THROW(config_error, "Input index vector is empty");
3493       auto keys = m_algorithmMultiparty->MultiEvalAtIndexKeyGen(
3494           privateKey, eAuto, indexList);
3495       for (auto it = keys->begin(); it != keys->end(); ++it) {
3496         if (it->second) {
3497           it->second->SetKeyTag(keyId);
3498         }
3499       }
3500       return keys;
3501     }
3502     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3503   }
3504 
3505   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> MultiEvalSumKeyGen(
3506       const LPPrivateKey<Element> privateKey,
3507       const shared_ptr<std::map<usint, LPEvalKey<Element>>> eSum,
3508       const std::string &keyId = "") {
3509     if (m_algorithmMultiparty) {
3510       if (!privateKey)
3511         PALISADE_THROW(config_error, "Input private key is nullptr");
3512       if (!eSum)
3513         PALISADE_THROW(config_error, "Input evaluation key map is nullptr");
3514       auto keys = m_algorithmMultiparty->MultiEvalSumKeyGen(privateKey, eSum);
3515       for (auto it = keys->begin(); it != keys->end(); ++it) {
3516         if (it->second) {
3517           it->second->SetKeyTag(keyId);
3518         }
3519       }
3520       return keys;
3521     }
3522     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3523   }
3524 
3525   virtual LPEvalKey<Element> MultiAddEvalKeys(LPEvalKey<Element> a,
3526                                               LPEvalKey<Element> b,
3527                                               const std::string &keyId = "") {
3528     if (m_algorithmMultiparty) {
3529       if (!a)
3530         PALISADE_THROW(config_error, "Input first evaluation key is nullptr");
3531       if (!b)
3532         PALISADE_THROW(config_error, "Input second evaluation key is nullptr");
3533 
3534       auto key = m_algorithmMultiparty->MultiAddEvalKeys(a, b);
3535       key->SetKeyTag(keyId);
3536       return key;
3537     }
3538     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3539   }
3540 
3541   virtual LPEvalKey<Element> MultiMultEvalKey(LPEvalKey<Element> evalKey,
3542                                               LPPrivateKey<Element> sk,
3543                                               const std::string &keyId = "") {
3544     if (m_algorithmMultiparty) {
3545       if (!evalKey)
3546         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
3547       if (!sk) PALISADE_THROW(config_error, "Input private key is nullptr");
3548 
3549       auto key = m_algorithmMultiparty->MultiMultEvalKey(evalKey, sk);
3550       key->SetKeyTag(keyId);
3551       return key;
3552     }
3553     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3554   }
3555 
3556   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> MultiAddEvalSumKeys(
3557       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es1,
3558       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es2,
3559       const std::string &keyId = "") {
3560     if (m_algorithmMultiparty) {
3561       if (!es1)
3562         PALISADE_THROW(config_error,
3563                        "Input first evaluation key map is nullptr");
3564       if (!es2)
3565         PALISADE_THROW(config_error,
3566                        "Input second evaluation key map is nullptr");
3567       auto keys = m_algorithmMultiparty->MultiAddEvalSumKeys(es1, es2);
3568       for (auto it = keys->begin(); it != keys->end(); ++it) {
3569         if (it->second) {
3570           it->second->SetKeyTag(keyId);
3571         }
3572       }
3573       return keys;
3574     }
3575     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3576   }
3577 
3578   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
3579   MultiAddEvalAutomorphismKeys(
3580       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es1,
3581       const shared_ptr<std::map<usint, LPEvalKey<Element>>> es2,
3582       const std::string &keyId = "") {
3583     if (m_algorithmMultiparty) {
3584       if (!es1)
3585         PALISADE_THROW(config_error,
3586                        "Input first evaluation key map is nullptr");
3587       if (!es2)
3588         PALISADE_THROW(config_error,
3589                        "Input second evaluation key map is nullptr");
3590 
3591       auto keys = m_algorithmMultiparty->MultiAddEvalAutomorphismKeys(es1, es2);
3592       for (auto it = keys->begin(); it != keys->end(); ++it) {
3593         if (it->second) {
3594           it->second->SetKeyTag(keyId);
3595         }
3596       }
3597       return keys;
3598     }
3599     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3600   }
3601 
3602   virtual LPPublicKey<Element> MultiAddPubKeys(LPPublicKey<Element> pubKey1,
3603                                                LPPublicKey<Element> pubKey2,
3604                                                const std::string &keyId = "") {
3605     if (m_algorithmMultiparty) {
3606       if (!pubKey1)
3607         PALISADE_THROW(config_error, "Input first public key is nullptr");
3608       if (!pubKey2)
3609         PALISADE_THROW(config_error, "Input second public key is nullptr");
3610 
3611       auto key = m_algorithmMultiparty->MultiAddPubKeys(pubKey1, pubKey2);
3612       key->SetKeyTag(keyId);
3613       return key;
3614     }
3615     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3616   }
3617 
3618   virtual LPEvalKey<Element> MultiAddEvalMultKeys(
3619       LPEvalKey<Element> evalKey1, LPEvalKey<Element> evalKey2,
3620       const std::string &keyId = "") {
3621     if (m_algorithmMultiparty) {
3622       if (!evalKey1)
3623         PALISADE_THROW(config_error, "Input first evaluation key is nullptr");
3624       if (!evalKey2)
3625         PALISADE_THROW(config_error, "Input second evaluation key is nullptr");
3626       auto key =
3627           m_algorithmMultiparty->MultiAddEvalMultKeys(evalKey1, evalKey2);
3628       key->SetKeyTag(keyId);
3629       return key;
3630     }
3631     PALISADE_THROW(config_error, "Multiparty capability has not been enabled");
3632   }
3633 
3634   /////////////////////////////////////////
3635   // the three functions below are wrappers for things in LPSHEAlgorithm (SHE)
3636   //
3637 
AddRandomNoise(ConstCiphertext<Element> ciphertext)3638   virtual Ciphertext<Element> AddRandomNoise(
3639       ConstCiphertext<Element> ciphertext) const {
3640     if (m_algorithmSHE) {
3641       if (!ciphertext)
3642         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3643       return m_algorithmSHE->AddRandomNoise(ciphertext);
3644     }
3645     PALISADE_THROW(config_error,
3646                    "AddRandomNoise operation has not been enabled");
3647   }
3648 
EvalAdd(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2)3649   virtual Ciphertext<Element> EvalAdd(
3650       ConstCiphertext<Element> ciphertext1,
3651       ConstCiphertext<Element> ciphertext2) const {
3652     if (m_algorithmSHE) {
3653       if (!ciphertext1)
3654         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3655       if (!ciphertext2)
3656         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3657       return m_algorithmSHE->EvalAdd(ciphertext1, ciphertext2);
3658     }
3659     PALISADE_THROW(config_error, "EvalAdd operation has not been enabled");
3660   }
3661 
EvalAddInPlace(Ciphertext<Element> & ciphertext1,ConstCiphertext<Element> ciphertext2)3662   virtual void EvalAddInPlace(Ciphertext<Element> &ciphertext1,
3663                               ConstCiphertext<Element> ciphertext2) const {
3664     if (m_algorithmSHE) {
3665       if (!ciphertext1)
3666         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3667       if (!ciphertext2)
3668         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3669       m_algorithmSHE->EvalAddInPlace(ciphertext1, ciphertext2);
3670       return;
3671     }
3672     PALISADE_THROW(config_error,
3673                    "EvalAddInPlace operation has not been enabled");
3674   }
3675 
EvalAddMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2)3676   virtual Ciphertext<Element> EvalAddMutable(
3677       Ciphertext<Element> &ciphertext1,
3678       Ciphertext<Element> &ciphertext2) const {
3679     if (m_algorithmSHE) {
3680       if (!ciphertext1)
3681         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3682       if (!ciphertext2)
3683         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3684       return m_algorithmSHE->EvalAddMutable(ciphertext1, ciphertext2);
3685     }
3686     PALISADE_THROW(config_error, "EvalAdd operation has not been enabled");
3687   }
3688 
EvalAdd(ConstCiphertext<Element> ciphertext1,ConstPlaintext plaintext)3689   virtual Ciphertext<Element> EvalAdd(ConstCiphertext<Element> ciphertext1,
3690                                       ConstPlaintext plaintext) const {
3691     if (m_algorithmSHE) {
3692       if (!ciphertext1)
3693         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3694       if (!plaintext)
3695         PALISADE_THROW(config_error, "Input plaintext is nullptr");
3696       return m_algorithmSHE->EvalAdd(ciphertext1, plaintext);
3697     }
3698     PALISADE_THROW(config_error, "EvalAdd operation has not been enabled");
3699   }
3700 
EvalAddMutable(Ciphertext<Element> & ciphertext1,Plaintext plaintext)3701   virtual Ciphertext<Element> EvalAddMutable(Ciphertext<Element> &ciphertext1,
3702                                              Plaintext plaintext) const {
3703     if (m_algorithmSHE) {
3704       if (!ciphertext1)
3705         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3706       if (!plaintext)
3707         PALISADE_THROW(config_error, "Input plaintext is nullptr");
3708       return m_algorithmSHE->EvalAddMutable(ciphertext1, plaintext);
3709     }
3710     PALISADE_THROW(config_error, "EvalAdd operation has not been enabled");
3711   }
3712 
EvalAdd(ConstCiphertext<Element> ciphertext1,double constant)3713   virtual Ciphertext<Element> EvalAdd(ConstCiphertext<Element> ciphertext1,
3714                                       double constant) const {
3715     if (m_algorithmSHE) {
3716       if (!ciphertext1)
3717         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3718       return m_algorithmSHE->EvalAdd(ciphertext1, constant);
3719     }
3720     PALISADE_THROW(config_error, "EvalAdd operation has not been enabled");
3721   }
3722 
EvalLinearWSum(vector<Ciphertext<Element>> ciphertexts,vector<double> constants)3723   virtual Ciphertext<Element> EvalLinearWSum(
3724       vector<Ciphertext<Element>> ciphertexts, vector<double> constants) const {
3725     if (m_algorithmSHE) {
3726       if (!ciphertexts.size())
3727         PALISADE_THROW(config_error, "Input ciphertext vector is empty");
3728       return m_algorithmSHE->EvalLinearWSum(ciphertexts, constants);
3729     }
3730     PALISADE_THROW(config_error,
3731                    "EvalLinearWSum operation has not been enabled");
3732   }
3733 
EvalLinearWSumMutable(vector<Ciphertext<Element>> ciphertexts,vector<double> constants)3734   virtual Ciphertext<Element> EvalLinearWSumMutable(
3735       vector<Ciphertext<Element>> ciphertexts, vector<double> constants) const {
3736     if (m_algorithmSHE) {
3737       if (!ciphertexts.size())
3738         PALISADE_THROW(config_error, "Input ciphertext vector is empty");
3739       return m_algorithmSHE->EvalLinearWSumMutable(ciphertexts, constants);
3740     }
3741     PALISADE_THROW(config_error,
3742                    "EvalLinearWSum operation has not been enabled");
3743   }
3744 
EvalSub(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2)3745   virtual Ciphertext<Element> EvalSub(
3746       ConstCiphertext<Element> ciphertext1,
3747       ConstCiphertext<Element> ciphertext2) const {
3748     if (m_algorithmSHE) {
3749       if (!ciphertext1)
3750         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3751       if (!ciphertext2)
3752         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3753       return m_algorithmSHE->EvalSub(ciphertext1, ciphertext2);
3754     }
3755     PALISADE_THROW(config_error, "EvalSub operation has not been enabled");
3756   }
3757 
EvalSubMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2)3758   virtual Ciphertext<Element> EvalSubMutable(
3759       Ciphertext<Element> &ciphertext1,
3760       Ciphertext<Element> &ciphertext2) const {
3761     if (m_algorithmSHE) {
3762       if (!ciphertext1)
3763         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3764       if (!ciphertext2)
3765         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3766       return m_algorithmSHE->EvalSubMutable(ciphertext1, ciphertext2);
3767     }
3768     PALISADE_THROW(config_error, "EvalSub operation has not been enabled");
3769   }
3770 
EvalSub(ConstCiphertext<Element> ciphertext1,ConstPlaintext plaintext)3771   virtual Ciphertext<Element> EvalSub(ConstCiphertext<Element> ciphertext1,
3772                                       ConstPlaintext plaintext) const {
3773     if (m_algorithmSHE) {
3774       if (!ciphertext1)
3775         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3776       if (!plaintext)
3777         PALISADE_THROW(config_error, "Input plaintext is nullptr");
3778       return m_algorithmSHE->EvalSub(ciphertext1, plaintext);
3779     }
3780     PALISADE_THROW(config_error, "EvalSub operation has not been enabled");
3781   }
3782 
EvalSubMutable(Ciphertext<Element> & ciphertext1,Plaintext plaintext)3783   virtual Ciphertext<Element> EvalSubMutable(Ciphertext<Element> &ciphertext1,
3784                                              Plaintext plaintext) const {
3785     if (m_algorithmSHE) {
3786       if (!ciphertext1)
3787         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3788       if (!plaintext)
3789         PALISADE_THROW(config_error, "Input plaintext is nullptr");
3790       return m_algorithmSHE->EvalSubMutable(ciphertext1, plaintext);
3791     }
3792     PALISADE_THROW(config_error, "EvalSub operation has not been enabled");
3793   }
3794 
EvalSub(ConstCiphertext<Element> ciphertext1,double constant)3795   virtual Ciphertext<Element> EvalSub(ConstCiphertext<Element> ciphertext1,
3796                                       double constant) const {
3797     if (m_algorithmSHE) {
3798       if (!ciphertext1)
3799         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3800       return m_algorithmSHE->EvalSub(ciphertext1, constant);
3801     }
3802     PALISADE_THROW(config_error, "EvalSub operation has not been enabled");
3803   }
3804 
EvalMult(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2)3805   virtual Ciphertext<Element> EvalMult(
3806       ConstCiphertext<Element> ciphertext1,
3807       ConstCiphertext<Element> ciphertext2) const {
3808     if (m_algorithmSHE) {
3809       if (!ciphertext1)
3810         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3811       if (!ciphertext2)
3812         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3813       return m_algorithmSHE->EvalMult(ciphertext1, ciphertext2);
3814     }
3815     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3816   }
3817 
EvalMultMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2)3818   virtual Ciphertext<Element> EvalMultMutable(
3819       Ciphertext<Element> &ciphertext1,
3820       Ciphertext<Element> &ciphertext2) const {
3821     if (m_algorithmSHE) {
3822       if (!ciphertext1)
3823         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3824       if (!ciphertext2)
3825         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3826       return m_algorithmSHE->EvalMultMutable(ciphertext1, ciphertext2);
3827     }
3828     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3829   }
3830 
EvalMult(ConstCiphertext<Element> ciphertext,ConstPlaintext plaintext)3831   virtual Ciphertext<Element> EvalMult(ConstCiphertext<Element> ciphertext,
3832                                        ConstPlaintext plaintext) const {
3833     if (m_algorithmSHE) {
3834       if (!ciphertext)
3835         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3836       if (!plaintext)
3837         PALISADE_THROW(config_error, "Input plaintext is nullptr");
3838       return m_algorithmSHE->EvalMult(ciphertext, plaintext);
3839     }
3840     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3841   }
3842 
EvalMultMutable(Ciphertext<Element> & ciphertext,Plaintext plaintext)3843   virtual Ciphertext<Element> EvalMultMutable(Ciphertext<Element> &ciphertext,
3844                                               Plaintext plaintext) const {
3845     if (m_algorithmSHE) {
3846       if (!ciphertext)
3847         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3848       if (!plaintext)
3849         PALISADE_THROW(config_error, "Input plaintext is nullptr");
3850       return m_algorithmSHE->EvalMultMutable(ciphertext, plaintext);
3851     }
3852     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3853   }
3854 
EvalMult(ConstCiphertext<Element> ciphertext1,double constant)3855   virtual Ciphertext<Element> EvalMult(ConstCiphertext<Element> ciphertext1,
3856                                        double constant) const {
3857     if (m_algorithmSHE) {
3858       if (!ciphertext1)
3859         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3860       return m_algorithmSHE->EvalMult(ciphertext1, constant);
3861     }
3862     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3863   }
3864 
EvalMultMutable(Ciphertext<Element> & ciphertext1,double constant)3865   virtual Ciphertext<Element> EvalMultMutable(Ciphertext<Element> &ciphertext1,
3866                                               double constant) const {
3867     if (m_algorithmSHE) {
3868       if (!ciphertext1)
3869         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3870       return m_algorithmSHE->EvalMultMutable(ciphertext1, constant);
3871     }
3872     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3873   }
3874 
EvalMult(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2,const LPEvalKey<Element> evalKey)3875   virtual Ciphertext<Element> EvalMult(ConstCiphertext<Element> ciphertext1,
3876                                        ConstCiphertext<Element> ciphertext2,
3877                                        const LPEvalKey<Element> evalKey) const {
3878     if (m_algorithmSHE) {
3879       if (!ciphertext1)
3880         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3881       if (!ciphertext2)
3882         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3883       if (!evalKey)
3884         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
3885       return m_algorithmSHE->EvalMult(ciphertext1, ciphertext2, evalKey);
3886     }
3887     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3888   }
3889 
EvalMultMutable(Ciphertext<Element> & ciphertext1,Ciphertext<Element> & ciphertext2,const LPEvalKey<Element> evalKey)3890   virtual Ciphertext<Element> EvalMultMutable(
3891       Ciphertext<Element> &ciphertext1, Ciphertext<Element> &ciphertext2,
3892       const LPEvalKey<Element> evalKey) const {
3893     if (m_algorithmSHE) {
3894       if (!ciphertext1)
3895         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
3896       if (!ciphertext2)
3897         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
3898       if (!evalKey)
3899         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
3900       auto ct =
3901           m_algorithmSHE->EvalMultMutable(ciphertext1, ciphertext2, evalKey);
3902       return ct;
3903     }
3904     PALISADE_THROW(config_error, "EvalMult operation has not been enabled");
3905   }
3906 
EvalMultMany(const vector<Ciphertext<Element>> & ciphertext,const vector<LPEvalKey<Element>> & evalKeys)3907   virtual Ciphertext<Element> EvalMultMany(
3908       const vector<Ciphertext<Element>> &ciphertext,
3909       const vector<LPEvalKey<Element>> &evalKeys) const {
3910     if (m_algorithmSHE) {
3911       if (!ciphertext.size())
3912         PALISADE_THROW(config_error, "Input ciphertext vector is empty");
3913       if (!evalKeys.size())
3914         PALISADE_THROW(config_error, "Input evaluation key vector is empty");
3915       return m_algorithmSHE->EvalMultMany(ciphertext, evalKeys);
3916     }
3917     PALISADE_THROW(config_error, "EvalMultMany operation has not been enabled");
3918   }
3919 
EvalAddMany(const vector<Ciphertext<Element>> & ciphertexts)3920   virtual Ciphertext<Element> EvalAddMany(
3921       const vector<Ciphertext<Element>> &ciphertexts) const {
3922     if (m_algorithmSHE) {
3923       if (!ciphertexts.size())
3924         PALISADE_THROW(config_error, "Input ciphertext vector is empty");
3925       return m_algorithmSHE->EvalAddMany(ciphertexts);
3926     }
3927     PALISADE_THROW(config_error, "EvalAddMany operation has not been enabled");
3928   }
3929 
EvalAddManyInPlace(vector<Ciphertext<Element>> & ciphertexts)3930   virtual Ciphertext<Element> EvalAddManyInPlace(
3931       vector<Ciphertext<Element>> &ciphertexts) const {
3932     if (m_algorithmSHE) {
3933       if (!ciphertexts.size())
3934         PALISADE_THROW(config_error, "Input ciphertext vector is empty");
3935       return m_algorithmSHE->EvalAddManyInPlace(ciphertexts);
3936     }
3937     PALISADE_THROW(config_error,
3938                    "EvalAddManyInPlace operation has not been enabled");
3939   }
3940 
EvalNegate(ConstCiphertext<Element> ciphertext)3941   virtual Ciphertext<Element> EvalNegate(
3942       ConstCiphertext<Element> ciphertext) const {
3943     if (m_algorithmSHE) {
3944       if (!ciphertext)
3945         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3946       auto ct = m_algorithmSHE->EvalNegate(ciphertext);
3947       return ct;
3948     }
3949     PALISADE_THROW(config_error, "EvalNegate operation has not been enabled");
3950   }
3951 
3952   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
EvalAutomorphismKeyGen(const LPPublicKey<Element> publicKey,const LPPrivateKey<Element> origPrivateKey,const std::vector<usint> & indexList)3953   EvalAutomorphismKeyGen(const LPPublicKey<Element> publicKey,
3954                          const LPPrivateKey<Element> origPrivateKey,
3955                          const std::vector<usint> &indexList) const {
3956     if (m_algorithmSHE) {
3957       if (!publicKey)
3958         PALISADE_THROW(config_error, "Input public key is nullptr");
3959       if (!origPrivateKey)
3960         PALISADE_THROW(config_error, "Input private key is nullptr");
3961       auto km = m_algorithmSHE->EvalAutomorphismKeyGen(
3962           publicKey, origPrivateKey, indexList);
3963       for (auto &k : *km) k.second->SetKeyTag(origPrivateKey->GetKeyTag());
3964       return km;
3965     }
3966     PALISADE_THROW(config_error,
3967                    "EvalAutomorphismKeyGen operation has not been enabled");
3968   }
3969 
EvalAtIndexKeyGen(const LPPublicKey<Element> publicKey,const LPPrivateKey<Element> origPrivateKey,const std::vector<int32_t> & indexList)3970   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalAtIndexKeyGen(
3971       const LPPublicKey<Element> publicKey,
3972       const LPPrivateKey<Element> origPrivateKey,
3973       const std::vector<int32_t> &indexList) const {
3974     if (m_algorithmSHE) {
3975       if (!origPrivateKey)
3976         PALISADE_THROW(config_error, "Input private key is nullptr");
3977       auto km = m_algorithmSHE->EvalAtIndexKeyGen(publicKey, origPrivateKey,
3978                                                   indexList);
3979       for (auto &k : *km) k.second->SetKeyTag(origPrivateKey->GetKeyTag());
3980       return km;
3981     }
3982     PALISADE_THROW(config_error,
3983                    "EvalAtIndexKeyGen operation has not been enabled");
3984   }
3985 
EvalAutomorphism(ConstCiphertext<Element> ciphertext,usint i,const std::map<usint,LPEvalKey<Element>> & evalKeys,CALLER_INFO_ARGS_HDR)3986   virtual Ciphertext<Element> EvalAutomorphism(
3987       ConstCiphertext<Element> ciphertext, usint i,
3988       const std::map<usint, LPEvalKey<Element>> &evalKeys,
3989       CALLER_INFO_ARGS_HDR) const {
3990     if (m_algorithmSHE) {
3991       if (!ciphertext)
3992         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
3993       if (!evalKeys.size())
3994         PALISADE_THROW(config_error, "Input evaluation key map is empty");
3995       auto ct = m_algorithmSHE->EvalAutomorphism(ciphertext, i, evalKeys);
3996       return ct;
3997     }
3998     std::string errorMsg(
3999         std::string("EvalAutomorphism operation has not been enabled") +
4000         CALLER_INFO);
4001     PALISADE_THROW(config_error, errorMsg);
4002   }
4003 
EvalAtIndex(ConstCiphertext<Element> ciphertext,usint i,const std::map<usint,LPEvalKey<Element>> & evalKeys)4004   virtual Ciphertext<Element> EvalAtIndex(
4005       ConstCiphertext<Element> ciphertext, usint i,
4006       const std::map<usint, LPEvalKey<Element>> &evalKeys) const {
4007     if (m_algorithmSHE) {
4008       if (!ciphertext)
4009         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4010       if (!evalKeys.size())
4011         PALISADE_THROW(config_error, "Input evaluation key map is empty");
4012       auto ct = m_algorithmSHE->EvalAtIndex(ciphertext, i, evalKeys);
4013       return ct;
4014     }
4015     PALISADE_THROW(config_error, "EvalAtIndex operation has not been enabled");
4016   }
4017 
EvalFastRotationPrecompute(ConstCiphertext<Element> ciphertext)4018   virtual shared_ptr<vector<Element>> EvalFastRotationPrecompute(
4019       ConstCiphertext<Element> ciphertext) const {
4020     if (m_algorithmSHE) {
4021       if (!ciphertext)
4022         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4023       auto ct = m_algorithmSHE->EvalFastRotationPrecompute(ciphertext);
4024       return ct;
4025     }
4026     PALISADE_THROW(config_error,
4027                    "EvalFastRotationPrecompute operation has not been enabled");
4028   }
4029 
EvalFastRotation(ConstCiphertext<Element> ciphertext,const usint index,const usint m,const shared_ptr<vector<Element>> digits)4030   virtual Ciphertext<Element> EvalFastRotation(
4031       ConstCiphertext<Element> ciphertext, const usint index, const usint m,
4032       const shared_ptr<vector<Element>> digits) const {
4033     if (m_algorithmSHE) {
4034       if (!ciphertext)
4035         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4036       auto ct = m_algorithmSHE->EvalFastRotation(ciphertext, index, m, digits);
4037       return ct;
4038     }
4039     PALISADE_THROW(config_error,
4040                    "EvalFastRotation operation has not been enabled");
4041   }
4042 
4043   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>>
EvalAutomorphismKeyGen(const LPPrivateKey<Element> privateKey,const std::vector<usint> & indexList)4044   EvalAutomorphismKeyGen(const LPPrivateKey<Element> privateKey,
4045                          const std::vector<usint> &indexList) const {
4046     if (m_algorithmSHE) {
4047       if (!privateKey)
4048         PALISADE_THROW(config_error, "Input private key is nullptr");
4049       auto km = m_algorithmSHE->EvalAutomorphismKeyGen(privateKey, indexList);
4050       for (auto &k : *km) k.second->SetKeyTag(privateKey->GetKeyTag());
4051       return km;
4052     }
4053     PALISADE_THROW(config_error,
4054                    "EvalAutomorphismKeyGen operation has not been enabled");
4055   }
4056 
EvalSumKeyGen(const LPPrivateKey<Element> privateKey,const LPPublicKey<Element> publicKey)4057   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalSumKeyGen(
4058       const LPPrivateKey<Element> privateKey,
4059       const LPPublicKey<Element> publicKey) const {
4060     if (m_algorithmSHE) {
4061       if (!privateKey)
4062         PALISADE_THROW(config_error, "Input private key is nullptr");
4063       auto km = m_algorithmSHE->EvalSumKeyGen(privateKey, publicKey);
4064       for (auto &k : *km) {
4065         k.second->SetKeyTag(privateKey->GetKeyTag());
4066       }
4067       return km;
4068     }
4069     PALISADE_THROW(config_error,
4070                    "EvalSumKeyGen operation has not been enabled");
4071   }
4072 
4073   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalSumRowsKeyGen(
4074       const LPPrivateKey<Element> privateKey,
4075       const LPPublicKey<Element> publicKey, usint rowSize,
4076       usint subringDim = 0) const {
4077     if (m_algorithmSHE) {
4078       if (!privateKey)
4079         PALISADE_THROW(config_error, "Input private key is nullptr");
4080       auto km = m_algorithmSHE->EvalSumRowsKeyGen(privateKey, publicKey,
4081                                                   rowSize, subringDim);
4082       for (auto &k : *km) {
4083         k.second->SetKeyTag(privateKey->GetKeyTag());
4084       }
4085       return km;
4086     }
4087     PALISADE_THROW(config_error,
4088                    "EvalSumRowsKeyGen operation has not been enabled");
4089   }
4090 
EvalSumColsKeyGen(const LPPrivateKey<Element> privateKey,const LPPublicKey<Element> publicKey)4091   virtual shared_ptr<std::map<usint, LPEvalKey<Element>>> EvalSumColsKeyGen(
4092       const LPPrivateKey<Element> privateKey,
4093       const LPPublicKey<Element> publicKey) const {
4094     if (m_algorithmSHE) {
4095       if (!privateKey)
4096         PALISADE_THROW(config_error, "Input private key is nullptr");
4097       auto km = m_algorithmSHE->EvalSumColsKeyGen(privateKey, publicKey);
4098       for (auto &k : *km) {
4099         k.second->SetKeyTag(privateKey->GetKeyTag());
4100       }
4101       return km;
4102     }
4103     PALISADE_THROW(config_error,
4104                    "EvalSumColsKeyGen operation has not been enabled");
4105   }
4106 
EvalSum(ConstCiphertext<Element> ciphertext,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalKeys)4107   virtual Ciphertext<Element> EvalSum(
4108       ConstCiphertext<Element> ciphertext, usint batchSize,
4109       const std::map<usint, LPEvalKey<Element>> &evalKeys) const {
4110     if (m_algorithmSHE) {
4111       if (!ciphertext)
4112         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4113       if (!evalKeys.size())
4114         PALISADE_THROW(config_error, "Input evaluation key map is empty");
4115       auto ct = m_algorithmSHE->EvalSum(ciphertext, batchSize, evalKeys);
4116       return ct;
4117     }
4118     PALISADE_THROW(config_error, "EvalSum operation has not been enabled");
4119   }
4120 
4121   virtual Ciphertext<Element> EvalSumRows(
4122       ConstCiphertext<Element> ciphertext, usint rowSize,
4123       const std::map<usint, LPEvalKey<Element>> &evalKeys,
4124       usint subringDim = 0) const {
4125     if (m_algorithmSHE) {
4126       if (!ciphertext)
4127         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4128       if (!evalKeys.size())
4129         PALISADE_THROW(config_error, "Input evaluation key map is empty");
4130       auto ct = m_algorithmSHE->EvalSumRows(ciphertext, rowSize, evalKeys,
4131                                             subringDim);
4132       return ct;
4133     }
4134     PALISADE_THROW(config_error, "EvalSumRow operation has not been enabled");
4135   }
4136 
EvalSumCols(ConstCiphertext<Element> ciphertext,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalKeys,const std::map<usint,LPEvalKey<Element>> & rightEvalKeys)4137   virtual Ciphertext<Element> EvalSumCols(
4138       ConstCiphertext<Element> ciphertext, usint batchSize,
4139       const std::map<usint, LPEvalKey<Element>> &evalKeys,
4140       const std::map<usint, LPEvalKey<Element>> &rightEvalKeys) const {
4141     if (m_algorithmSHE) {
4142       if (!evalKeys.size())
4143         PALISADE_THROW(config_error, "Input first evaluation key map is empty");
4144       if (!rightEvalKeys.size())
4145         PALISADE_THROW(config_error,
4146                        "Input second evaluation key map is empty");
4147       auto ct = m_algorithmSHE->EvalSumCols(ciphertext, batchSize, evalKeys,
4148                                             rightEvalKeys);
4149       return ct;
4150     }
4151     PALISADE_THROW(config_error, "EvalSumCols operation has not been enabled");
4152   }
4153 
EvalInnerProduct(ConstCiphertext<Element> ciphertext1,ConstCiphertext<Element> ciphertext2,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalSumKeys,const LPEvalKey<Element> evalMultKey)4154   virtual Ciphertext<Element> EvalInnerProduct(
4155       ConstCiphertext<Element> ciphertext1,
4156       ConstCiphertext<Element> ciphertext2, usint batchSize,
4157       const std::map<usint, LPEvalKey<Element>> &evalSumKeys,
4158       const LPEvalKey<Element> evalMultKey) const {
4159     if (m_algorithmSHE) {
4160       if (!ciphertext1)
4161         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
4162       if (!ciphertext2)
4163         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
4164       if (!evalSumKeys.size())
4165         PALISADE_THROW(config_error, "Input evaluation key map is empty");
4166       if (!evalMultKey)
4167         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
4168       auto ct = m_algorithmSHE->EvalInnerProduct(
4169           ciphertext1, ciphertext2, batchSize, evalSumKeys, evalMultKey);
4170       ct->SetKeyTag(evalSumKeys.begin()->second->GetKeyTag());
4171       return ct;
4172     }
4173     PALISADE_THROW(config_error,
4174                    "EvalInnerProduct operation has not been enabled");
4175   }
4176 
EvalMerge(const vector<Ciphertext<Element>> & ciphertextVector,const std::map<usint,LPEvalKey<Element>> & evalKeys)4177   virtual Ciphertext<Element> EvalMerge(
4178       const vector<Ciphertext<Element>> &ciphertextVector,
4179       const std::map<usint, LPEvalKey<Element>> &evalKeys) const {
4180     if (m_algorithmSHE) {
4181       if (!ciphertextVector.size())
4182         PALISADE_THROW(config_error, "Input ciphertext vector is empty");
4183       if (!evalKeys.size())
4184         PALISADE_THROW(config_error, "Input evaluation key map is empty");
4185       return m_algorithmSHE->EvalMerge(ciphertextVector, evalKeys);
4186     }
4187     PALISADE_THROW(config_error, "EvalMerge operation has not been enabled");
4188   }
4189 
EvalInnerProduct(ConstCiphertext<Element> ciphertext1,ConstPlaintext plaintext,usint batchSize,const std::map<usint,LPEvalKey<Element>> & evalSumKeys)4190   virtual Ciphertext<Element> EvalInnerProduct(
4191       ConstCiphertext<Element> ciphertext1, ConstPlaintext plaintext,
4192       usint batchSize,
4193       const std::map<usint, LPEvalKey<Element>> &evalSumKeys) const {
4194     if (m_algorithmSHE) {
4195       if (!ciphertext1)
4196         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
4197       if (!plaintext)
4198         PALISADE_THROW(config_error, "Input plaintext is nullptr");
4199       if (!evalSumKeys.size())
4200         PALISADE_THROW(config_error, "Input evaluation key map is empty");
4201       return m_algorithmSHE->EvalInnerProduct(ciphertext1, plaintext, batchSize,
4202                                               evalSumKeys);
4203     }
4204     PALISADE_THROW(config_error,
4205                    "EvalInnerProduct operation has not been enabled");
4206   }
4207 
KeySwitchGen(const LPPrivateKey<Element> originalPrivateKey,const LPPrivateKey<Element> newPrivateKey)4208   virtual LPEvalKey<Element> KeySwitchGen(
4209       const LPPrivateKey<Element> originalPrivateKey,
4210       const LPPrivateKey<Element> newPrivateKey) const {
4211     if (m_algorithmSHE) {
4212       if (!originalPrivateKey)
4213         PALISADE_THROW(config_error, "Input first private key is nullptr");
4214       if (!newPrivateKey)
4215         PALISADE_THROW(config_error, "Input second private key is nullptr");
4216       auto kp = m_algorithmSHE->KeySwitchGen(originalPrivateKey, newPrivateKey);
4217       kp->SetKeyTag(newPrivateKey->GetKeyTag());
4218       return kp;
4219     }
4220     PALISADE_THROW(config_error, "KeySwitchGen operation has not been enabled");
4221   }
4222 
KeySwitch(const LPEvalKey<Element> keySwitchHint,ConstCiphertext<Element> cipherText)4223   virtual Ciphertext<Element> KeySwitch(
4224       const LPEvalKey<Element> keySwitchHint,
4225       ConstCiphertext<Element> cipherText) const {
4226     if (m_algorithmSHE) {
4227       if (!keySwitchHint)
4228         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
4229       if (!cipherText)
4230         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4231       auto ct = m_algorithmSHE->KeySwitch(keySwitchHint, cipherText);
4232       return ct;
4233     }
4234     PALISADE_THROW(config_error, "KeySwitch operation has not been enabled");
4235   }
4236 
KeySwitchInPlace(const LPEvalKey<Element> keySwitchHint,Ciphertext<Element> & cipherText)4237   virtual void KeySwitchInPlace(const LPEvalKey<Element> keySwitchHint,
4238                                 Ciphertext<Element> &cipherText) const {
4239     if (m_algorithmSHE) {
4240       if (!keySwitchHint)
4241         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
4242       if (!cipherText)
4243         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4244       m_algorithmSHE->KeySwitchInPlace(keySwitchHint, cipherText);
4245       return;
4246     }
4247     PALISADE_THROW(config_error,
4248                    "KeySwitchInPlace operation has not been enabled");
4249   }
4250 
EvalMultKeyGen(const LPPrivateKey<Element> originalPrivateKey)4251   virtual LPEvalKey<Element> EvalMultKeyGen(
4252       const LPPrivateKey<Element> originalPrivateKey) const {
4253     if (m_algorithmSHE) {
4254       if (!originalPrivateKey)
4255         PALISADE_THROW(config_error, "Input private key is nullptr");
4256       auto ek = m_algorithmSHE->EvalMultKeyGen(originalPrivateKey);
4257       ek->SetKeyTag(originalPrivateKey->GetKeyTag());
4258       return ek;
4259     }
4260     PALISADE_THROW(config_error,
4261                    "EvalMultKeyGen operation has not been enabled");
4262   }
4263 
EvalMultKeysGen(const LPPrivateKey<Element> originalPrivateKey)4264   virtual vector<LPEvalKey<Element>> EvalMultKeysGen(
4265       const LPPrivateKey<Element> originalPrivateKey) const {
4266     if (m_algorithmSHE) {
4267       if (!originalPrivateKey)
4268         PALISADE_THROW(config_error, "Input private key is nullptr");
4269       auto ek = m_algorithmSHE->EvalMultKeysGen(originalPrivateKey);
4270       for (size_t i = 0; i < ek.size(); i++)
4271         ek[i]->SetKeyTag(originalPrivateKey->GetKeyTag());
4272       return ek;
4273     }
4274     PALISADE_THROW(config_error,
4275                    "EvalMultKeysGen operation has not been enabled");
4276   }
4277 
EvalMultAndRelinearize(ConstCiphertext<Element> ct1,ConstCiphertext<Element> ct2,const vector<LPEvalKey<Element>> & ek)4278   virtual Ciphertext<Element> EvalMultAndRelinearize(
4279       ConstCiphertext<Element> ct1, ConstCiphertext<Element> ct2,
4280       const vector<LPEvalKey<Element>> &ek) const {
4281     if (m_algorithmSHE) {
4282       if (!ct1)
4283         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
4284       if (!ct2)
4285         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
4286       if (!ek.size())
4287         PALISADE_THROW(config_error, "Input evaluation key vector is empty");
4288       return m_algorithmSHE->EvalMultAndRelinearize(ct1, ct2, ek);
4289     }
4290     PALISADE_THROW(config_error,
4291                    "EvalMultAndRelinearize operation has not been enabled");
4292   }
4293 
Relinearize(ConstCiphertext<Element> ciphertext,const vector<LPEvalKey<Element>> & ek)4294   virtual Ciphertext<Element> Relinearize(
4295       ConstCiphertext<Element> ciphertext,
4296       const vector<LPEvalKey<Element>> &ek) const {
4297     if (m_algorithmSHE) {
4298       if (!ciphertext)
4299         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4300       if (!ek.size())
4301         PALISADE_THROW(config_error, "Input evaluation key vector is empty");
4302       return m_algorithmSHE->Relinearize(ciphertext, ek);
4303     }
4304     PALISADE_THROW(config_error, "Relinearize operation has not been enabled");
4305   }
4306 
RelinearizeInPlace(Ciphertext<Element> & ciphertext,const vector<LPEvalKey<Element>> & ek)4307   virtual void RelinearizeInPlace(
4308       Ciphertext<Element> &ciphertext,
4309       const vector<LPEvalKey<Element>> &ek) const {
4310     if (m_algorithmSHE) {
4311       if (!ciphertext)
4312         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4313       if (!ek.size())
4314         PALISADE_THROW(config_error, "Input evaluation key vector is empty");
4315       return m_algorithmSHE->RelinearizeInPlace(ciphertext, ek);
4316     }
4317     PALISADE_THROW(config_error, "RelinearizeInPlace operation has not been enabled");
4318   }
4319 
4320   /////////////////////////////////////////
4321   // the functions below are wrappers for things in LPFHEAlgorithm (FHE)
4322   //
4323   // TODO: Add Bootstrap and any other FHE methods
4324 
4325   /////////////////////////////////////////
4326   // the functions below are wrappers for things in LPSHEAlgorithm (SHE)
4327   //
4328 
4329   virtual Ciphertext<Element> ModReduce(ConstCiphertext<Element> cipherText,
4330                                         size_t levels = 1) const {
4331     if (m_algorithmLeveledSHE) {
4332       if (!cipherText)
4333         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4334       auto ct = m_algorithmLeveledSHE->ModReduce(cipherText, levels);
4335       ct->SetKeyTag(cipherText->GetKeyTag());
4336       return ct;
4337     }
4338     PALISADE_THROW(config_error, "ModReduce operation has not been enabled");
4339   }
4340 
4341   virtual void ModReduceInPlace(Ciphertext<Element> &cipherText,
4342                                 size_t levels = 1) const {
4343     if (m_algorithmLeveledSHE) {
4344       if (!cipherText)
4345         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4346       m_algorithmLeveledSHE->ModReduceInPlace(cipherText, levels);
4347       return;
4348     }
4349     PALISADE_THROW(config_error, "ModReduce operation has not been enabled");
4350   }
4351 
ComposedEvalMult(ConstCiphertext<Element> cipherText1,ConstCiphertext<Element> cipherText2,const LPEvalKey<Element> quadKeySwitchHint)4352   virtual Ciphertext<Element> ComposedEvalMult(
4353       ConstCiphertext<Element> cipherText1,
4354       ConstCiphertext<Element> cipherText2,
4355       const LPEvalKey<Element> quadKeySwitchHint) const {
4356     if (m_algorithmLeveledSHE) {
4357       if (!cipherText1)
4358         PALISADE_THROW(config_error, "Input first ciphertext is nullptr");
4359       if (!cipherText2)
4360         PALISADE_THROW(config_error, "Input second ciphertext is nullptr");
4361       if (!quadKeySwitchHint)
4362         PALISADE_THROW(config_error, "Input evaluation key is nullptr");
4363       auto ct = m_algorithmLeveledSHE->ComposedEvalMult(
4364           cipherText1, cipherText2, quadKeySwitchHint);
4365       ct->SetKeyTag(quadKeySwitchHint->GetKeyTag());
4366       return ct;
4367     }
4368     PALISADE_THROW(config_error,
4369                    "ComposedEvalMult operation has not been enabled");
4370   }
4371 
4372   virtual Ciphertext<Element> LevelReduce(
4373       ConstCiphertext<Element> cipherText1,
4374       const LPEvalKey<Element> linearKeySwitchHint, size_t levels = 1) const {
4375     if (m_algorithmLeveledSHE) {
4376       if (!cipherText1)
4377         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4378       // if (!linearKeySwitchHint)
4379       //    PALISADE_THROW(config_error, "Input evaluation key is nullptr");
4380       auto ct = m_algorithmLeveledSHE->LevelReduce(cipherText1,
4381                                                    linearKeySwitchHint, levels);
4382       ct->SetKeyTag(cipherText1->GetKeyTag());
4383       return ct;
4384     }
4385     PALISADE_THROW(config_error, "LevelReduce operation has not been enabled");
4386   }
4387 
4388   /**
4389    * Method for polynomial evaluation for polynomials represented as power
4390    * series.
4391    *
4392    * @param &cipherText input ciphertext
4393    * @param &coefficients is the vector of coefficients in the polynomial; the
4394    * size of the vector is the degree of the polynomial + 1
4395    * @return the result of polynomial evaluation.
4396    */
EvalPoly(ConstCiphertext<Element> ciphertext,const std::vector<double> & coefficients)4397   Ciphertext<Element> EvalPoly(ConstCiphertext<Element> ciphertext,
4398                                const std::vector<double> &coefficients) const {
4399     if (this->m_algorithmLeveledSHE) {
4400       if (!ciphertext)
4401         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4402       auto ctm =
4403           this->m_algorithmLeveledSHE->EvalPoly(ciphertext, coefficients);
4404       return ctm;
4405     } else {
4406       PALISADE_THROW(config_error, "EvalPoly operation has not been enabled");
4407     }
4408   }
4409 
4410   /*
4411    * This exposes CKKS's own ParamsGen through the
4412    * LPPublicKeyEncryptionSchemeCKKS API. See
4413    * LPAlgorithmParamsGenCKKS::ParamsGen for a description of the arguments.
4414    *
4415    */
ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,usint cyclOrder,usint numPrimes,usint scaleExp,usint relinWindow,MODE mode,enum KeySwitchTechnique ksTech,usint firstModSize,RescalingTechnique rsTech,uint32_t numLargeDigits)4416   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
4417                          usint cyclOrder, usint numPrimes, usint scaleExp,
4418                          usint relinWindow, MODE mode,
4419                          enum KeySwitchTechnique ksTech, usint firstModSize,
4420                          RescalingTechnique rsTech,
4421                          uint32_t numLargeDigits) const {
4422     if (m_algorithmParamsGen) {
4423       return m_algorithmParamsGen->ParamsGen(
4424           cryptoParams, cyclOrder, numPrimes, scaleExp, relinWindow, mode,
4425           ksTech, firstModSize, rsTech, numLargeDigits);
4426     }
4427     PALISADE_THROW(not_implemented_error,
4428                    "Parameter generation operation has not been implemented "
4429                    "for this scheme.");
4430   }
4431 
4432   /*
4433    * This exposes BGVrns own ParamsGen through the
4434    * LPPublicKeyEncryptionSchemeBGVrns API. See
4435    * LPAlgorithmParamsGenBGVrns::ParamsGen for a description of the arguments.
4436    *
4437    */
ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,usint cyclOrder,usint ptm,usint numPrimes,usint relinWindow,MODE mode,enum KeySwitchTechnique ksTech,usint firstModSize,usint dcrtBits,uint32_t numLargeDigits)4438   virtual bool ParamsGen(shared_ptr<LPCryptoParameters<Element>> cryptoParams,
4439                          usint cyclOrder, usint ptm, usint numPrimes,
4440                          usint relinWindow, MODE mode,
4441                          enum KeySwitchTechnique ksTech, usint firstModSize,
4442                          usint dcrtBits, uint32_t numLargeDigits) const {
4443     if (m_algorithmParamsGen) {
4444       return m_algorithmParamsGen->ParamsGen(
4445           cryptoParams, cyclOrder, ptm, numPrimes, relinWindow, mode, ksTech,
4446           firstModSize, dcrtBits, numLargeDigits);
4447     }
4448     PALISADE_THROW(
4449         not_implemented_error,
4450         "Parameter generation operation has not been implemented for this "
4451         "scheme.");
4452   }
4453 
4454   /*
4455    * Internal method performing level reduce (drop towers).
4456    * It's exposed here so methods in LPAlgorithmSHECKKS can access methods
4457    * from LPLeveledSHEAlgorithmCKKS (so that automatic rescaling can work
4458    * in EXACTRESCALE).
4459    *
4460    * @param cipherText1 input ciphertext
4461    * @param linearKeySwitchHint not used in the CKKS scheme.
4462    * @param levels the number of towers to drop from the input ciphertext
4463    * @return a ciphertext of the same plaintext value as that of the input,
4464    *         but with fewer towers.
4465    *
4466    */
LevelReduceInternal(ConstCiphertext<Element> cipherText1,const LPEvalKey<Element> linearKeySwitchHint,size_t levels)4467   virtual Ciphertext<Element> LevelReduceInternal(
4468       ConstCiphertext<Element> cipherText1,
4469       const LPEvalKey<Element> linearKeySwitchHint, size_t levels) const {
4470     if (m_algorithmLeveledSHE) {
4471       if (!cipherText1)
4472         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4473       return m_algorithmLeveledSHE->LevelReduceInternal(
4474           cipherText1, linearKeySwitchHint, levels);
4475     }
4476     PALISADE_THROW(not_implemented_error,
4477                    "LevelReduceInternal has not been enabled for this scheme.");
4478   }
4479 
4480   /*
4481    * Internal method performing in-place level reduce (drop towers).
4482    * It's exposed here so methods in LPAlgorithmSHECKKS can access methods
4483    * from LPLeveledSHEAlgorithmCKKS (so that automatic rescaling can work
4484    * in EXACTRESCALE).
4485    *
4486    * @param cipherText1 input/output ciphertext
4487    * @param linearKeySwitchHint not used in the CKKS scheme.
4488    * @param levels the number of towers to drop from the input ciphertext
4489    * @return a ciphertext of the same plaintext value as that of the input,
4490    *         but with fewer towers.
4491    *
4492    */
LevelReduceInternalInPlace(Ciphertext<Element> & cipherText1,const LPEvalKey<Element> linearKeySwitchHint,size_t levels)4493   virtual void LevelReduceInternalInPlace(
4494       Ciphertext<Element> &cipherText1,
4495       const LPEvalKey<Element> linearKeySwitchHint, size_t levels) const {
4496     if (m_algorithmLeveledSHE) {
4497       if (!cipherText1)
4498         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4499       m_algorithmLeveledSHE->LevelReduceInternalInPlace(
4500           cipherText1, linearKeySwitchHint, levels);
4501       return;
4502     }
4503     PALISADE_THROW(
4504         not_implemented_error,
4505         "LevelReduceInternalInPlace has not been enabled for this scheme.");
4506   }
4507 
4508   /*
4509    * Internal method performing mod reduce (rescaling).
4510    * It's exposed here so methods in LPAlgorithmSHECKKS can access the method
4511    * from LPLeveledSHEAlgorithmCKKS (so that automatic rescaling can work
4512    * in EXACTRESCALE).
4513    *
4514    * @param cipherText1 input ciphertext
4515    * @return the rescaled ciphertext.
4516    *
4517    */
4518   virtual Ciphertext<Element> ModReduceInternal(
4519       ConstCiphertext<Element> cipherText, size_t levels = 1) const {
4520     if (m_algorithmLeveledSHE) {
4521       if (!cipherText)
4522         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4523       return m_algorithmLeveledSHE->ModReduceInternal(cipherText, levels);
4524     }
4525     PALISADE_THROW(config_error,
4526                    "ModReduceInternal has not been enabled for this scheme.");
4527   }
4528 
4529   virtual void ModReduceInternalInPlace(Ciphertext<Element> &cipherText,
4530                                         size_t levels = 1) const {
4531     if (m_algorithmLeveledSHE) {
4532       if (!cipherText)
4533         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4534       m_algorithmLeveledSHE->ModReduceInternalInPlace(cipherText, levels);
4535       return;
4536     }
4537     PALISADE_THROW(
4538         config_error,
4539         "ModReduceInternalInPlace has not been enabled for this scheme.");
4540   }
4541 
4542   virtual Ciphertext<Element> Compress(ConstCiphertext<Element> cipherText,
4543                                        size_t towersLeft = 1) const {
4544     if (m_algorithmLeveledSHE) {
4545       if (!cipherText)
4546         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4547       return m_algorithmLeveledSHE->Compress(cipherText, towersLeft);
4548     }
4549     PALISADE_THROW(config_error,
4550                    "Compress has not been enabled for this scheme.");
4551   }
4552 
AdjustLevelWithRescale(Ciphertext<Element> cipherText,uint32_t targetLevel)4553   virtual Ciphertext<Element> AdjustLevelWithRescale(
4554       Ciphertext<Element> cipherText, uint32_t targetLevel) const {
4555     if (m_algorithmSHE) {
4556       if (!cipherText)
4557         PALISADE_THROW(config_error, "Input ciphertext is nullptr");
4558       return m_algorithmSHE->AdjustLevelWithRescale(cipherText, targetLevel);
4559     }
4560     PALISADE_THROW(
4561         config_error,
4562         "AdjustLevelWithRescale has not been enabled for this scheme.");
4563   }
4564 
getAlgorithm()4565   const std::shared_ptr<LPEncryptionAlgorithm<Element>> getAlgorithm() const {
4566     return m_algorithmEncryption;
4567   }
4568 
4569   template <class Archive>
save(Archive & ar,std::uint32_t const version)4570   void save(Archive &ar, std::uint32_t const version) const {
4571     ar(::cereal::make_nvp("enabled", GetEnabled()));
4572   }
4573 
4574   template <class Archive>
load(Archive & ar,std::uint32_t const version)4575   void load(Archive &ar, std::uint32_t const version) {
4576     if (version > SerializedVersion()) {
4577       PALISADE_THROW(deserialize_error,
4578                      "serialized object version " + std::to_string(version) +
4579                          " is from a later version of the library");
4580     }
4581 
4582     usint enabled;
4583     ar(::cereal::make_nvp("enabled", enabled));
4584     this->Enable(enabled);
4585   }
4586 
SerializedObjectName()4587   virtual std::string SerializedObjectName() const { return "Scheme"; }
SerializedVersion()4588   static uint32_t SerializedVersion() { return 1; }
4589 
4590   friend std::ostream &operator<<(
4591       std::ostream &out, const LPPublicKeyEncryptionScheme<Element> &s) {
4592     out << typeid(s).name() << ":";
4593     out << " ParameterGeneration "
4594         << (s.m_algorithmParamsGen == 0
4595                 ? "none"
4596                 : typeid(*s.m_algorithmParamsGen).name());
4597     out << ", Encryption "
4598         << (s.m_algorithmEncryption == 0
4599                 ? "none"
4600                 : typeid(*s.m_algorithmEncryption).name());
4601     out << ", PRE "
4602         << (s.m_algorithmPRE == 0 ? "none" : typeid(*s.m_algorithmPRE).name());
4603     out << ", Multiparty "
4604         << (s.m_algorithmMultiparty == 0
4605                 ? "none"
4606                 : typeid(*s.m_algorithmMultiparty).name());
4607     out << ", SHE "
4608         << (s.m_algorithmSHE == 0 ? "none" : typeid(*s.m_algorithmSHE).name());
4609     out << ", LeveledSHE "
4610         << (s.m_algorithmLeveledSHE == 0
4611                 ? "none"
4612                 : typeid(*s.m_algorithmLeveledSHE).name());
4613     return out;
4614   }
4615 
4616  protected:
4617   std::shared_ptr<LPParameterGenerationAlgorithm<Element>> m_algorithmParamsGen;
4618   std::shared_ptr<LPEncryptionAlgorithm<Element>> m_algorithmEncryption;
4619   std::shared_ptr<LPPREAlgorithm<Element>> m_algorithmPRE;
4620   std::shared_ptr<LPMultipartyAlgorithm<Element>> m_algorithmMultiparty;
4621   std::shared_ptr<LPSHEAlgorithm<Element>> m_algorithmSHE;
4622   std::shared_ptr<LPLeveledSHEAlgorithm<Element>> m_algorithmLeveledSHE;
4623 };
4624 
4625 }  // namespace lbcrypto
4626 
4627 #endif
4628