Browse Source

Threshold FHE Support (#61)

* Threshold FHE - BGVrnsAdditive

* EvalKeyMap Prototypes

* Threshold FHE - BGVrns/CKKS + 5 parties prototype

* Threshold-FHE final working capabilities

* PR review response
Rener Oliveira 1 year ago
parent
commit
08c7f7b403

+ 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
+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

+ 124 - 0
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
 
@@ -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

+ 92 - 7
src/lib/bindings.cpp

@@ -69,7 +69,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)
@@ -104,6 +104,10 @@ void bind_crypto_context(py::module &m)
         //.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("GetModulus", &CryptoContextImpl<DCRTPoly>::GetModulus, cc_GetModulus_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)
@@ -185,6 +189,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 +433,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 +458,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 +522,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}),
@@ -479,7 +546,6 @@ void bind_crypto_context(py::module &m)
              py::arg("ciphertext"),
              py::arg("numIterations") = 1,
              py::arg("precision") = 0)
-        //TODO (Oliveira, R.): Solve pointer handling bug when returning EvalKeyMap objects for the next functions
         .def("EvalAutomorphismKeyGen", &EvalAutomorphismKeyGenWrapper, 
             cc_EvalAutomorphismKeyGen_docs,
             py::arg("privateKey"),
@@ -501,10 +567,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 = "")
             {
@@ -712,19 +789,27 @@ 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<>());
 }
 

+ 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();
+}