Quellcode durchsuchen

Merge pull request #67 from openfheorg/dev

* pybind global

* Update ckks-boot example (Issue #54)

* Threshold FHE Support (#61)

* Threshold FHE - BGVrnsAdditive

* EvalKeyMap Prototypes

* Threshold FHE - BGVrns/CKKS + 5 parties prototype

* Threshold-FHE final working capabilities

* PR review response

* Fix Serialization/Deserialization API and examples (#63)

* temporary fix for deserialization

* Deserial: testing different workaround

* test tuple impl

* test seria file

* testing ownership problem

* temporary fix for deserialization

* Deserial: testing different workaround

* test tuple impl

* test seria file

* testing ownership problem

* add debug functions

* serialization works for ckks, added missing serialization methods, privatekey has constructor now

* resolve merge conflicts

* Fix DeserializeFromFile bug

* simple-real-numbers-serial

* real numbers fix

* fix bindings file

* fix merge conflicts

* typo

---------

Co-authored-by: Gergo, Petrik <gergo.petrik@xtendr.io>

* Update CMakeLists.txt

* remove unavaiable section (#66)

---------

Co-authored-by: Rener Oliveira (Ubuntu WSL) <rener.oliveira@fgv.edu.br>
Co-authored-by: Rener Oliveira <53785783+reneroliveira@users.noreply.github.com>
Co-authored-by: Gergo, Petrik <gergo.petrik@xtendr.io>
yspolyakov vor 1 Jahr
Ursprung
Commit
d983d5e868

+ 5 - 2
.gitignore

@@ -2,9 +2,12 @@ build/
 .vscode/
 .idea
 .png
-.json
-.txt
+*.json
+*.txt
 .cproject
 .project
+.html
+*.pyc
 .settings/
+docs/
 demoData/

+ 8 - 2
CMakeLists.txt

@@ -1,6 +1,12 @@
 cmake_minimum_required (VERSION 3.5.1)
 
-project(openfhe)
+project (OpenFHE-Python)
+
+set(OPENFHE_PYTHON_VERSION_MAJOR 0)
+set(OPENFHE_PYTHON_VERSION_MINOR 8)
+set(OPENFHE_PYTHON_VERSION_PATCH 1)
+set(OPENFHE_PYTHON_VERSION ${OPENFHE_PYTHON_VERSION_MAJOR}.${OPENFHE_PYTHON_VERSION_MINOR}.${OPENFHE_PYTHON_VERSION_PATCH})
+
 set(CMAKE_CXX_STANDARD 17)
 option( BUILD_STATIC "Set to ON to include static versions of the library" OFF)
 
@@ -66,4 +72,4 @@ execute_process(
 )
 
 message(STATUS "Python site packages directory: ${PYTHON_SITE_PACKAGES}")
-install(TARGETS openfhe LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES})
+install(TARGETS openfhe LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES})

+ 1 - 2
README.md

@@ -5,8 +5,7 @@
 - [Building](#building)
   - [Prerequisites](#requirements)
   - [Linux Install](#linux)
-    - [Installing the .so: Conda](#conda)
-    - [Installing the .so: System](#system-install)
+    - [Using Conda environments](#conda)
 - [Running Examples](#code-examples)
 - [OpenFHE Python Wrapper Documentation](#openfhe-python-wrapper-documentation)
 

+ 12 - 7
examples/pke/iterative-ckks-bootstrapping.py

@@ -21,9 +21,14 @@ def iterative_bootstrap_example():
     parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
     parameters.SetRingDim(1 << 12)
 
-    rescale_tech = ScalingTechnique.FLEXIBLEAUTO
-    dcrt_bits = 59
-    first_mod = 60
+    if get_native_int()==128:
+        rescale_tech = ScalingTechnique.FIXEDAUTO
+        dcrt_bits = 78
+        first_mod = 89
+    else:
+        rescale_tech = ScalingTechnique.FLEXIBLEAUTO
+        dcrt_bits = 59
+        first_mod = 60
 
     parameters.SetScalingModSize(dcrt_bits)
     parameters.SetScalingTechnique(rescale_tech)
@@ -35,12 +40,10 @@ def iterative_bootstrap_example():
     num_iterations = 2
 
     level_budget = [3, 3]
-    # Each extra iteration on top of 1 requires an extra level to be consumed.
-    approx_bootstrapp_depth = 8 + (num_iterations - 1)
     bsgs_dim = [0,0]
 
-    levels_used_before_bootstrap = 10
-    depth = levels_used_before_bootstrap + FHECKKSRNS.GetBootstrapDepth(approx_bootstrapp_depth, level_budget, secret_key_dist)
+    levels_available_after_bootstrap = 10
+    depth = levels_available_after_bootstrap = 10 + FHECKKSRNS.GetBootstrapDepth(level_budget, secret_key_dist) + (num_iterations - 1)
     parameters.SetMultiplicativeDepth(depth)
 
     # Generate crypto context
@@ -80,6 +83,8 @@ def iterative_bootstrap_example():
         Here, we assume all ciphertexts in the cryptoContext will have num_slots slots.
         We start with a depleted ciphertext that has used up all of its levels."""
     ptxt = cryptocontext.MakeCKKSPackedPlaintext(x, 1, depth -1,None,num_slots)
+    ptxt.SetLength(num_slots)
+    print(f"Input: {ptxt}")
 
     # Encrypt the encoded vectors
     ciph = cryptocontext.Encrypt(key_pair.publicKey, ptxt)

+ 14 - 10
examples/pke/simple-ckks-bootstrapping.py

@@ -12,20 +12,24 @@ def simple_bootstrap_example():
     parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
     parameters.SetRingDim(1<<12)
 
-    rescale_tech = ScalingTechnique.FLEXIBLEAUTO
-    dcrt_bits = 59
-    first_mod = 60
+    if get_native_int()==128:
+        rescale_tech = ScalingTechnique.FIXEDAUTO
+        dcrt_bits = 78
+        first_mod = 89
+    else:
+        rescale_tech = ScalingTechnique.FLEXIBLEAUTO
+        dcrt_bits = 59
+        first_mod = 60
     
     parameters.SetScalingModSize(dcrt_bits)
     parameters.SetScalingTechnique(rescale_tech)
     parameters.SetFirstModSize(first_mod)
 
     level_budget = [4, 4]
-    approx_bootstrapp_depth = 8
 
-    levels_used_before_bootstrap = 10
+    levels_available_after_bootstrap = 10
 
-    depth = levels_used_before_bootstrap + FHECKKSRNS.GetBootstrapDepth(approx_bootstrapp_depth, level_budget, secret_key_dist)
+    depth = levels_available_after_bootstrap + FHECKKSRNS.GetBootstrapDepth(level_budget, secret_key_dist)
 
     parameters.SetMultiplicativeDepth(depth)
 
@@ -51,18 +55,18 @@ def simple_bootstrap_example():
     x = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0]
     encoded_length = len(x)
 
-    ptxt = cryptocontext.MakeCKKSPackedPlaintext(x)
+    ptxt = cryptocontext.MakeCKKSPackedPlaintext(x,1,depth-1)
     ptxt.SetLength(encoded_length)
 
-    print(f"Input: {x}")
+    print(f"Input: {ptxt}")
 
     ciph = cryptocontext.Encrypt(key_pair.publicKey, ptxt)
 
-    print(f"Initial number of levels remaining: {ciph.GetLevel()}")
+    print(f"Initial number of levels remaining: {depth - ciph.GetLevel()}")
 
     ciphertext_after = cryptocontext.EvalBootstrap(ciph)
 
-    print(f"Number of levels remaining after bootstrapping: {ciphertext_after.GetLevel()}")
+    print(f"Number of levels remaining after bootstrapping: {depth - ciphertext_after.GetLevel()}")
 
     result = cryptocontext.Decrypt(ciphertext_after,key_pair.secretKey)
     result.SetLength(encoded_length)

+ 61 - 36
examples/pke/simple-integers-serial-bgvrns.py

@@ -3,7 +3,7 @@ from openfhe import *
 # import openfhe.PKESchemeFeature as Feature
 
 datafolder = 'demoData'
-
+serType = BINARY # BINARY or JSON 
 print("This program requres the subdirectory `" + datafolder + "' to exist, otherwise you will get an error writing serializations.")
 
 # Sample Program: Step 1: Set CryptoContext
@@ -18,7 +18,7 @@ cryptoContext.Enable(PKESchemeFeature.KEYSWITCH)
 cryptoContext.Enable(PKESchemeFeature.LEVELEDSHE)
 
 # Serialize cryptocontext
-if not SerializeToFile(datafolder + "/cryptocontext.txt", cryptoContext, BINARY):
+if not SerializeToFile(datafolder + "/cryptocontext.txt", cryptoContext, serType):
    raise Exception("Error writing serialization of the crypto context to cryptocontext.txt")
 print("The cryptocontext has been serialized.")
 
@@ -29,13 +29,13 @@ keypair = cryptoContext.KeyGen()
 print("The keypair has been generated.")
 
 # Serialize the public key
-if not SerializeToFile(datafolder + "/key-public.txt", keypair.publicKey, BINARY):
+if not SerializeToFile(datafolder + "/key-public.txt", keypair.publicKey, serType):
    raise Exception("Error writing serialization of the public key to key-public.txt")
 print("The public key has been serialized.")
 
 # Serialize the secret key
-if not SerializeToFile(datafolder + "/key-secret.txt", keypair.secretKey, BINARY):
-   raise Exception("Error writing serialization of the secret key to key-secret.txt")
+if not SerializeToFile(datafolder + "/key-private.txt", keypair.secretKey, serType):
+   raise Exception("Error writing serialization of the secret key to key-private.txt")
 print("The secret key has been serialized.")
 
 # Generate the relinearization key
@@ -43,7 +43,7 @@ cryptoContext.EvalMultKeyGen(keypair.secretKey)
 print("The relinearization key has been generated.")
 
 # Serialize the relinearization key
-if not cryptoContext.SerializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+if not cryptoContext.SerializeEvalMultKey(datafolder + "/key-eval-mult.txt",serType):
    raise Exception("Error writing serialization of the eval mult keys to \"key-eval-mult.txt\"")
 print("The relinearization key has been serialized.")
 
@@ -52,7 +52,7 @@ cryptoContext.EvalRotateKeyGen(keypair.secretKey, [1, 2, -1, -2])
 print("The rotation evaluation keys have been generated.")
 
 # Serialize the rotation evaluation keys
-if not cryptoContext.SerializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+if not cryptoContext.SerializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",serType):
    raise Exception("Error writing serialization of the eval rotate keys to \"key-eval-rot.txt\"")
 print("The rotation evaluation keys have been serialized.")
 
@@ -77,15 +77,15 @@ ciphertext2 = cryptoContext.Encrypt(keypair.publicKey, plaintext2)
 ciphertext3 = cryptoContext.Encrypt(keypair.publicKey, plaintext3)
 print("The plaintexts have been encrypted.")
 
-if not SerializeToFile(datafolder + "/ciphertext1.txt", ciphertext1, BINARY):
+if not SerializeToFile(datafolder + "/ciphertext1.txt", ciphertext1, serType):
    raise Exception("Error writing serialization of ciphertext 1 to ciphertext1.txt")
 print("The first ciphertext has been serialized.")
 
-if not SerializeToFile(datafolder + "/ciphertext2.txt", ciphertext2, BINARY):
+if not SerializeToFile(datafolder + "/ciphertext2.txt", ciphertext2, serType):
    raise Exception("Error writing serialization of ciphertext2 to ciphertext2.txt")
 print("The second ciphertext has been serialized.")
 
-if not SerializeToFile(datafolder + "/ciphertext3.txt", ciphertext3, BINARY):   
+if not SerializeToFile(datafolder + "/ciphertext3.txt", ciphertext3, serType):   
    raise Exception("Error writing serialization of ciphertext3 to ciphertext3.txt")
 print("The third ciphertext has been serialized.")
 
@@ -101,49 +101,45 @@ cryptoContext.ClearEvalAutomorphismKeys()
 ReleaseAllContexts()
 
 # Deserialize the crypto context
-cc = CryptoContext()
 
-if not DeserializeFromFile(datafolder + "/cryptocontext.txt", cc, BINARY):
+cc, res = DeserializeCryptoContext(datafolder + "/cryptocontext.txt", serType)
+if not res:
    raise Exception("Error reading serialization of the crypto context from cryptocontext.txt")
 print("The cryptocontext has been deserialized.")
 
 # Deserialize the public key
-pk = PublicKey()
+pk, res = DeserializePublicKey(datafolder + "/key-public.txt", serType)
 
-if not DeserializeFromFile(datafolder + "/key-public.txt", pk, BINARY):
+if not res:
    raise Exception("Error reading serialization of the public key from key-public.txt")
-
 print("The public key has been deserialized.")
 
-# if cryptoContext.DeserializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
-#    raise Exception("Could not deserialize the eval mult key file")
+if not cc.DeserializeEvalMultKey(datafolder + "/key-eval-mult.txt",serType):
+   raise Exception("Could not deserialize the eval mult key file")
 
-# print("The relinearization key has been deserialized.")
+print("The relinearization key has been deserialized.")
 
-# if cryptoContext.DeserializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
-#    raise Exception("Could not deserialize the eval rotation key file")
+if not cc.DeserializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",serType):
+   raise Exception("Could not deserialize the eval rotation key file")
 
-# print("Deserialized the eval rotation keys.")
+print("Deserialized the eval rotation keys.")
 
 # Deserialize the ciphertexts
+ct1, res =  DeserializeCiphertext(datafolder + "/ciphertext1.txt", serType)
 
-ct1 = Ciphertext()
-ct2 = Ciphertext()
-ct3 = Ciphertext()
-
-if not DeserializeFromFile(datafolder + "/ciphertext1.txt", ct1, BINARY):
+if not res:
     raise Exception("Could not read the ciphertext")
-
 print("The first ciphertext has been deserialized.")
 
-if not DeserializeFromFile(datafolder + "/ciphertext2.txt", ct2, BINARY):
-    raise Exception("Could not read the ciphertext")
+ct2, res = DeserializeCiphertext(datafolder + "/ciphertext2.txt", serType)
 
+if not res:
+    raise Exception("Could not read the ciphertext")
 print("The second ciphertext has been deserialized.")
 
-if not DeserializeFromFile(datafolder + "/ciphertext3.txt", ct3, BINARY):   
+ct3, res = DeserializeCiphertext(datafolder + "/ciphertext3.txt", serType)
+if not res:   
     raise Exception("Could not read the ciphertext")
-
 print("The third ciphertext has been deserialized.")
 
 # Homomorphic addition
@@ -161,8 +157,37 @@ ciphertextRot2 = cc.EvalRotate(ct2, 2)
 ciphertextRot3 = cc.EvalRotate(ct3, -1)
 ciphertextRot4 = cc.EvalRotate(ct3, -2)
 
-
-
-
-
-
+# Sample Program: Step 5: Decryption
+
+sk, res = DeserializePrivateKey(datafolder + "/key-private.txt", serType)
+if not res:
+      raise Exception("Could not read secret key")
+print("The secret key has been deserialized.")
+
+# Decrypt the result of additions
+plaintextAddResult = cc.Decrypt(sk, ciphertextAddResult)
+
+# Decrypt the result of multiplications
+plaintextMultResult = cc.Decrypt(sk, ciphertextMultResult)
+
+# Decrypt the result of rotations
+plaintextRot1 = cc.Decrypt(sk, ciphertextRot1)
+plaintextRot2 = cc.Decrypt(sk, ciphertextRot2)
+plaintextRot3 = cc.Decrypt(sk, ciphertextRot3)
+plaintextRot4 = cc.Decrypt(sk, ciphertextRot4)
+
+# Shows only the same number of elements as in the original plaintext vector
+# By default it will show all coefficients in the BFV-encoded polynomial
+plaintextRot1.SetLength(len(vectorOfInts1))
+plaintextRot2.SetLength(len(vectorOfInts1))
+plaintextRot3.SetLength(len(vectorOfInts1))
+plaintextRot4.SetLength(len(vectorOfInts1))
+
+# Output results
+print("\nResults of homomorphic computations")
+print("#1 + #2 + #3: " + str(plaintextAddResult))
+print("#1 * #2 * #3: " + str(plaintextMultResult))
+print("Left rotation of #1 by 1: " + str(plaintextRot1))
+print("Left rotation of #1 by 2: " + str(plaintextRot2))
+print("Right rotation of #1 by 1: " + str(plaintextRot3))
+print("Right rotation of #1 by 2: " + str(plaintextRot4))

+ 57 - 32
examples/pke/simple-integers-serial.py

@@ -3,7 +3,7 @@ from openfhe import *
 # import openfhe.PKESchemeFeature as Feature
 
 datafolder = 'demoData'
-
+serType = BINARY # BINARY or JSON 
 print("This program requres the subdirectory `" + datafolder + "' to exist, otherwise you will get an error writing serializations.")
 
 # Sample Program: Step 1: Set CryptoContext
@@ -18,7 +18,7 @@ cryptoContext.Enable(PKESchemeFeature.KEYSWITCH)
 cryptoContext.Enable(PKESchemeFeature.LEVELEDSHE)
 
 # Serialize cryptocontext
-if not SerializeToFile(datafolder + "/cryptocontext.txt", cryptoContext, BINARY):
+if not SerializeToFile(datafolder + "/cryptocontext.txt", cryptoContext, serType):
    raise Exception("Error writing serialization of the crypto context to cryptocontext.txt")
 print("The cryptocontext has been serialized.")
 
@@ -29,13 +29,13 @@ keypair = cryptoContext.KeyGen()
 print("The keypair has been generated.")
 
 # Serialize the public key
-if not SerializeToFile(datafolder + "/key-public.txt", keypair.publicKey, BINARY):
+if not SerializeToFile(datafolder + "/key-public.txt", keypair.publicKey, serType):
    raise Exception("Error writing serialization of the public key to key-public.txt")
 print("The public key has been serialized.")
 
 # Serialize the secret key
-if not SerializeToFile(datafolder + "/key-secret.txt", keypair.secretKey, BINARY):
-   raise Exception("Error writing serialization of the secret key to key-secret.txt")
+if not SerializeToFile(datafolder + "/key-private.txt", keypair.secretKey, serType):
+   raise Exception("Error writing serialization of the secret key to key-private.txt")
 print("The secret key has been serialized.")
 
 # Generate the relinearization key
@@ -43,7 +43,7 @@ cryptoContext.EvalMultKeyGen(keypair.secretKey)
 print("The relinearization key has been generated.")
 
 # Serialize the relinearization key
-if not cryptoContext.SerializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+if not cryptoContext.SerializeEvalMultKey(datafolder + "/key-eval-mult.txt",serType):
    raise Exception("Error writing serialization of the eval mult keys to \"key-eval-mult.txt\"")
 print("The relinearization key has been serialized.")
 
@@ -52,7 +52,7 @@ cryptoContext.EvalRotateKeyGen(keypair.secretKey, [1, 2, -1, -2])
 print("The rotation evaluation keys have been generated.")
 
 # Serialize the rotation evaluation keys
-if not cryptoContext.SerializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+if not cryptoContext.SerializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",serType):
    raise Exception("Error writing serialization of the eval rotate keys to \"key-eval-rot.txt\"")
 print("The rotation evaluation keys have been serialized.")
 
@@ -77,15 +77,15 @@ ciphertext2 = cryptoContext.Encrypt(keypair.publicKey, plaintext2)
 ciphertext3 = cryptoContext.Encrypt(keypair.publicKey, plaintext3)
 print("The plaintexts have been encrypted.")
 
-if not SerializeToFile(datafolder + "/ciphertext1.txt", ciphertext1, BINARY):
+if not SerializeToFile(datafolder + "/ciphertext1.txt", ciphertext1, serType):
    raise Exception("Error writing serialization of ciphertext 1 to ciphertext1.txt")
 print("The first ciphertext has been serialized.")
 
-if not SerializeToFile(datafolder + "/ciphertext2.txt", ciphertext2, BINARY):
+if not SerializeToFile(datafolder + "/ciphertext2.txt", ciphertext2, serType):
    raise Exception("Error writing serialization of ciphertext2 to ciphertext2.txt")
 print("The second ciphertext has been serialized.")
 
-if not SerializeToFile(datafolder + "/ciphertext3.txt", ciphertext3, BINARY):   
+if not SerializeToFile(datafolder + "/ciphertext3.txt", ciphertext3, serType):   
    raise Exception("Error writing serialization of ciphertext3 to ciphertext3.txt")
 print("The third ciphertext has been serialized.")
 
@@ -101,49 +101,45 @@ cryptoContext.ClearEvalAutomorphismKeys()
 ReleaseAllContexts()
 
 # Deserialize the crypto context
-cc = CryptoContext()
 
-if not DeserializeFromFile(datafolder + "/cryptocontext.txt", cc, BINARY):
+cc, res = DeserializeCryptoContext(datafolder + "/cryptocontext.txt", serType)
+if not res:
    raise Exception("Error reading serialization of the crypto context from cryptocontext.txt")
 print("The cryptocontext has been deserialized.")
 
 # Deserialize the public key
-pk = PublicKey()
+pk, res = DeserializePublicKey(datafolder + "/key-public.txt", serType)
 
-if not DeserializeFromFile(datafolder + "/key-public.txt", pk, BINARY):
+if not res:
    raise Exception("Error reading serialization of the public key from key-public.txt")
-
 print("The public key has been deserialized.")
 
-if cryptoContext.DeserializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+if not cc.DeserializeEvalMultKey(datafolder + "/key-eval-mult.txt",serType):
    raise Exception("Could not deserialize the eval mult key file")
 
 print("The relinearization key has been deserialized.")
 
-if cryptoContext.DeserializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+if not cc.DeserializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",serType):
    raise Exception("Could not deserialize the eval rotation key file")
 
 print("Deserialized the eval rotation keys.")
 
 # Deserialize the ciphertexts
+ct1, res =  DeserializeCiphertext(datafolder + "/ciphertext1.txt", serType)
 
-ct1 = Ciphertext()
-ct2 = Ciphertext()
-ct3 = Ciphertext()
-
-if not DeserializeFromFile(datafolder + "/ciphertext1.txt", ct1, BINARY):
+if not res:
     raise Exception("Could not read the ciphertext")
-
 print("The first ciphertext has been deserialized.")
 
-if not DeserializeFromFile(datafolder + "/ciphertext2.txt", ct2, BINARY):
-    raise Exception("Could not read the ciphertext")
+ct2, res = DeserializeCiphertext(datafolder + "/ciphertext2.txt", serType)
 
+if not res:
+    raise Exception("Could not read the ciphertext")
 print("The second ciphertext has been deserialized.")
 
-if not DeserializeFromFile(datafolder + "/ciphertext3.txt", ct3, BINARY):   
+ct3, res = DeserializeCiphertext(datafolder + "/ciphertext3.txt", serType)
+if not res:   
     raise Exception("Could not read the ciphertext")
-
 print("The third ciphertext has been deserialized.")
 
 # Homomorphic addition
@@ -161,8 +157,37 @@ ciphertextRot2 = cc.EvalRotate(ct2, 2)
 ciphertextRot3 = cc.EvalRotate(ct3, -1)
 ciphertextRot4 = cc.EvalRotate(ct3, -2)
 
-
-
-
-
-
+# Sample Program: Step 5: Decryption
+
+sk, res = DeserializePrivateKey(datafolder + "/key-private.txt", serType)
+if not res:
+      raise Exception("Could not read secret key")
+print("The secret key has been deserialized.")
+
+# Decrypt the result of additions
+plaintextAddResult = cc.Decrypt(sk, ciphertextAddResult)
+
+# Decrypt the result of multiplications
+plaintextMultResult = cc.Decrypt(sk, ciphertextMultResult)
+
+# Decrypt the result of rotations
+plaintextRot1 = cc.Decrypt(sk, ciphertextRot1)
+plaintextRot2 = cc.Decrypt(sk, ciphertextRot2)
+plaintextRot3 = cc.Decrypt(sk, ciphertextRot3)
+plaintextRot4 = cc.Decrypt(sk, ciphertextRot4)
+
+# Shows only the same number of elements as in the original plaintext vector
+# By default it will show all coefficients in the BFV-encoded polynomial
+plaintextRot1.SetLength(len(vectorOfInts1))
+plaintextRot2.SetLength(len(vectorOfInts1))
+plaintextRot3.SetLength(len(vectorOfInts1))
+plaintextRot4.SetLength(len(vectorOfInts1))
+
+# Output results
+print("\nResults of homomorphic computations")
+print("#1 + #2 + #3: " + str(plaintextAddResult))
+print("#1 * #2 * #3: " + str(plaintextMultResult))
+print("Left rotation of #1 by 1: " + str(plaintextRot1))
+print("Left rotation of #1 by 2: " + str(plaintextRot2))
+print("Right rotation of #1 by 1: " + str(plaintextRot3))
+print("Right rotation of #1 by 2: " + str(plaintextRot4))

+ 287 - 0
examples/pke/simple-real-numbers-serial.py

@@ -0,0 +1,287 @@
+from openfhe import *
+
+
+# NOTE:
+# If running locally, you may want to replace the "hardcoded" datafolder with
+# the datafolder location below which gets the current working directory
+
+# Save-Load locations for keys
+datafolder = 'demoData'
+ccLocation = '/cryptocontext.txt'
+pubKeyLocation = '/key_pub.txt' # Pub key
+multKeyLocation = '/key_mult.txt' # relinearization key
+rotKeyLocation = '/key_rot.txt' # automorphism / rotation key
+
+# Save-load locations for RAW ciphertexts
+cipherOneLocation = '/ciphertext1.txt'
+cipherTwoLocation = '/ciphertext2.txt'
+
+# Save-load locations for evaluated ciphertexts
+cipherMultLocation = '/ciphertextMult.txt'
+cipherAddLocation = '/ciphertextAdd.txt'
+cipherRotLocation = '/ciphertextRot.txt'
+cipherRotNegLocation = '/ciphertextRotNegLocation.txt'
+clientVectorLocation = '/clientVectorFromClient.txt'
+
+# Demarcate - Visual separator between the sections of code
+def demarcate(msg):
+    print("**************************************************\n")
+    print(msg)
+    print("**************************************************\n")
+
+"""
+serverSetupAndWrite(multDepth, scaleModSize, batchSize)
+    simulates a server at startup where we generate a cryptocontext and keys.
+    then, we generate some data (akin to loading raw data on an enclave)
+    before encrypting the data
+    :param multDepth: multiplication depth
+    :param scaleModSize: number of bits to use in the scale factor (not the
+    scale factor itself)
+    :param batchSize: batch size to use
+    :return Tuple<cryptoContext, keyPair>
+"""
+def serverSetupAndWrite(multDepth, scaleModSize, batchSize):
+
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(multDepth)
+    parameters.SetScalingModSize(scaleModSize)
+    parameters.SetBatchSize(batchSize)
+
+    serverCC = GenCryptoContext(parameters)
+
+    serverCC.Enable(PKE)
+    serverCC.Enable(KEYSWITCH)
+    serverCC.Enable(LEVELEDSHE)
+
+    print("Cryptocontext generated")
+
+    serverKP = serverCC.KeyGen()
+    print("Keypair generated")
+
+    serverCC.EvalMultKeyGen(serverKP.secretKey)
+    print("Eval Mult Keys/ Relinearization keys have been generated")
+
+    serverCC.EvalRotateKeyGen(serverKP.secretKey, [1, 2, -1, -2])
+    print("Rotation keys generated")
+
+    vec1 = [1.0, 2.0, 3.0, 4.0]
+    vec2 = [12.5, 13.5, 14.5, 15.5]
+    vec3 = [10.5, 11.5, 12.5, 13.5]
+    print("\nDisplaying first data vector: ")
+    print(vec1)
+    print("\n")
+
+    serverP1 = serverCC.MakeCKKSPackedPlaintext(vec1)
+    serverP2 = serverCC.MakeCKKSPackedPlaintext(vec2)
+    serverP3 = serverCC.MakeCKKSPackedPlaintext(vec3)
+
+    print("Plaintext version of first vector: "+ str(serverP1))
+
+    print("Plaintexts have been generated from complex-double vectors")
+
+    serverC1 = serverCC.Encrypt(serverKP.publicKey, serverP1)
+    serverC2 = serverCC.Encrypt(serverKP.publicKey, serverP2)
+    serverC3 = serverCC.Encrypt(serverKP.publicKey, serverP3)
+
+    print("Ciphertexts have been generated from plaintexts")
+
+    ###
+    #    Part 2:
+    #    We serialize the following:
+    #      Cryptocontext
+    #      Public key
+    #      relinearization (eval mult keys)
+    #      rotation keys
+    #      Some of the ciphertext
+    #    
+    #      We serialize all of them to files
+    ###
+    demarcate("Part 2: Data Serialization (server)")
+
+    if not SerializeToFile(datafolder + ccLocation, serverCC, BINARY):
+        raise Exception("Exception writing cryptocontext to cryptocontext.txt")
+    print("Cryptocontext serialized")
+
+    if not SerializeToFile(datafolder + pubKeyLocation, serverKP.publicKey, BINARY):
+        raise Exception("Exception writing public key to pubkey.txt")
+    print("Public key has been serialized")
+
+    if not serverCC.SerializeEvalMultKey(datafolder + multKeyLocation, BINARY):
+        raise Exception("Error writing eval mult keys")
+    print("EvalMult/ relinearization keys have been serialized")
+
+    if not serverCC.SerializeEvalAutomorphismKey(datafolder + rotKeyLocation, BINARY):
+        raise Exception("Error writing rotation keys")
+    print("Rotation keys have been serialized")
+
+    if not SerializeToFile(datafolder + cipherOneLocation, serverC1, BINARY):
+        raise Exception("Error writing ciphertext 1")
+    
+    if not SerializeToFile(datafolder + cipherTwoLocation, serverC2, BINARY):
+        raise Exception("Error writing ciphertext 2")
+    
+    return (serverCC, serverKP, len(vec1))
+
+###
+ # clientProcess
+ #  - deserialize data from a file which simulates receiving data from a server
+ # after making a request
+ #  - we then process the data by doing operations (multiplication, addition,
+ # rotation, etc)
+ #  - !! We also create an object and encrypt it in this function before sending
+ # it off to the server to be decrypted
+###
+
+def clientProcess():
+    # clientCC = CryptoContext()
+    # clientCC.ClearEvalMultKeys()
+    # clientCC.ClearEvalAutomorphismKeys()
+    ReleaseAllContexts()
+
+    clientCC, res = DeserializeCryptoContext(datafolder + ccLocation, BINARY)
+    if not res:
+        raise Exception(f"I cannot deserialize the cryptocontext from {datafolder+ccLocation}")
+
+    print("Client CC deserialized")
+
+    #clientKP = KeyPair()
+    # We do NOT have a secret key. The client
+    # should not have access to this
+    clientPuclicKey, res = DeserializePublicKey(datafolder + pubKeyLocation, BINARY)
+    if not res:
+        raise Exception(f"I cannot deserialize the public key from {datafolder+pubKeyLocation}")
+    print("Client KP deserialized\n")
+
+    if not clientCC.DeserializeEvalMultKey(datafolder + multKeyLocation, BINARY):
+        raise Exception(f"Cannot deserialize eval mult keys from {datafolder+multKeyLocation}")
+    print("Deserialized eval mult keys\n")
+
+    if not clientCC.DeserializeEvalAutomorphismKey(datafolder + rotKeyLocation, BINARY):
+        raise Exception(f"Cannot deserialize eval automorphism keys from {datafolder+rotKeyLocation}")
+    
+    clientC1, res = DeserializeCiphertext(datafolder + cipherOneLocation, BINARY)
+    if not res:
+        raise Exception(f"Cannot deserialize the ciphertext from {datafolder+cipherOneLocation}")
+    print("Deserialized ciphertext 1\n")
+
+    clientC2, res = DeserializeCiphertext(datafolder + cipherTwoLocation, BINARY)
+    if not res:
+        raise Exception(f"Cannot deserialize the ciphertext from {datafolder+cipherTwoLocation}")
+    print("Deserialized ciphertext 2\n")
+
+    clientCiphertextMult = clientCC.EvalMult(clientC1, clientC2)
+    clientCiphertextAdd = clientCC.EvalAdd(clientC1, clientC2)
+    clientCiphertextRot = clientCC.EvalRotate(clientC1, 1)
+    clientCiphertextRotNeg = clientCC.EvalRotate(clientC1, -1)
+
+    # Now, we want to simulate a client who is encrypting data for the server to
+    # decrypt. E.g weights of a machine learning algorithm
+    demarcate("Part 3.5: Client Serialization of data that has been operated on")
+
+    clientVector1 = [1.0, 2.0, 3.0, 4.0]
+    clientPlaintext1 = clientCC.MakeCKKSPackedPlaintext(clientVector1)
+    clientInitializedEncryption = clientCC.Encrypt(clientPuclicKey, clientPlaintext1)
+    SerializeToFile(datafolder + cipherMultLocation, clientCiphertextMult, BINARY)
+    SerializeToFile(datafolder + cipherAddLocation, clientCiphertextAdd, BINARY)
+    SerializeToFile(datafolder + cipherRotLocation, clientCiphertextRot, BINARY)
+    SerializeToFile(datafolder + cipherRotNegLocation, clientCiphertextRotNeg, BINARY)
+    SerializeToFile(datafolder + clientVectorLocation, clientInitializedEncryption, BINARY)
+
+    print("Serialized all ciphertexts from client\n")
+
+###
+#  serverVerification
+#  - deserialize data from the client.
+#  - Verify that the results are as we expect
+# @param cc cryptocontext that was previously generated
+# @param kp keypair that was previously generated
+# @param vectorSize vector size of the vectors supplied
+# @return
+#  5-tuple of the plaintexts of various operations
+##
+def serverVerification(cc,kp,vectorSize):
+    
+    serverCiphertextFromClient_Mult, res = DeserializeCiphertext(datafolder + cipherMultLocation, BINARY)
+    serverCiphertextFromClient_Add, res = DeserializeCiphertext(datafolder + cipherAddLocation, BINARY)
+    serverCiphertextFromClient_Rot, res = DeserializeCiphertext(datafolder + cipherRotLocation, BINARY)
+    serverCiphertextFromClient_RotNeg, res = DeserializeCiphertext(datafolder + cipherRotNegLocation, BINARY)
+    serverCiphertextFromClient_Vec, res = DeserializeCiphertext(datafolder + clientVectorLocation, BINARY)
+    print("Deserialized all data from client on server\n")
+
+    print("Part 5: Correctness Verification")
+
+    serverPlaintextFromClient_Mult = cc.Decrypt(kp.secretKey, serverCiphertextFromClient_Mult)
+    serverPlaintextFromClient_Add = cc.Decrypt(kp.secretKey, serverCiphertextFromClient_Add)
+    serverPlaintextFromClient_Rot = cc.Decrypt(kp.secretKey, serverCiphertextFromClient_Rot)
+    serverPlaintextFromClient_RotNeg = cc.Decrypt(kp.secretKey, serverCiphertextFromClient_RotNeg)
+    serverPlaintextFromClient_Vec = cc.Decrypt(kp.secretKey, serverCiphertextFromClient_Vec)
+
+    serverPlaintextFromClient_Mult.SetLength(vectorSize)
+    serverPlaintextFromClient_Add.SetLength(vectorSize)
+    serverPlaintextFromClient_Vec.SetLength(vectorSize)
+    serverPlaintextFromClient_Rot.SetLength(vectorSize + 1)
+    serverPlaintextFromClient_RotNeg.SetLength(vectorSize + 1)
+
+    return (serverPlaintextFromClient_Mult, 
+            serverPlaintextFromClient_Add, 
+            serverPlaintextFromClient_Vec, 
+            serverPlaintextFromClient_Rot, 
+            serverPlaintextFromClient_RotNeg)
+
+def main():
+    print(f"This program requires the subdirectory `{datafolder}' to exist, otherwise you will get\n an error writing serializations.")
+
+    # Set main params
+    multDepth = 5
+    scaleModSize = 40
+    batchSize = 32
+
+    cryptoContextIdx = 0
+    keyPairIdx = 1
+    vectorSizeIdx = 2
+
+    cipherMultResIdx = 0
+    cipherAddResIdx = 1
+    cipherVecResIdx = 2
+    cipherRotResIdx = 3
+    cipherRotNegResIdx = 4
+
+    demarcate("Part 1: Cryptocontext generation, key generation, data encryption \n(server)")
+
+    tupleCryptoContext_KeyPair = serverSetupAndWrite(multDepth, scaleModSize, batchSize)
+    cc = tupleCryptoContext_KeyPair[cryptoContextIdx]
+    kp = tupleCryptoContext_KeyPair[keyPairIdx]
+    vectorSize = tupleCryptoContext_KeyPair[vectorSizeIdx]
+
+    demarcate("Part 3: Client deserialize all data")
+    clientProcess()
+
+    demarcate("Part 4: Server deserialization of data from client. ")
+
+    tupleRes = serverVerification(cc, kp, vectorSize)
+    multRes = tupleRes[cipherMultResIdx]
+    addRes = tupleRes[cipherAddResIdx]
+    vecRes = tupleRes[cipherVecResIdx]
+    rotRes = tupleRes[cipherRotResIdx]
+    rotNegRes = tupleRes[cipherRotNegResIdx]
+
+    # vec1: [1,2,3,4]
+    # vec2: [12.5, 13.5, 14.5, 15.5]
+
+    print(multRes)  # EXPECT: 12.5, 27.0, 43.5, 62
+    print(addRes)   # EXPECT: 13.5, 15.5, 17.5, 19.5
+    print(vecRes)   # EXPECT:  [1,2,3,4]
+
+    print("Displaying 5 elements of a 4-element vector to illustrate rotation")
+    print(rotRes)     # EXPECT: [2, 3, 4, noise, noise]
+    print(rotNegRes)  # EXPECT: [noise, 1, 2, 3, 4]
+
+if __name__ == "__main__":
+    main()
+
+
+
+
+
+
+

+ 220 - 0
examples/pke/threshold-fhe-5p.py

@@ -0,0 +1,220 @@
+from openfhe import *
+from math import log2
+
+def main():
+    print("\n=================RUNNING FOR BFVrns======================\n")
+    RunBFVrns()
+
+def RunBFVrns():
+    plaintextModulus = 65537
+    sigma = 3.2
+    securityLevel = SecurityLevel.HEStd_128_classic
+
+    batchSize = 16
+    multDepth = 4
+    digitSize = 30
+    dcrtBits = 60
+
+    parameters = CCParamsBFVRNS()
+    parameters.SetPlaintextModulus(plaintextModulus)
+    parameters.SetSecurityLevel(securityLevel)
+    parameters.SetStandardDeviation(sigma)
+    parameters.SetSecretKeyDist(UNIFORM_TERNARY)
+    parameters.SetMultiplicativeDepth(multDepth)
+    parameters.SetBatchSize(batchSize)
+    parameters.SetDigitSize(digitSize)
+    parameters.SetScalingModSize(dcrtBits)
+    parameters.SetThresholdNumOfParties(5)
+    parameters.SetMultiplicationTechnique(HPSPOVERQLEVELED)
+
+    cc = GenCryptoContext(parameters)
+    # Enable features you wish to use
+    cc.Enable(PKE)
+    cc.Enable(KEYSWITCH)
+    cc.Enable(LEVELEDSHE)
+    cc.Enable(ADVANCEDSHE)
+    cc.Enable(MULTIPARTY)
+
+    ##########################################################
+    # Set-up of parameters
+    ##########################################################
+
+    # Output the generated parameters
+    print(f"p = {cc.GetPlaintextModulus()}")
+    print(f"n = {cc.GetCyclotomicOrder() / 2}")
+    print(f"log2 q = {log2(cc.GetModulus())}")
+
+    ############################################################
+    ## Perform Key Generation Operation
+    ############################################################
+
+    print("Running key generation (used for source data)...")
+
+    # Round 1 (party A)
+
+    print("Round 1 (party A) started.")
+
+    kp1 = cc.KeyGen()
+    kp2 = cc.MultipartyKeyGen(kp1.publicKey)
+    kp3 = cc.MultipartyKeyGen(kp2.publicKey)
+    kp4 = cc.MultipartyKeyGen(kp3.publicKey)
+    kp5 = cc.MultipartyKeyGen(kp4.publicKey)
+
+    # Generate evalmult key part for A
+    evalMultKey = cc.KeySwitchGen(kp1.secretKey, kp1.secretKey)
+    evalMultKey2 = cc.MultiKeySwitchGen(kp2.secretKey, kp2.secretKey, evalMultKey)
+    evalMultKey3 = cc.MultiKeySwitchGen(kp3.secretKey, kp3.secretKey, evalMultKey)
+    evalMultKey4 = cc.MultiKeySwitchGen(kp4.secretKey, kp4.secretKey, evalMultKey)
+    evalMultKey5 = cc.MultiKeySwitchGen(kp5.secretKey, kp5.secretKey, evalMultKey)
+
+    evalMultAB = cc.MultiAddEvalKeys(evalMultKey, evalMultKey2, kp2.publicKey.GetKeyTag())
+    evalMultABC = cc.MultiAddEvalKeys(evalMultAB, evalMultKey3, kp3.publicKey.GetKeyTag())
+    evalMultABCD = cc.MultiAddEvalKeys(evalMultABC, evalMultKey4, kp4.publicKey.GetKeyTag())
+    evalMultABCDE = cc.MultiAddEvalKeys(evalMultABCD, evalMultKey5, kp5.publicKey.GetKeyTag())
+
+    evalMultEABCDE = cc.MultiMultEvalKey(kp5.secretKey, evalMultABCDE, kp5.publicKey.GetKeyTag())
+    evalMultDABCDE = cc.MultiMultEvalKey(kp4.secretKey, evalMultABCDE, kp5.publicKey.GetKeyTag())
+    evalMultCABCDE = cc.MultiMultEvalKey(kp3.secretKey, evalMultABCDE, kp5.publicKey.GetKeyTag())
+    evalMultBABCDE = cc.MultiMultEvalKey(kp2.secretKey, evalMultABCDE, kp5.publicKey.GetKeyTag())
+    evalMultAABCDE = cc.MultiMultEvalKey(kp1.secretKey, evalMultABCDE, kp5.publicKey.GetKeyTag())
+
+    evalMultDEABCDE = cc.MultiAddEvalMultKeys(evalMultEABCDE, evalMultDABCDE, evalMultEABCDE.GetKeyTag())
+    evalMultCDEABCDE = cc.MultiAddEvalMultKeys(evalMultCABCDE, evalMultDEABCDE, evalMultCABCDE.GetKeyTag())
+    evalMultBCDEABCDE = cc.MultiAddEvalMultKeys(evalMultBABCDE, evalMultCDEABCDE, evalMultBABCDE.GetKeyTag())
+
+    evalMultFinal = cc.MultiAddEvalMultKeys(evalMultAABCDE, evalMultBCDEABCDE, kp5.publicKey.GetKeyTag())
+    cc.InsertEvalMultKey([evalMultFinal])
+
+    print("Round 1 of key generation completed.")
+
+    ############################################################
+    ## EvalSum Key Generation
+    ############################################################
+
+    print("Running evalsum key generation (used for source data)...")
+
+    # Generate evalsum key part for A
+    cc.EvalSumKeyGen(kp1.secretKey)
+    evalSumKeys = cc.GetEvalSumKeyMap(kp1.secretKey.GetKeyTag())
+
+    evalSumKeysB = cc.MultiEvalSumKeyGen(kp2.secretKey, evalSumKeys, kp2.publicKey.GetKeyTag())
+    evalSumKeysC = cc.MultiEvalSumKeyGen(kp3.secretKey, evalSumKeys, kp3.publicKey.GetKeyTag())
+    evalSumKeysD = cc.MultiEvalSumKeyGen(kp4.secretKey, evalSumKeys, kp4.publicKey.GetKeyTag())
+    evalSumKeysE = cc.MultiEvalSumKeyGen(kp5.secretKey, evalSumKeys, kp5.publicKey.GetKeyTag())
+
+    evalSumKeysAB = cc.MultiAddEvalSumKeys(evalSumKeys, evalSumKeysB, kp2.publicKey.GetKeyTag())
+    evalSumKeysABC = cc.MultiAddEvalSumKeys(evalSumKeysC, evalSumKeysAB, kp3.publicKey.GetKeyTag())
+    evalSumKeysABCD = cc.MultiAddEvalSumKeys(evalSumKeysABC, evalSumKeysD, kp4.publicKey.GetKeyTag())
+
+    evalSumKeysJoin = cc.MultiAddEvalSumKeys(evalSumKeysE, evalSumKeysABCD, kp5.publicKey.GetKeyTag())
+    cc.InsertEvalSumKey(evalSumKeysJoin)
+
+    print("Evalsum key generation completed.")
+
+    ############################################################
+    ## Encode source data
+    ############################################################
+
+    vectorOfInts1 = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0]
+    vectorOfInts2 = [1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0]
+    vectorOfInts3 = [2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0]
+
+    plaintext1 = cc.MakePackedPlaintext(vectorOfInts1)
+    plaintext2 = cc.MakePackedPlaintext(vectorOfInts2)
+    plaintext3 = cc.MakePackedPlaintext(vectorOfInts3)
+
+    ############################################################
+    ## Encryption
+    ############################################################
+
+    ciphertext1 = cc.Encrypt(kp5.publicKey, plaintext1)
+    ciphertext2 = cc.Encrypt(kp5.publicKey, plaintext2)
+    ciphertext3 = cc.Encrypt(kp5.publicKey, plaintext3)
+
+    ############################################################
+    ## Homomorphic Operations
+    ############################################################
+
+    ciphertextAdd12 = cc.EvalAdd(ciphertext1, ciphertext2)
+    ciphertextAdd123 = cc.EvalAdd(ciphertextAdd12, ciphertext3)
+
+    ciphertextMult1 = cc.EvalMult(ciphertext1, ciphertext1)
+    ciphertextMult2 = cc.EvalMult(ciphertextMult1, ciphertext1)
+    ciphertextMult3 = cc.EvalMult(ciphertextMult2, ciphertext1)
+    ciphertextMult = cc.EvalMult(ciphertextMult3, ciphertext1)
+
+    ciphertextEvalSum = cc.EvalSum(ciphertext3, batchSize)
+
+    ############################################################
+    ## Decryption after Accumulation Operation on Encrypted Data with Multiparty
+    ############################################################
+
+    # Distributed decryption
+    # partial decryption by party A
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextAdd123], kp1.secretKey)
+
+    # partial decryption by party B
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextAdd123], kp2.secretKey)
+
+    # partial decryption by party C
+    ciphertextPartial3 = cc.MultipartyDecryptMain([ciphertextAdd123], kp3.secretKey)
+
+    # partial decryption by party D
+    ciphertextPartial4 = cc.MultipartyDecryptMain([ciphertextAdd123], kp4.secretKey)
+
+    # partial decryption by party E
+    ciphertextPartial5 = cc.MultipartyDecryptMain([ciphertextAdd123], kp5.secretKey)
+
+    partialCiphertextVec = [ciphertextPartial1[0], ciphertextPartial2[0], ciphertextPartial3[0],
+                            ciphertextPartial4[0], ciphertextPartial5[0]]
+
+    plaintextMultipartyNew = cc.MultipartyDecryptFusion(partialCiphertextVec)
+
+    print("\n Original Plaintext: \n")
+    print(plaintext1)
+    print(plaintext2)
+    print(plaintext3)
+
+    plaintextMultipartyNew.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext: \n")
+    print(plaintextMultipartyNew)
+
+    print("\n")
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextMult], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextMult], kp2.secretKey)
+    ciphertextPartial3 = cc.MultipartyDecryptMain([ciphertextMult], kp3.secretKey)
+    ciphertextPartial4 = cc.MultipartyDecryptMain([ciphertextMult], kp4.secretKey)
+    ciphertextPartial5 = cc.MultipartyDecryptMain([ciphertextMult], kp5.secretKey)
+
+    partialCiphertextVecMult = [ciphertextPartial1[0], ciphertextPartial2[0], ciphertextPartial3[0],
+                                ciphertextPartial4[0], ciphertextPartial5[0]]
+
+    plaintextMultipartyMult = cc.MultipartyDecryptFusion(partialCiphertextVecMult)
+
+    plaintextMultipartyMult.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext after Multiplication of plaintexts 1 and 3: \n")
+    print(plaintextMultipartyMult)
+
+    print("\n")
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextEvalSum], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextEvalSum], kp2.secretKey)
+    ciphertextPartial3 = cc.MultipartyDecryptMain([ciphertextEvalSum], kp3.secretKey)
+    ciphertextPartial4 = cc.MultipartyDecryptMain([ciphertextEvalSum], kp4.secretKey)
+    ciphertextPartial5 = cc.MultipartyDecryptMain([ciphertextEvalSum], kp5.secretKey)
+
+    partialCiphertextVecEvalSum = [ciphertextPartial1[0], ciphertextPartial2[0], ciphertextPartial3[0],
+                                   ciphertextPartial4[0], ciphertextPartial5[0]]
+
+    plaintextMultipartyEvalSum = cc.MultipartyDecryptFusion(partialCiphertextVecEvalSum)
+
+    plaintextMultipartyEvalSum.SetLength(plaintext1.GetLength())
+
+    print("\n Fused result after the Summation of ciphertext 3: \n")
+    print(plaintextMultipartyEvalSum)
+
+if __name__ == "__main__":
+    main()

+ 450 - 0
examples/pke/threshold-fhe.py

@@ -0,0 +1,450 @@
+from openfhe import *
+from math import log2
+
+def main():
+    print("\n=================RUNNING FOR BGVrns - Additive =====================")
+
+    RunBGVrnsAdditive()
+
+    print("\n=================RUNNING FOR BFVrns=====================")
+
+    RunBFVrns()
+
+    print("\n=================RUNNING FOR CKKS=====================")
+
+    RunCKKS()
+
+def RunBGVrnsAdditive():
+    parameters = CCParamsBGVRNS()
+    parameters.SetPlaintextModulus(65537)
+
+    # NOISE_FLOODING_MULTIPARTY adds extra noise to the ciphertext before decrypting
+    # and is most secure mode of threshold FHE for BFV and BGV.
+    parameters.SetMultipartyMode(NOISE_FLOODING_MULTIPARTY)
+
+    cc = GenCryptoContext(parameters)
+    # Enable Features you wish to use
+    cc.Enable(PKE)
+    cc.Enable(KEYSWITCH)
+    cc.Enable(LEVELEDSHE)
+    cc.Enable(ADVANCEDSHE)
+    cc.Enable(MULTIPARTY)
+
+    ##########################################################
+    # Set-up of parameters
+    ##########################################################
+
+    # Print out the parameters
+    print(f"p = {cc.GetPlaintextModulus()}")
+    print(f"n = {cc.GetCyclotomicOrder()/2}")
+    print(f"lo2 q = {log2(cc.GetModulus())}")
+
+    ############################################################
+    ## Perform Key Generation Operation
+    ############################################################
+
+    print("Running key generation (used for source data)...")
+
+    # generate the public key for first share
+    kp1 = cc.KeyGen()
+    # generate the public key for two shares
+    kp2 = cc.MultipartyKeyGen(kp1.publicKey)
+    # generate the public key for all three secret shares
+    kp3 = cc.MultipartyKeyGen(kp2.publicKey)
+
+    if not kp1.good():
+        print("Key generation failed!")
+        return 1
+    if not kp2.good():
+        print("Key generation failed!")
+        return 1
+    if not kp3.good():
+        print("Key generation failed!")
+        return 1
+    
+    ############################################################
+    ## Encode source data
+    ############################################################
+
+    vectorOfInts1 = [1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
+    vectorOfInts2 = [1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0]
+    vectorOfInts3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0]
+
+    plaintext1 = cc.MakePackedPlaintext(vectorOfInts1)
+    plaintext2 = cc.MakePackedPlaintext(vectorOfInts2)
+    plaintext3 = cc.MakePackedPlaintext(vectorOfInts3)
+
+    ############################################################
+    ## Encryption
+    ############################################################
+    ciphertext1 = cc.Encrypt(kp3.publicKey, plaintext1)
+    ciphertext2 = cc.Encrypt(kp3.publicKey, plaintext2)
+    ciphertext3 = cc.Encrypt(kp3.publicKey, plaintext3)
+
+    ############################################################
+    ## EvalAdd Operation on Re-Encrypted Data
+    ############################################################
+
+    ciphertextAdd12 = cc.EvalAdd(ciphertext1, ciphertext2)
+    ciphertextAdd123 = cc.EvalAdd(ciphertextAdd12, ciphertext3)
+
+    ############################################################
+    ## Decryption after Accumulation Operation on Encrypted Data with Multiparty
+    ############################################################
+
+    # partial decryption by first party
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextAdd123], kp1.secretKey)
+
+    # partial decryption by second party
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextAdd123], kp2.secretKey)
+
+    # partial decryption by third party
+    ciphertextPartial3 = cc.MultipartyDecryptMain([ciphertextAdd123], kp3.secretKey)
+
+    partialCiphertextVec = []
+    partialCiphertextVec.append(ciphertextPartial1[0])
+    partialCiphertextVec.append(ciphertextPartial2[0])
+    partialCiphertextVec.append(ciphertextPartial3[0])
+
+    # partial decryption are combined together
+    plaintextMultipartyNew = cc.MultipartyDecryptFusion(partialCiphertextVec)
+
+    print("\n Original Plaintext: \n")
+    print(plaintext1)
+    print(plaintext2)
+    print(plaintext3)
+
+    plaintextMultipartyNew.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext adding 3 ciphertexts: \n")
+    print(plaintextMultipartyNew)
+
+    print("\n")
+
+
+def RunBFVrns():
+    batchSize = 16
+
+    parameters = CCParamsBFVRNS()
+    parameters.SetPlaintextModulus(65537)
+    parameters.SetBatchSize(batchSize)
+    parameters.SetMultiplicativeDepth(2)
+    ## NOISE_FLOODING_MULTIPARTY adds extra noise to the ciphertext before decrypting
+    ## and is most secure mode of threshold FHE for BFV and BGV.
+    parameters.SetMultipartyMode(NOISE_FLOODING_MULTIPARTY)
+
+    cc = GenCryptoContext(parameters)
+    cc.Enable(PKE)
+    cc.Enable(KEYSWITCH)
+    cc.Enable(LEVELEDSHE)
+    cc.Enable(ADVANCEDSHE)
+    cc.Enable(MULTIPARTY)
+
+    ##########################################################
+    # Set-up of parameters
+    ##########################################################
+
+    # Output the generated parameters
+    print(f"p = {cc.GetPlaintextModulus()}")
+    print(f"n = {cc.GetCyclotomicOrder()/2}")
+    print(f"lo2 q = {log2(cc.GetModulus())}")
+
+    ############################################################
+    # Perform Key Generation Operation
+    ############################################################
+
+    print("Running key generation (used for source data)...")
+
+    # Round 1 (party A)
+
+    print("Round 1 (party A) started.")
+
+    kp1 = cc.KeyGen()
+
+    # Generate evalmult key part for A
+    evalMultKey = cc.KeySwitchGen(kp1.secretKey, kp1.secretKey)
+
+    # Generate evalsum key part for A
+    cc.EvalSumKeyGen(kp1.secretKey)
+    evalSumKeys = cc.GetEvalSumKeyMap(kp1.secretKey.GetKeyTag())
+    print("Round 1 of key generation completed.")
+
+    # Round 2 (party B)
+
+    print("Round 2 (party B) started.")
+
+    print("Joint public key for (s_a + s_b) is generated...")
+    kp2 = cc.MultipartyKeyGen(kp1.publicKey)
+
+    evalMultKey2 = cc.MultiKeySwitchGen(kp2.secretKey, kp2.secretKey, evalMultKey)
+
+    print("Joint evaluation multiplication key for (s_a + s_b) is generated...")
+    evalMultAB = cc.MultiAddEvalKeys(evalMultKey, evalMultKey2, kp2.publicKey.GetKeyTag())
+
+    print("Joint evaluation multiplication key (s_a + s_b) is transformed into s_b*(s_a + s_b)...")
+    evalMultBAB = cc.MultiMultEvalKey(kp2.secretKey, evalMultAB, kp2.publicKey.GetKeyTag())
+
+    evalSumKeysB = cc.MultiEvalSumKeyGen(kp2.secretKey, evalSumKeys, kp2.publicKey.GetKeyTag())
+
+    print("Joint evaluation summation key for (s_a + s_b) is generated...")
+    evalSumKeysJoin = cc.MultiAddEvalSumKeys(evalSumKeys, evalSumKeysB, kp2.publicKey.GetKeyTag())
+
+    cc.InsertEvalSumKey(evalSumKeysJoin)
+
+    print("Round 2 of key generation completed.")
+
+    print("Round 3 (party A) started.")
+
+    print("Joint key (s_a + s_b) is transformed into s_a*(s_a + s_b)...")
+    evalMultAAB = cc.MultiMultEvalKey(kp1.secretKey, evalMultAB, kp2.publicKey.GetKeyTag())
+
+    print("Computing the final evaluation multiplication key for (s_a + s_b)*(s_a + s_b)...")
+    evalMultFinal = cc.MultiAddEvalMultKeys(evalMultAAB, evalMultBAB, evalMultAB.GetKeyTag())
+
+    cc.InsertEvalMultKey([evalMultFinal])
+
+    print("Round 3 of key generation completed.")
+
+    ############################################################
+    ## Encode source data
+    ############################################################
+    vectorOfInts1 = [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 0]
+    vectorOfInts2 = [1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0]
+    vectorOfInts3 = [2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0]
+
+    plaintext1 = cc.MakePackedPlaintext(vectorOfInts1)
+    plaintext2 = cc.MakePackedPlaintext(vectorOfInts2)
+    plaintext3 = cc.MakePackedPlaintext(vectorOfInts3)
+
+    ############################################################
+    ## Encryption
+    ############################################################
+    ciphertext1 = cc.Encrypt(kp2.publicKey, plaintext1)
+    ciphertext2 = cc.Encrypt(kp2.publicKey, plaintext2)
+    ciphertext3 = cc.Encrypt(kp2.publicKey, plaintext3)
+
+    ############################################################
+    ## Homomorphic Operations
+    ############################################################
+    ciphertextAdd12 = cc.EvalAdd(ciphertext1, ciphertext2)
+    ciphertextAdd123 = cc.EvalAdd(ciphertextAdd12, ciphertext3)
+
+    ciphertextMult = cc.EvalMult(ciphertext1, ciphertext3)
+    ciphertextEvalSum = cc.EvalSum(ciphertext3, batchSize)
+
+    ############################################################
+    ## Decryption after Accumulation Operation on Encrypted Data with Multiparty
+    ############################################################
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextAdd123], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextAdd123], kp2.secretKey)
+
+    partialCiphertextVec = [ciphertextPartial1[0], ciphertextPartial2[0]]
+
+    plaintextMultipartyNew = cc.MultipartyDecryptFusion(partialCiphertextVec)
+
+    print("\n Original Plaintext: \n")
+    print(plaintext1)
+    print(plaintext2)
+    print(plaintext3)
+
+    plaintextMultipartyNew.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext: \n")
+    print(plaintextMultipartyNew)
+
+    print("\n")
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextMult], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextMult], kp2.secretKey)
+
+    partialCiphertextVecMult = [ciphertextPartial1[0], ciphertextPartial2[0]]
+
+    plaintextMultipartyMult = cc.MultipartyDecryptFusion(partialCiphertextVecMult)
+
+    plaintextMultipartyMult.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext after Multiplication of plaintexts 1 and 3: \n")
+    print(plaintextMultipartyMult)
+
+    print("\n")
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextEvalSum], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextEvalSum], kp2.secretKey)
+
+    partialCiphertextVecEvalSum = [ciphertextPartial1[0], ciphertextPartial2[0]]
+
+    plaintextMultipartyEvalSum = cc.MultipartyDecryptFusion(partialCiphertextVecEvalSum)
+
+    plaintextMultipartyEvalSum.SetLength(plaintext1.GetLength())
+
+    print("\n Fused result after summation of ciphertext 3: \n")
+    print(plaintextMultipartyEvalSum)
+
+    
+
+def RunCKKS():
+    batchSize = 16
+
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(3)
+    parameters.SetScalingModSize(50)
+    parameters.SetBatchSize(batchSize)
+
+    cc = GenCryptoContext(parameters)
+    # Enable features you wish to use
+    cc.Enable(PKE)
+    cc.Enable(KEYSWITCH)
+    cc.Enable(LEVELEDSHE)
+    cc.Enable(ADVANCEDSHE)
+    cc.Enable(MULTIPARTY)
+
+    ##########################################################
+    # Set-up of parameters
+    ##########################################################
+
+    # Output the generated parameters
+    print(f"p = {cc.GetPlaintextModulus()}")
+    print(f"n = {cc.GetCyclotomicOrder()/2}")
+    print(f"lo2 q = {log2(cc.GetModulus())}")
+
+    ############################################################
+    ## Perform Key Generation Operation
+    ############################################################
+
+    print("Running key generation (used for source data)...")
+
+    # Round 1 (party A)
+
+    print("Round 1 (party A) started.")
+
+    kp1 = cc.KeyGen()
+
+    # Generate evalmult key part for A
+    evalMultKey = cc.KeySwitchGen(kp1.secretKey, kp1.secretKey)
+
+    # Generate evalsum key part for A
+    cc.EvalSumKeyGen(kp1.secretKey)
+    evalSumKeys = cc.GetEvalSumKeyMap(kp1.secretKey.GetKeyTag())
+
+    print("Round 1 of key generation completed.")
+
+    # Round 2 (party B)
+
+    print("Round 2 (party B) started.")
+
+    print("Joint public key for (s_a + s_b) is generated...")
+    kp2 = cc.MultipartyKeyGen(kp1.publicKey)
+
+    evalMultKey2 = cc.MultiKeySwitchGen(kp2.secretKey, kp2.secretKey, evalMultKey)
+
+    print("Joint evaluation multiplication key for (s_a + s_b) is generated...")
+    evalMultAB = cc.MultiAddEvalKeys(evalMultKey, evalMultKey2, kp2.publicKey.GetKeyTag())
+
+    print("Joint evaluation multiplication key (s_a + s_b) is transformed into s_b*(s_a + s_b)...")
+    evalMultBAB = cc.MultiMultEvalKey(kp2.secretKey, evalMultAB, kp2.publicKey.GetKeyTag())
+
+    evalSumKeysB = cc.MultiEvalSumKeyGen(kp2.secretKey, evalSumKeys, kp2.publicKey.GetKeyTag())
+
+    print("Joint evaluation summation key for (s_a + s_b) is generated...")
+    evalSumKeysJoin = cc.MultiAddEvalSumKeys(evalSumKeys, evalSumKeysB, kp2.publicKey.GetKeyTag())
+
+    cc.InsertEvalSumKey(evalSumKeysJoin)
+
+    print("Round 2 of key generation completed.")
+
+    print("Round 3 (party A) started.")
+
+    print("Joint key (s_a + s_b) is transformed into s_a*(s_a + s_b)...")
+    evalMultAAB = cc.MultiMultEvalKey(kp1.secretKey, evalMultAB, kp2.publicKey.GetKeyTag())
+
+    print("Computing the final evaluation multiplication key for (s_a + s_b)*(s_a + s_b)...")
+    evalMultFinal = cc.MultiAddEvalMultKeys(evalMultAAB, evalMultBAB, evalMultAB.GetKeyTag())
+
+    cc.InsertEvalMultKey([evalMultFinal])
+
+    print("Round 3 of key generation completed.")
+
+    ############################################################
+    ## Encode source data
+    ############################################################
+
+    vectorOfInts1 = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.0]
+    vectorOfInts2 = [1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
+    vectorOfInts3 = [2.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 0.0, 0.0]
+
+    plaintext1 = cc.MakeCKKSPackedPlaintext(vectorOfInts1)
+    plaintext2 = cc.MakeCKKSPackedPlaintext(vectorOfInts2)
+    plaintext3 = cc.MakeCKKSPackedPlaintext(vectorOfInts3)
+
+    ############################################################
+    ## Encryption
+    ############################################################
+
+    ciphertext1 = cc.Encrypt(kp2.publicKey, plaintext1)
+    ciphertext2 = cc.Encrypt(kp2.publicKey, plaintext2)
+    ciphertext3 = cc.Encrypt(kp2.publicKey, plaintext3)
+
+    ############################################################
+    ## EvalAdd Operation on Re-Encrypted Data
+    ############################################################
+
+    ciphertextAdd12 = cc.EvalAdd(ciphertext1, ciphertext2)
+    ciphertextAdd123 = cc.EvalAdd(ciphertextAdd12, ciphertext3)
+
+    ciphertextMultTemp = cc.EvalMult(ciphertext1, ciphertext3)
+    ciphertextMult = cc.ModReduce(ciphertextMultTemp)
+    ciphertextEvalSum = cc.EvalSum(ciphertext3, batchSize)
+
+    ############################################################
+    ## Decryption after Accumulation Operation on Encrypted Data with Multiparty
+    ############################################################
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextAdd123], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextAdd123], kp2.secretKey)
+
+    partialCiphertextVec = [ciphertextPartial1[0], ciphertextPartial2[0]]
+
+    plaintextMultipartyNew = cc.MultipartyDecryptFusion(partialCiphertextVec)
+
+    print("\n Original Plaintext: \n")
+    print(plaintext1)
+    print(plaintext2)
+    print(plaintext3)
+
+    plaintextMultipartyNew.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext: \n")
+    print(plaintextMultipartyNew)
+
+    print("\n")
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextMult], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextMult], kp2.secretKey)
+
+    partialCiphertextVecMult = [ciphertextPartial1[0], ciphertextPartial2[0]]
+
+    plaintextMultipartyMult = cc.MultipartyDecryptFusion(partialCiphertextVecMult)
+
+    plaintextMultipartyMult.SetLength(plaintext1.GetLength())
+
+    print("\n Resulting Fused Plaintext after Multiplication of plaintexts 1 and 3: \n")
+    print(plaintextMultipartyMult)
+
+    print("\n")
+
+    ciphertextPartial1 = cc.MultipartyDecryptLead([ciphertextEvalSum], kp1.secretKey)
+    ciphertextPartial2 = cc.MultipartyDecryptMain([ciphertextEvalSum], kp2.secretKey)
+
+    partialCiphertextVecEvalSum = [ciphertextPartial1[0], ciphertextPartial2[0]]
+
+    plaintextMultipartyEvalSum = cc.MultipartyDecryptFusion(partialCiphertextVecEvalSum)
+
+    plaintextMultipartyEvalSum.SetLength(plaintext1.GetLength())
+
+    print("\n Fused result after the Summation of ciphertext 3: \n")
+    print(plaintextMultipartyEvalSum)
+
+if __name__ == '__main__':
+    main()
+

+ 8 - 0
src/include/docstrings/ciphertext_docs.h

@@ -44,4 +44,12 @@ const char* ctx_SetLevel_docs = R"pbdoc(
     :type level: int
 )pbdoc";
 
+//KeyPair Docs
+const char* kp_good_docs = R"pbdoc(
+    Checks whether both public key and secret key are non-null, or correctly initialized.
+
+    :return: Result.
+    :rtype: bool
+)pbdoc";
+
 #endif // CIPHERTEXT_DOCSTRINGS_H

+ 128 - 4
src/include/docstrings/cryptocontext_docs.h

@@ -59,6 +59,27 @@ const char* cc_GetRingDimension_docs = R"pbdoc(
     :rtype: int
 )pbdoc";
 
+const char* cc_GetPlaintextModulus_docs = R"pbdoc(
+    Get the plaintext modulus used for this context
+
+    :return: The plaintext modulus
+    :rtype: int
+)pbdoc";
+
+const char* cc_GetCyclotomicOrder_docs = R"pbdoc(
+    Get the cyclotomic order used for this context
+
+    :return: The cyclotomic order
+    :rtype: int
+)pbdoc";
+
+const char* cc_GetModulus_docs = R"pbdoc(
+    Get the cyclotomic order used for this context
+
+    :return: The modulus
+    :rtype: int
+)pbdoc";
+
 const char* cc_Enable_docs = R"pbdoc(
     Enable a particular feature for use with this CryptoContext
 
@@ -143,8 +164,8 @@ const char* cc_MakeCKKSPackedPlaintextComplex_docs = R"pbdoc(
 
     :param value: input vector (of complex numbers)
     :type value: list
-    :param depth: depth used to encode the vector
-    :type depth: int
+    :param scaleDeg: degree of scaling factor used to encode the vector
+    :type scaleDeg: int
     :param level: level at each the vector will get encrypted
     :type level: int
     :param params: parameters to be used for the ciphertext (Only accepting params = None in this version)
@@ -160,8 +181,8 @@ const char* cc_MakeCKKSPlaintextReal_docs = R"pbdoc(
 
     :param value: input vector (of floats)
     :type value: list
-    :param depth: depth used to encode the vector
-    :type depth: int
+    :param scaleDeg: degree of scaling factor used to encode the vector
+    :type scaleDeg: int
     :param level: level at each the vector will get encrypted
     :type level: int
     :param params: parameters to be used for the ciphertext (Only accepting params = None in this version)
@@ -308,6 +329,17 @@ Decrypt a single ciphertext into the appropriate plaintext
 :rtype: Plaintext
 )pbdoc";
 
+const char* cc_KeySwitchGen_docs = R"pbdoc(
+    KeySwitchGen creates a key that can be used with the OpenFHE KeySwitch operation
+
+    :param oldPrivateKey: input secrey key
+    :type oldPrivateKey: PrivateKey
+    :param newPrivateKey: output secret key
+    :type newPrivateKey: PrivateKey
+    :return: new evaluation key
+    :rtype: EvalKey
+)pbdoc";
+
 const char* cc_EvalAdd_docs = R"pbdoc(
 Add two ciphertexts
 
@@ -825,6 +857,75 @@ const char* cc_EvalInnerProductPlaintext_docs = R"pbdoc(
     :rtype: Ciphertext
 )pbdoc";
 
+const char* cc_MultipartyKeyGen_docs = R"pbdoc(
+    Threshold FHE: Generation of a public key derived from a previous joined public key (for prior secret shares) and the secret key share of the current party.
+
+    :param publicKey:  joined public key from prior parties.
+    :type publicKey: PublicKey
+    :param makeSparse: set to true if ring reduce by a factor of 2 is to be used. NOT SUPPORTED BY ANY SCHEME ANYMORE.
+    :type makeSparse: bool
+    :param fresh: set to true if proxy re-encryption is used in the multi-party protocol or star topology is used
+    :type fresh: bool
+    :return: KeyPair: key pair including the secret share for the current party and joined public key
+    :rtype: KeyPair
+)pbdoc";
+
+const char* cc_MultipartyDecryptLead_docs = R"pbdoc(
+    Threshold FHE: Method for decryption operation run by the lead decryption client
+
+    :param ciphertextVec: a list of ciphertexts
+    :type ciphertextVec: list
+    :param privateKey:  secret key share used for decryption. list of partially decrypted ciphertexts.
+    :type privateKey: PrivateKey
+    :return: Ciphertext: resulting ciphertext
+    :rtype: Ciphertext
+)pbdoc";
+
+const char* cc_MultipartyDecryptMain_docs = R"pbdoc(
+    Threshold FHE: "Partial" decryption computed by all parties except for the lead one
+
+    :param ciphertextVec: a list of ciphertexts
+    :type ciphertextVec: list
+    :param privateKey:  secret key share used for decryption. list of partially decrypted ciphertexts.
+    :type privateKey: PrivateKey
+    :return: Ciphertext: resulting ciphertext
+    :rtype: Ciphertext
+)pbdoc";
+
+const char* cc_MultipartyDecryptFusion_docs = R"pbdoc(
+    Threshold FHE: Method for combining the partially decrypted ciphertexts and getting the final decryption in the clear.
+
+    :param partialCiphertextVec: list of "partial" decryptions
+    :type partialCiphertextVec: list
+    :return: Plaintext: resulting plaintext
+    :rtype: Plaintext
+)pbdoc";
+
+const char* cc_MultiKeySwitchGen_docs = R"pbdoc(
+    Threshold FHE: Generates a joined evaluation key from the current secret share and a prior joined evaluation key
+
+    :param originalPrivateKey: secret key transformed from.
+    :type originalPrivateKey: PrivateKey
+    :param newPrivateKey: secret key transformed from.
+    :type newPrivateKey: PrivateKey
+    :param evalKey: the prior joined evaluation key.
+    :type evalKey: EvalKey
+    :return: EvalKey: the new joined evaluation key.
+    :rtype: EvalKey
+)pbdoc";
+
+// TODO (Oliveira, R.) - Complete the following documentation
+const char* cc_GetEvalSumKeyMap_docs = "";
+const char* cc_InsertEvalSumKey_docs = "";
+const char* cc_MultiEvalSumKeyGen_docs = "";
+const char* cc_MultiAddEvalKeys_docs = "";
+const char* cc_MultiMultEvalKey_docs = "";
+const char* cc_MultiAddEvalSumKeys_docs = "";
+const char* cc_MultiAddEvalMultKeys_docs = "";
+const char* cc_InsertEvalMultKey_docs = "";
+const char* cc_EvalSum_docs = "";
+
+
 const char* cc_EvalMerge_docs = R"pbdoc(
     Merges multiple ciphertexts with encrypted results in slot 0 into a single ciphertext The slot assignment is done based on the order of ciphertexts in the vector
 
@@ -875,6 +976,29 @@ const char* cc_Rescale_docs = R"pbdoc(
     :rtype: Ciphertext
 )pbdoc";
 
+const char* cc_RescaleInPlace_docs = R"pbdoc(
+    RescaleInPlace - An alias for OpenFHE ModReduceInPlace method. This is because ModReduceInPlace is called RescaleInPlace in CKKS.
+
+    :param ciphertext: ciphertext
+    :type ciphertext: Ciphertext
+)pbdoc";
+
+const char* cc_ModReduce_docs = R"pbdoc(
+    ModReduce - OpenFHE ModReduce method used only for BGV/CKKS.
+
+    :param ciphertext: ciphertext
+    :type ciphertext: Ciphertext
+    :return: Ciphertext: mod reduced ciphertext
+    :rtype: Ciphertext
+)pbdoc";
+
+const char* cc_ModReduceInPlace_docs = R"pbdoc(
+    ModReduce - OpenFHE ModReduceInPlace method used only for BGV/CKKS.
+
+    :param ciphertext: ciphertext to be mod-reduced in-place
+    :type ciphertext: Ciphertext
+)pbdoc";
+
 const char* cc_EvalBootstrapSetup_docs = R"pbdoc(
     Bootstrap functionality: There are three methods that have to be called in this specific order:
 

+ 4 - 0
src/include/pke/cryptocontext_wrapper.h

@@ -54,7 +54,11 @@ Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,
 ConstCiphertext<DCRTPoly> ciphertext,const PrivateKey<DCRTPoly> privateKey);
 Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,
 const PrivateKey<DCRTPoly> privateKey,ConstCiphertext<DCRTPoly> ciphertext);
+Plaintext MultipartyDecryptFusionWrapper(CryptoContext<DCRTPoly>& self,const std::vector<Ciphertext<DCRTPoly>>& partialCiphertextVec);
 
 const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper(CryptoContext<DCRTPoly>& self,const PrivateKey<DCRTPoly> privateKey,const std::vector<usint> &indexList);
 const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper_PublicKey(CryptoContext<DCRTPoly>& self,const PublicKey<DCRTPoly> publicKey, const PrivateKey<DCRTPoly> privateKey, const std::vector<usint> &indexList);
+const std::shared_ptr<std::map<usint, EvalKey<DCRTPoly>>> GetEvalSumKeyMapWrapper(CryptoContext<DCRTPoly>& self, const std::string &id);
+const PlaintextModulus GetPlaintextModulusWrapper(CryptoContext<DCRTPoly>& self);
+const double GetModulusWrapper(CryptoContext<DCRTPoly>& self);
 #endif // OPENFHE_CRYPTOCONTEXT_BINDINGS_H

+ 9 - 0
src/include/pke/serialization.h

@@ -29,9 +29,18 @@
 #define OPENFHE_SERIALIZATION_BINDINGS_H
 
 #include <pybind11/pybind11.h>
+#include "openfhe.h"
+#include "bindings.h"
+
 using namespace lbcrypto;
 
 template <typename ST>
 bool SerializeEvalMultKeyWrapper(const std::string& filename, const ST& sertype, std::string id);
 
+template <typename ST>
+bool SerializeEvalAutomorphismKeyWrapper(const std::string& filename, const ST& sertype, std::string id);
+
+template <typename ST>
+bool DeserializeEvalMultKeyWrapper(const std::string& filename, const ST& sertype);
+
 #endif // OPENFHE_SERIALIZATION_BINDINGS_H

+ 157 - 18
src/lib/bindings.cpp

@@ -15,6 +15,7 @@
 #include "cryptocontext_docs.h"
 #include "plaintext_docs.h"
 #include "ciphertext_docs.h"
+#include "serialization.h"
 
 using namespace lbcrypto;
 namespace py = pybind11;
@@ -69,7 +70,7 @@ void bind_parameters(py::module &m,const std::string name)
         .def("SetDesiredPrecision", &CCParams<T>::SetDesiredPrecision)
         .def("SetStatisticalSecurity", &CCParams<T>::SetStatisticalSecurity)
         .def("SetNumAdversarialQueries", &CCParams<T>::SetNumAdversarialQueries)
-        //.def("SetThresholdNumOfParties", &CCParams<T>::SetThresholdNumOfParties)
+        .def("SetThresholdNumOfParties", &CCParams<T>::SetThresholdNumOfParties)
         .def("SetKeySwitchTechnique", &CCParams<T>::SetKeySwitchTechnique)
         .def("SetScalingTechnique", &CCParams<T>::SetScalingTechnique)
         .def("SetBatchSize", &CCParams<T>::SetBatchSize)
@@ -101,9 +102,14 @@ void bind_crypto_context(py::module &m)
         .def("GetKeyGenLevel", &CryptoContextImpl<DCRTPoly>::GetKeyGenLevel, cc_GetKeyGenLevel_docs)
         .def("SetKeyGenLevel", &CryptoContextImpl<DCRTPoly>::SetKeyGenLevel, cc_SetKeyGenLevel_docs,
              py::arg("level"))
+        .def("get_ptr", [](const CryptoContext<DCRTPoly> &self)
+             { std::cout << "CC shared ptr (python cc)" << self << std::endl; })
         //.def("GetScheme",&CryptoContextImpl<DCRTPoly>::GetScheme)
         //.def("GetCryptoParameters", &CryptoContextImpl<DCRTPoly>::GetCryptoParameters)
         .def("GetRingDimension", &CryptoContextImpl<DCRTPoly>::GetRingDimension, cc_GetRingDimension_docs)
+        .def("GetPlaintextModulus", &GetPlaintextModulusWrapper, cc_GetPlaintextModulus_docs)
+        .def("GetModulus", &GetModulusWrapper, cc_GetModulus_docs)
+        .def("GetCyclotomicOrder", &CryptoContextImpl<DCRTPoly>::GetCyclotomicOrder, cc_GetCyclotomicOrder_docs)
         .def("Enable", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(PKESchemeFeature)>(&CryptoContextImpl<DCRTPoly>::Enable), cc_Enable_docs,
              py::arg("feature"))
         .def("KeyGen", &CryptoContextImpl<DCRTPoly>::KeyGen, cc_KeyGen_docs)
@@ -134,13 +140,13 @@ void bind_crypto_context(py::module &m)
         // TODO (Oliveira): allow user to specify different params values
         .def("MakeCKKSPackedPlaintext", static_cast<Plaintext (CryptoContextImpl<DCRTPoly>::*)(const std::vector<std::complex<double>> &, size_t, uint32_t, const std::shared_ptr<ParmType>, usint) const>(&CryptoContextImpl<DCRTPoly>::MakeCKKSPackedPlaintext), cc_MakeCKKSPackedPlaintextComplex_docs,
              py::arg("value"),
-             py::arg("depth") = static_cast<size_t>(1),
+             py::arg("scaleDeg") = static_cast<size_t>(1),
              py::arg("level") = static_cast<uint32_t>(0),
              py::arg("params") = py::none(),
              py::arg("slots") = 0)
         .def("MakeCKKSPackedPlaintext", static_cast<Plaintext (CryptoContextImpl<DCRTPoly>::*)(const std::vector<double> &, size_t, uint32_t, const std::shared_ptr<ParmType>, usint) const>(&CryptoContextImpl<DCRTPoly>::MakeCKKSPackedPlaintext), cc_MakeCKKSPlaintextReal_docs,
              py::arg("value"),
-             py::arg("depth") = static_cast<size_t>(1),
+             py::arg("scaleDeg") = static_cast<size_t>(1),
              py::arg("level") = static_cast<uint32_t>(0),
              py::arg("params") = py::none(),
              py::arg("slots") = 0)
@@ -185,6 +191,10 @@ void bind_crypto_context(py::module &m)
             (&DecryptWrapper), cc_Decrypt_docs,
             py::arg("ciphertext"),
             py::arg("privateKey"))
+        .def("KeySwitchGen", &CryptoContextImpl<DCRTPoly>::KeySwitchGen,
+            cc_KeySwitchGen_docs,
+            py::arg("oldPrivateKey"),
+            py::arg("newPrivateKey"))
         .def("EvalAdd", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>
             (&CryptoContextImpl<DCRTPoly>::EvalAdd), 
             cc_EvalAdd_docs,
@@ -425,6 +435,10 @@ void bind_crypto_context(py::module &m)
              cc_EvalSumColsKeyGen_docs,
              py::arg("privateKey"),
              py::arg("publicKey") = py::none())
+        .def("EvalSum", &CryptoContextImpl<DCRTPoly>::EvalSum,
+             cc_EvalSum_docs,
+             py::arg("ciphertext"),
+             py::arg("batchSize"))
         .def("EvalSumRows", &CryptoContextImpl<DCRTPoly>::EvalSumRows,
              cc_EvalSumRows_docs,
              py::arg("ciphertext"),
@@ -446,6 +460,52 @@ void bind_crypto_context(py::module &m)
              py::arg("ciphertext"),
              py::arg("plaintext"),
              py::arg("batchSize"))
+        .def("MultipartyKeyGen", static_cast<KeyPair<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(const PublicKey<DCRTPoly>, bool, bool)>(&CryptoContextImpl<DCRTPoly>::MultipartyKeyGen),
+             cc_MultipartyKeyGen_docs,
+             py::arg("publicKey"),
+             py::arg("makeSparse") = false,
+             py::arg("fresh") = false)
+        .def("MultipartyDecryptLead", &CryptoContextImpl<DCRTPoly>::MultipartyDecryptLead,
+             cc_MultipartyDecryptLead_docs,
+             py::arg("ciphertextVec"),
+             py::arg("privateKey"))
+        .def("MultipartyDecryptMain", &CryptoContextImpl<DCRTPoly>::MultipartyDecryptMain,
+            cc_MultipartyDecryptMain_docs,
+            py::arg("ciphertextVec"),
+            py::arg("privateKey"))
+        .def("MultipartyDecryptFusion", &MultipartyDecryptFusionWrapper,
+            cc_MultipartyDecryptFusion_docs,
+            py::arg("ciphertextVec"))
+        .def("MultiKeySwitchGen", &CryptoContextImpl<DCRTPoly>::MultiKeySwitchGen,
+             cc_MultiKeySwitchGen_docs,
+             py::arg("originalPrivateKey"),
+             py::arg("newPrivateKey"),
+             py::arg("evalKey"))
+        .def("MultiEvalSumKeyGen", &CryptoContextImpl<DCRTPoly>::MultiEvalSumKeyGen,
+             cc_MultiEvalSumKeyGen_docs,
+             py::arg("privateKey"),
+             py::arg("evalKeyMap"),
+             py::arg("keyId") = "")
+        .def("MultiAddEvalKeys", &CryptoContextImpl<DCRTPoly>::MultiAddEvalKeys,
+             cc_MultiAddEvalKeys_docs,
+             py::arg("evalKey1"),
+             py::arg("evalKey2"),
+             py::arg("keyId") = "")
+        .def("MultiAddEvalMultKeys", &CryptoContextImpl<DCRTPoly>::MultiAddEvalMultKeys,
+             cc_MultiAddEvalMultKeys_docs,
+             py::arg("evalKey1"),
+             py::arg("evalKey2"),
+             py::arg("keyId") = "")
+        .def("MultiMultEvalKey", &CryptoContextImpl<DCRTPoly>::MultiMultEvalKey,
+             cc_MultiMultEvalKey_docs,
+             py::arg("privateKey"),
+             py::arg("evalKey"),
+             py::arg("keyId") = "")
+        .def("MultiAddEvalSumKeys", &CryptoContextImpl<DCRTPoly>::MultiAddEvalSumKeys,
+             cc_MultiAddEvalSumKeys_docs,
+             py::arg("evalKeyMap1"),
+             py::arg("evalKeyMap2"),
+             py::arg("keyId") = "")
         .def("EvalMerge", &CryptoContextImpl<DCRTPoly>::EvalMerge,
              cc_EvalMerge_docs,
              py::arg("ciphertextVec"))
@@ -464,6 +524,15 @@ void bind_crypto_context(py::module &m)
         .def("Rescale", &CryptoContextImpl<DCRTPoly>::Rescale,
              cc_Rescale_docs,
              py::arg("ciphertext"))
+        .def("RescaleInPlace", &CryptoContextImpl<DCRTPoly>::RescaleInPlace,
+             cc_RescaleInPlace_docs,
+             py::arg("ciphertext"))
+        .def("ModReduce", &CryptoContextImpl<DCRTPoly>::ModReduce,
+             cc_ModReduce_docs,
+             py::arg("ciphertext"))
+        .def("ModReduceInPlace", &CryptoContextImpl<DCRTPoly>::ModReduceInPlace,
+             cc_ModReduceInPlace_docs,
+             py::arg("ciphertext"))
         .def("EvalBootstrapSetup", &CryptoContextImpl<DCRTPoly>::EvalBootstrapSetup,
              cc_EvalBootstrapSetup_docs,
              py::arg("levelBudget") = std::vector<uint32_t>({5, 4}),
@@ -501,10 +570,21 @@ void bind_crypto_context(py::module &m)
             "ClearEvalMultKeys", []()
             { CryptoContextImpl<DCRTPoly>::ClearEvalMultKeys(); },
             cc_ClearEvalMultKeys_docs)
+        .def_static(
+            "InsertEvalSumKey", &CryptoContextImpl<DCRTPoly>::InsertEvalSumKey,
+            cc_InsertEvalSumKey_docs,
+            py::arg("evalKeyMap"))
+        .def_static(
+            "InsertEvalMultKey", &CryptoContextImpl<DCRTPoly>::InsertEvalMultKey,
+            cc_InsertEvalMultKey_docs,
+            py::arg("evalKeyVec"))
         .def_static(
             "ClearEvalAutomorphismKeys", []()
             { CryptoContextImpl<DCRTPoly>::ClearEvalAutomorphismKeys(); },
             cc_ClearEvalAutomorphismKeys_docs)
+        .def("GetEvalSumKeyMap", &GetEvalSumKeyMapWrapper,
+            cc_GetEvalSumKeyMap_docs,
+            py::return_value_policy::reference)
         .def_static(
             "SerializeEvalMultKey", [](const std::string &filename, const SerType::SERBINARY &sertype, std::string id = "")
             {
@@ -515,7 +595,17 @@ void bind_crypto_context(py::module &m)
                 return res; },
             cc_SerializeEvalMultKey_docs,
             py::arg("filename"), py::arg("sertype"), py::arg("id") = "")
-        .def_static(
+        .def_static( // SerializeEvalMultKey - JSON
+            "SerializeEvalMultKey", [](const std::string &filename, const SerType::SERJSON &sertype, std::string id = "")
+            {
+                std::ofstream outfile(filename,std::ios::out | std::ios::binary);
+                bool res;
+                res = CryptoContextImpl<DCRTPoly>::SerializeEvalMultKey<SerType::SERJSON>(outfile, sertype, id);
+                outfile.close();
+                return res; },
+            cc_SerializeEvalMultKey_docs,
+            py::arg("filename"), py::arg("sertype"), py::arg("id") = "")
+        .def_static( // SerializeEvalAutomorphismKey - Binary
             "SerializeEvalAutomorphismKey", [](const std::string &filename, const SerType::SERBINARY &sertype, std::string id = "")
             {
                 std::ofstream outfile(filename,std::ios::out | std::ios::binary);
@@ -525,29 +615,65 @@ void bind_crypto_context(py::module &m)
                 return res; },
             cc_SerializeEvalAutomorphismKey_docs,
             py::arg("filename"), py::arg("sertype"), py::arg("id") = "")
-        .def_static("DeserializeEvalMultKey", [](std::shared_ptr<CryptoContextImpl<DCRTPoly>> &self, const std::string &filename, const SerType::SERBINARY &sertype)
+        .def_static( // SerializeEvalAutomorphismKey - JSON
+            "SerializeEvalAutomorphismKey", [](const std::string &filename, const SerType::SERJSON &sertype, std::string id = "")
+            {
+                std::ofstream outfile(filename,std::ios::out | std::ios::binary);
+                bool res;
+                res = CryptoContextImpl<DCRTPoly>::SerializeEvalAutomorphismKey<SerType::SERJSON>(outfile, sertype, id);
+                outfile.close();
+                return res; },
+            cc_SerializeEvalAutomorphismKey_docs,
+            py::arg("filename"), py::arg("sertype"), py::arg("id") = "")
+        .def_static("DeserializeEvalMultKey", // DeserializeEvalMultKey - Binary
+        [](const std::string &filename, const SerType::SERBINARY &sertype)
+                    {
+                        std::ifstream emkeys(filename, std::ios::in | std::ios::binary);
+                         if (!emkeys.is_open()) {
+                            std::cerr << "I cannot read serialization from " << filename << std::endl;
+                         }
+                        bool res;
+                        res = CryptoContextImpl<DCRTPoly>::DeserializeEvalMultKey<SerType::SERBINARY>(emkeys, sertype);
+                        return res; 
+                        
+                        },
+                        cc_DeserializeEvalMultKey_docs,
+                        py::arg("filename"), py::arg("sertype"))
+        .def_static("DeserializeEvalMultKey", // DeserializeEvalMultKey - JSON
+        [](const std::string &filename, const SerType::SERJSON &sertype)
                     {
                         std::ifstream emkeys(filename, std::ios::in | std::ios::binary);
                          if (!emkeys.is_open()) {
                             std::cerr << "I cannot read serialization from " << filename << std::endl;
                          }
                         bool res;
-                        res = self->DeserializeEvalMultKey<SerType::SERBINARY>(emkeys, sertype);
+                        res = CryptoContextImpl<DCRTPoly>::DeserializeEvalMultKey<SerType::SERJSON>(emkeys, sertype);
                         return res; },
                         cc_DeserializeEvalMultKey_docs,
-                        py::arg("self"),
                         py::arg("filename"), py::arg("sertype"))
-        .def_static("DeserializeEvalAutomorphismKey", [](std::shared_ptr<CryptoContextImpl<DCRTPoly>> &self, const std::string &filename, const SerType::SERBINARY &sertype)
+        .def_static("DeserializeEvalAutomorphismKey", // DeserializeEvalAutomorphismKey - Binary
+        [](const std::string &filename, const SerType::SERBINARY &sertype)
+                    {
+                        std::ifstream erkeys(filename, std::ios::in | std::ios::binary);
+                         if (!erkeys.is_open()) {
+                            std::cerr << "I cannot read serialization from " << filename << std::endl;
+                         }
+                        bool res;
+                        res = CryptoContextImpl<DCRTPoly>::DeserializeEvalAutomorphismKey<SerType::SERBINARY>(erkeys, sertype);
+                        return res; },
+                        cc_DeserializeEvalAutomorphismKey_docs,
+                        py::arg("filename"), py::arg("sertype"))
+        .def_static("DeserializeEvalAutomorphismKey", // DeserializeEvalAutomorphismKey - JSON
+        [](const std::string &filename, const SerType::SERJSON &sertype)
                     {
                         std::ifstream erkeys(filename, std::ios::in | std::ios::binary);
                          if (!erkeys.is_open()) {
                             std::cerr << "I cannot read serialization from " << filename << std::endl;
                          }
                         bool res;
-                        res = self->DeserializeEvalAutomorphismKey<SerType::SERBINARY>(erkeys, sertype);
+                        res = CryptoContextImpl<DCRTPoly>::DeserializeEvalAutomorphismKey<SerType::SERJSON>(erkeys, sertype);
                         return res; },
                         cc_DeserializeEvalAutomorphismKey_docs,
-                        py::arg("self"),
                         py::arg("filename"), py::arg("sertype"));
 
     // Generator Functions
@@ -558,6 +684,7 @@ void bind_crypto_context(py::module &m)
     m.def("GenCryptoContext", &GenCryptoContext<CryptoContextCKKSRNS>,
         py::arg("params"));
     m.def("ReleaseAllContexts", &CryptoContextFactory<DCRTPoly>::ReleaseAllContexts);
+    m.def("GetAllContexts", &CryptoContextFactory<DCRTPoly>::GetAllContexts);
 }
 
 int get_native_int(){
@@ -712,19 +839,28 @@ void bind_enums_and_constants(py::module &m)
     //NATIVEINT function
     m.def("get_native_int", &get_native_int);
   
-    // EvalKeyMap
-    py::bind_map<std::map<usint, EvalKey<DCRTPoly>>>(m, "EvalKeyMap");
+    
 }
 
 void bind_keys(py::module &m)
 {
     py::class_<PublicKeyImpl<DCRTPoly>, std::shared_ptr<PublicKeyImpl<DCRTPoly>>>(m, "PublicKey")
-        .def(py::init<>());
-    py::class_<PrivateKeyImpl<DCRTPoly>, std::shared_ptr<PrivateKeyImpl<DCRTPoly>>>(m, "PrivateKey");
+        .def(py::init<>())
+        .def("GetKeyTag", &PublicKeyImpl<DCRTPoly>::GetKeyTag)
+        .def("SetKeyTag", &PublicKeyImpl<DCRTPoly>::SetKeyTag);
+    py::class_<PrivateKeyImpl<DCRTPoly>, std::shared_ptr<PrivateKeyImpl<DCRTPoly>>>(m, "PrivateKey")
+        .def(py::init<>())
+        .def("GetKeyTag", &PrivateKeyImpl<DCRTPoly>::GetKeyTag)
+        .def("SetKeyTag", &PrivateKeyImpl<DCRTPoly>::SetKeyTag);
     py::class_<KeyPair<DCRTPoly>>(m, "KeyPair")
         .def_readwrite("publicKey", &KeyPair<DCRTPoly>::publicKey)
-        .def_readwrite("secretKey", &KeyPair<DCRTPoly>::secretKey);
+        .def_readwrite("secretKey", &KeyPair<DCRTPoly>::secretKey)
+        .def("good", &KeyPair<DCRTPoly>::good,kp_good_docs);
     py::class_<EvalKeyImpl<DCRTPoly>, std::shared_ptr<EvalKeyImpl<DCRTPoly>>>(m, "EvalKey")
+    .def(py::init<>())
+        .def("GetKeyTag", &EvalKeyImpl<DCRTPoly>::GetKeyTag)
+        .def("SetKeyTag", &EvalKeyImpl<DCRTPoly>::SetKeyTag);
+    py::class_<std::map<usint, EvalKey<DCRTPoly>>, std::shared_ptr<std::map<usint, EvalKey<DCRTPoly>>>>(m, "EvalKeyMap")
         .def(py::init<>());
 }
 
@@ -780,7 +916,9 @@ void bind_ciphertext(py::module &m)
         ctx_GetLevel_docs)
      .def("SetLevel", &CiphertextImpl<DCRTPoly>::SetLevel,
         ctx_SetLevel_docs,
-        py::arg("level"));
+        py::arg("level"))
+    .def("get_ptr",[](const Ciphertext<DCRTPoly> &self){
+        std::cout<< "cryptoparameters shared ptr (python)" << self->GetCryptoContext().get() << std::endl;});
     // .def("GetHopLevel", &CiphertextImpl<DCRTPoly>::GetHopLevel)
     // .def("SetHopLevel", &CiphertextImpl<DCRTPoly>::SetHopLevel)
     // .def("GetScalingFactor", &CiphertextImpl<DCRTPoly>::GetScalingFactor)
@@ -794,7 +932,8 @@ void bind_schemes(py::module &m){
     py::class_<FHECKKSRNS>(m, "FHECKKSRNS")
         .def(py::init<>())
         //.def_static("GetBootstrapDepth", &FHECKKSRNS::GetBootstrapDepth)
-        .def_static("GetBootstrapDepth", static_cast<uint32_t (*)(uint32_t, const std::vector<uint32_t>&, SecretKeyDist)>(&FHECKKSRNS::GetBootstrapDepth));                               
+        .def_static("GetBootstrapDepth", static_cast<uint32_t (*)(uint32_t, const std::vector<uint32_t>&, SecretKeyDist)>(&FHECKKSRNS::GetBootstrapDepth))
+        .def_static("GetBootstrapDepth", static_cast<uint32_t (*)(const std::vector<uint32_t>&, SecretKeyDist)>(&FHECKKSRNS::GetBootstrapDepth));                               
     
 }
 
@@ -817,4 +956,4 @@ PYBIND11_MODULE(openfhe, m)
     bind_binfhe_context(m);
     bind_binfhe_keys(m);
     bind_binfhe_ciphertext(m);
-}
+}

+ 21 - 1
src/lib/pke/cryptocontext_wrapper.cpp

@@ -69,10 +69,30 @@ Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,const PrivateKey<DCRTPoly
     return plaintextDecResult;
 }
 
+Plaintext MultipartyDecryptFusionWrapper(CryptoContext<DCRTPoly>& self,const std::vector<Ciphertext<DCRTPoly>>& partialCiphertextVec){
+    Plaintext plaintextDecResult;
+    self->MultipartyDecryptFusion(partialCiphertextVec,&plaintextDecResult);
+    return plaintextDecResult;
+}
+
 const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper(CryptoContext<DCRTPoly>& self,const PrivateKey<DCRTPoly> privateKey,const std::vector<usint> &indexList){
     return *(self->EvalAutomorphismKeyGen(privateKey, indexList));
 }
 
 const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper_PublicKey(CryptoContext<DCRTPoly>& self,const PublicKey<DCRTPoly> publicKey, const PrivateKey<DCRTPoly> privateKey, const std::vector<usint> &indexList){
     return *(self->EvalAutomorphismKeyGen(publicKey, privateKey, indexList));
-};
+};
+
+const std::shared_ptr<std::map<usint, EvalKey<DCRTPoly>>> GetEvalSumKeyMapWrapper(CryptoContext<DCRTPoly>& self,const std::string &id){
+    auto evalSumKeyMap = 
+        std::make_shared<std::map<usint, EvalKey<DCRTPoly>>>(self->GetEvalSumKeyMap(id));
+    return evalSumKeyMap;
+}
+
+const PlaintextModulus GetPlaintextModulusWrapper(CryptoContext<DCRTPoly>& self){
+    return self->GetCryptoParameters()->GetPlaintextModulus();
+}
+
+const double GetModulusWrapper(CryptoContext<DCRTPoly>& self){
+    return self->GetCryptoParameters()->GetElementParams()->GetModulus().ConvertToDouble();
+}

+ 77 - 22
src/lib/pke/serialization.cpp

@@ -28,17 +28,23 @@
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h>
 #include "openfhe.h"
-#include "scheme/bfvrns/bfvrns-ser.h"
-#include "scheme/bgvrns/bgvrns-ser.h"
-#include "cryptocontext-ser.h"
 #include "bindings.h"
+#include "utils/exception.h"
+// header files needed for serialization
 #include "serialization.h"
+#include "metadata-ser.h"
+#include "ciphertext-ser.h"
+#include "cryptocontext-ser.h"
+#include "key/key-ser.h"
+#include "scheme/bfvrns/bfvrns-ser.h"
+#include "scheme/bgvrns/bgvrns-ser.h"
+#include "scheme/ckksrns/ckksrns-ser.h"
 
 using namespace lbcrypto;
 namespace py = pybind11;
 
 template <typename ST>
-bool SerializeEvalMultKeyWrapper(const std::string& filename, const ST& sertype, std::string id)
+bool SerializeEvalMultKeyWrapper(const std::string &filename, const ST &sertype, std::string id)
 {
     std::ofstream outfile(filename, std::ios::out | std::ios::binary);
     bool res;
@@ -47,27 +53,76 @@ bool SerializeEvalMultKeyWrapper(const std::string& filename, const ST& sertype,
     return res;
 }
 
+template <typename ST>
+bool SerializeEvalAutomorphismKeyWrapper(const std::string& filename, const ST& sertype, std::string id)
+{
+    std::ofstream outfile(filename, std::ios::out | std::ios::binary);
+    bool res;
+    res = CryptoContextImpl<DCRTPoly>::SerializeEvalAutomorphismKey<ST>(outfile, sertype, id);
+    outfile.close();
+    return res;
+}
+
+template <typename ST>
+bool DeserializeEvalMultKeyWrapper(const std::string &filename, const ST &sertype)
+{
+    std::ifstream emkeys(filename, std::ios::in | std::ios::binary);
+    if (!emkeys.is_open())
+    {
+        std::cerr << "I cannot read serialization from " << filename << std::endl;
+    }
+    bool res;
+    res = CryptoContextImpl<DCRTPoly>::DeserializeEvalMultKey<ST>(emkeys, sertype);
+    return res; }
+
+template <typename T, typename ST>
+std::tuple<T, bool> DeserializeFromFileWrapper(const std::string& filename, const ST& sertype) {
+    T newob;
+    bool result = Serial::DeserializeFromFile<T>(filename, newob, sertype);
+    return std::make_tuple(newob, result);
+}
+template <typename ST>
+std::tuple<CryptoContext<DCRTPoly>, bool> DeserializeCCWrapper(const std::string& filename, const ST& sertype) {
+    CryptoContext<DCRTPoly> newob;
+    bool result = Serial::DeserializeFromFile<DCRTPoly>(filename, newob, sertype);
+    return std::make_tuple(newob, result);
+}
+
 void bind_serialization(pybind11::module &m) {
     // Json Serialization
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const CryptoContext<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, CryptoContext<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PublicKey<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PublicKey<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PrivateKey<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PrivateKey<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const Ciphertext<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, Ciphertext<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string &, const CryptoContext<DCRTPoly> &, const SerType::SERJSON &)>(&Serial::SerializeToFile<DCRTPoly>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeCryptoContext", static_cast<std::tuple<CryptoContext<DCRTPoly>, bool> (*)(const std::string &, const SerType::SERJSON &)>(&DeserializeCCWrapper<SerType::SERJSON>),
+          py::arg("filename"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string &, const PublicKey<DCRTPoly> &, const SerType::SERJSON &)>(&Serial::SerializeToFile<PublicKey<DCRTPoly>>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializePublicKey", static_cast<std::tuple<PublicKey<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERJSON&)>(&DeserializeFromFileWrapper<PublicKey<DCRTPoly>, SerType::SERJSON>),
+          py::arg("filename"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PrivateKey<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<PrivateKey<DCRTPoly>>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializePrivateKey", static_cast<std::tuple<PrivateKey<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERJSON&)>(&DeserializeFromFileWrapper<PrivateKey<DCRTPoly>, SerType::SERJSON>),
+            py::arg("filename"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const Ciphertext<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<Ciphertext<DCRTPoly>>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeCiphertext", static_cast<std::tuple<Ciphertext<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERJSON&)>(&DeserializeFromFileWrapper<Ciphertext<DCRTPoly>, SerType::SERJSON>),
+          py::arg("filename"), py::arg("sertype"));
     // Binary Serialization
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const CryptoContext<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, CryptoContext<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PublicKey<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PublicKey<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PrivateKey<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PrivateKey<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const Ciphertext<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, Ciphertext<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
-    
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&,const CryptoContext<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<DCRTPoly>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeCryptoContext", static_cast<std::tuple<CryptoContext<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERBINARY&)>(&DeserializeCCWrapper<SerType::SERBINARY>),
+          py::arg("filename"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PublicKey<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<PublicKey<DCRTPoly>>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializePublicKey", static_cast<std::tuple<PublicKey<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERBINARY&)>(&DeserializeFromFileWrapper<PublicKey<DCRTPoly>, SerType::SERBINARY>),
+          py::arg("filename"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PrivateKey<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<PrivateKey<DCRTPoly>>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializePrivateKey", static_cast<std::tuple<PrivateKey<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERBINARY&)>(&DeserializeFromFileWrapper<PrivateKey<DCRTPoly>, SerType::SERBINARY>),
+          py::arg("filename"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const Ciphertext<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<Ciphertext<DCRTPoly>>),
+          py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeCiphertext", static_cast<std::tuple<Ciphertext<DCRTPoly>,bool> (*)(const std::string&, const SerType::SERBINARY&)>(&DeserializeFromFileWrapper<Ciphertext<DCRTPoly>, SerType::SERBINARY>),
+            py::arg("filename"), py::arg("sertype"));   
     
 }
 
-

+ 59 - 0
src/pke/examples/test_serial_cc.py

@@ -0,0 +1,59 @@
+# Initial Settings
+from openfhe import *
+# import openfhe.PKESchemeFeature as Feature
+
+datafolder = 'demoData'
+
+print("This program requres the subdirectory `" + datafolder + "' to exist, otherwise you will get an error writing serializations.")
+
+# Sample Program: Step 1: Set CryptoContext
+parameters = CCParamsBFVRNS()
+parameters.SetPlaintextModulus(65537)
+parameters.SetMultiplicativeDepth(2)
+
+cryptoContext = GenCryptoContext(parameters)
+# Enable features that you wish to use
+cryptoContext.Enable(PKESchemeFeature.PKE)
+cryptoContext.Enable(PKESchemeFeature.KEYSWITCH)
+cryptoContext.Enable(PKESchemeFeature.LEVELEDSHE)
+
+keypair = cryptoContext.KeyGen()
+vectorOfInts1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext1 = cryptoContext.MakePackedPlaintext(vectorOfInts1)
+ciphertext1 = cryptoContext.Encrypt(keypair.publicKey, plaintext1)
+
+# Serialize cryptocontext
+if not SerializeToFile(datafolder + "/cryptocontext.json", cryptoContext, JSON):
+   raise Exception("Error writing serialization of the crypto context to cryptocontext.json")
+print("The cryptocontext has been serialized.")
+# Serialize Ciphertext
+if not SerializeToFile(datafolder + "/ciphertext1.json", ciphertext1, JSON):
+   raise Exception("Error writing serialization of the ciphertext to ciphertext1.json")
+
+cryptoContext.ClearEvalMultKeys()
+cryptoContext.ClearEvalAutomorphismKeys()
+ReleaseAllContexts()
+# Deserialize the crypto context
+#cc = CryptoContext()
+
+res, cc = DeserializeFromFiletuple(datafolder + "/cryptocontext.json", JSON)
+if not res:
+   raise Exception("Error reading serialization of the crypto context from cryptocontext.txt")
+# cc = DeserializeFromFile2(datafolder + "/cryptocontext.json", JSON)
+# print("The cryptocontext has been deserialized.")
+# Serialize cryptocontext again
+if not SerializeToFile(datafolder + "/cryptocontext2.json", cc, JSON):
+   raise Exception("Error writing serialization of the crypto context to cryptocontext.json")
+print("The cryptocontext has been serialized.")
+
+ct1 = Ciphertext()
+# Deserialize the ciphertext
+if not DeserializeFromFile(datafolder + "/ciphertext1.json", ct1, JSON):
+   raise Exception("Error reading serialization of the ciphertext from ciphertext1.txt")
+
+# Serialize the ciphertext again
+if not SerializeToFile(datafolder + "/ciphertext12.json", ct1, JSON):
+   raise Exception("Error writing serialization of the ciphertext to ciphertext2.json")
+
+
+