.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "_autoexamples/Demo_9_Integer_BGV.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code .. rst-class:: sphx-glr-example-title .. _sphx_glr__autoexamples_Demo_9_Integer_BGV.py: Integer FHE with BGV scheme ======================================== //TODO .. GENERATED FROM PYTHON SOURCE LINES 8-11 1. Imports --------------------------- We start by importing the library .. GENERATED FROM PYTHON SOURCE LINES 11-18 .. code-block:: Python import numpy as np from Pyfhel import Pyfhel print("\n1. Pyfhel Import") .. rst-class:: sphx-glr-script-out .. code-block:: none 1. Pyfhel Import .. GENERATED FROM PYTHON SOURCE LINES 19-22 2. BGV context and key setup ------------------------------------------------------------------------------ We take a look at the different parameters that can be set for the BGV scheme. .. GENERATED FROM PYTHON SOURCE LINES 22-49 .. code-block:: Python HE = Pyfhel() # Creating empty Pyfhel object # HE.contextGen(scheme='bgv', n=2**14, t_bits=20) # Generate context for 'bfv'/'bgv'/'ckks' scheme bgv_params = { 'scheme': 'BGV', # can also be 'bgv' 'n': 2**13, # Polynomial modulus degree, the num. of slots per plaintext, # of elements to be encoded in a single ciphertext in a # 2 by n/2 rectangular matrix (mind this shape for rotations!) # Typ. 2^D for D in [10, 16] 't': 65537, # Plaintext modulus. Encrypted operations happen modulo t # Must be prime such that t-1 be divisible by 2^N. 't_bits': 20, # Number of bits in t. Used to generate a suitable value # for t. Overrides t if specified. 'sec': 128, # Security parameter. The equivalent length of AES key in bits. # Sets the ciphertext modulus q, can be one of {128, 192, 256} # More means more security but also slower computation. } HE.contextGen(**bgv_params) # Generate context for bgv scheme HE.keyGen() # Key Generation: generates a pair of public/secret keys HE.rotateKeyGen() # Rotate key generation --> Allows rotation/shifting HE.relinKeyGen() # Relinearization key generation print("\n2. Pyfhel FHE context generation") print(f"\t{HE}") .. rst-class:: sphx-glr-script-out .. code-block:: none 2. Pyfhel FHE context generation .. GENERATED FROM PYTHON SOURCE LINES 50-53 3. BGV Integer Encryption --------------------------- we will define two integers and encrypt them using `encryptBGV`: .. GENERATED FROM PYTHON SOURCE LINES 53-60 .. code-block:: Python integer1 = np.array([127], dtype=np.int64) integer2 = np.array([-2], dtype=np.int64) ctxt1 = HE.encryptBGV(integer1) # Encryption makes use of the public key ctxt2 = HE.encryptBGV(integer2) # For BGV, encryptBGV function is used. print("\n3. BGV Encryption, ") print(" int ",integer1,'-> ctxt1 ', type(ctxt1)) print(" int ",integer2,'-> ctxt2 ', type(ctxt2)) .. rst-class:: sphx-glr-script-out .. code-block:: none 3. BGV Encryption, int [127] -> ctxt1 int [-2] -> ctxt2 .. GENERATED FROM PYTHON SOURCE LINES 61-62 # The best way to obtain information from a ciphertext is to print it: .. GENERATED FROM PYTHON SOURCE LINES 62-65 .. code-block:: Python print(ctxt1) print(ctxt2) .. rst-class:: sphx-glr-script-out .. code-block:: none .. GENERATED FROM PYTHON SOURCE LINES 66-70 4. Operating with encrypted integers in BGV --------------------------------------------- Relying on the context defined before, we will now operate (addition, substaction, multiplication) the two ciphertexts: .. GENERATED FROM PYTHON SOURCE LINES 70-78 .. code-block:: Python ctxtSum = ctxt1 + ctxt2 # `ctxt1 += ctxt2` for inplace operation ctxtSub = ctxt1 - ctxt2 # `ctxt1 -= ctxt2` for inplace operation ctxtMul = ctxt1 * ctxt2 # `ctxt1 *= ctxt2` for inplace operation print("\n4. Operating with encrypted integers") print(f"Sum: {ctxtSum}") print(f"Sub: {ctxtSub}") print(f"Mult:{ctxtMul}") .. rst-class:: sphx-glr-script-out .. code-block:: none 4. Operating with encrypted integers Sum: Sub: Mult: .. GENERATED FROM PYTHON SOURCE LINES 79-83 5. Decrypting BGV integers ------------------------------ Once we're finished with the encrypted operations, we can use the Pyfhel instance to decrypt the results using `decryptBGV`: .. GENERATED FROM PYTHON SOURCE LINES 83-94 .. code-block:: Python resSum = HE.decryptBGV(ctxtSum) # Decryption must use the corresponding function # decryptBGV. resSub = HE.decrypt(ctxtSub) # `decrypt` function detects the scheme and # calls the corresponding decryption function. resMul = HE.decryptBGV(ctxtMul) print("\n5. Decrypting result:") print(" addition: decrypt(ctxt1 + ctxt2) = ", resSum) print(" substraction: decrypt(ctxt1 - ctxt2) = ", resSub) print(" multiplication: decrypt(ctxt1 + ctxt2) = ", resMul) .. rst-class:: sphx-glr-script-out .. code-block:: none 5. Decrypting result: addition: decrypt(ctxt1 + ctxt2) = [125 0 0 ... 0 0 0] substraction: decrypt(ctxt1 - ctxt2) = [129 0 0 ... 0 0 0] multiplication: decrypt(ctxt1 + ctxt2) = [-254 0 0 ... 0 0 0] .. GENERATED FROM PYTHON SOURCE LINES 95-100 6. Integer Array Encoding & Encryption ------------------------------------------------------------------------------ we will define two 1D integer arrays, encode and encrypt them: arr1 = [0, 1, ... n-1] (length n) arr2 = [-t//2, -1, 1] (length 3) --> Encoding fills the rest of the array with zeros .. GENERATED FROM PYTHON SOURCE LINES 100-123 .. code-block:: Python arr1 = np.arange(bgv_params['n'], dtype=np.int64) # Max possible value is t/2-1. Always use type int64! arr2 = np.array([-bgv_params['t']//2, -1, 1], dtype=np.int64) # Min possible value is -t/2. ptxt1 = HE.encodeBGV(arr1) # Creates a PyPtxt plaintext with the encoded arr1 # plaintexts created from arrays shorter than 'n' are filled with zeros. ptxt2 = HE.encode(arr2) # `encode` function detects the scheme and calls the # corresponding encoding function. assert np.allclose(HE.decodeBGV(ptxt1), arr1) # Decoding the encoded array should return the original array assert np.allclose(HE.decode(ptxt2)[:3], arr2) # `decode` function detects the scheme ctxt1 = HE.encryptPtxt(ptxt1) # Encrypts the plaintext ptxt1 and returns a PyCtxt ctxt2 = HE.encryptPtxt(ptxt2) # Alternatively you can use HE.encryptInt(arr2) # Otherwise, a single call to `HE.encrypt` would detect the data type, # encode it and encrypt it #> ctxt1 = HE.encrypt(arr1) print("\n6. Integer Array Encoding & Encryption, ") print("->\tarr1 ", arr1,'\n\t==> ptxt1 ', ptxt1,'\n\t==> ctxt1 ', ctxt1) print("->\tarr2 ", arr2,'\n\t==> ptxt2 ', ptxt2,'\n\t==> ctxt2 ', ctxt2) .. rst-class:: sphx-glr-script-out .. code-block:: none 6. Integer Array Encoding & Encryption, -> arr1 [ 0 1 2 ... 8189 8190 8191] ==> ptxt1 ==> ctxt1 -> arr2 [-32769 -1 1] ==> ptxt2 ==> ctxt2 .. GENERATED FROM PYTHON SOURCE LINES 124-129 7. Securely operating on encrypted ingeger arrays ------------------------------------------------------------------------------ We try all the operations supported by Pyfhel. Note that, to operate, the ciphertexts/plaintexts must be built with the same context. Internal checks prevent ops between ciphertexts of different contexts. .. GENERATED FROM PYTHON SOURCE LINES 129-177 .. code-block:: Python # Ciphertext-ciphertext ops: ccSum = ctxt1 + ctxt2 # Calls HE.add(ctxt1, ctxt2, in_new_ctxt=True) # `ctxt1 += ctxt2` for inplace operation ccSub = ctxt1 - ctxt2 # Calls HE.sub(ctxt1, ctxt2, in_new_ctxt=True) # `ctxt1 -= ctxt2` for inplace operation ccMul = ctxt1 * ctxt2 # Calls HE.multiply(ctxt1, ctxt2, in_new_ctxt=True) # `ctxt1 *= ctxt2` for inplace operation cSq = ctxt1**2 # Calls HE.square(ctxt1, in_new_ctxt=True) # `ctxt1 **= 2` for inplace operation cNeg = -ctxt1 # Calls HE.negate(ctxt1, in_new_ctxt=True) # cPow = ctxt1**3 # Calls HE.power(ctxt1, 3, in_new_ctxt=True) # `ctxt1 **= 3` for inplace operation cRotR = ctxt1 >> 2 # Calls HE.rotate(ctxt1, k=2, in_new_ctxt=True) # `ctxt1 >>= 2` for inplace operation # WARNING! the encoded data is placed in a n//2 by 2 # matrix. Hence, these rotations apply independently # to each of the rows! cRotL = ctxt1 << 2 # Calls HE.rotate(ctxt1, k=-2, in_new_ctxt=True) # `ctxt1 <<= 2` for inplace operation # Ciphetext-plaintext ops cpSum = ctxt1 + ptxt2 # Calls HE.add_plain(ctxt1, ptxt2, in_new_ctxt=True) # `ctxt1 += ctxt2` for inplace operation cpSub = ctxt1 - ptxt2 # Calls HE.sub_plain(ctxt1, ptxt2, in_new_ctxt=True) # `ctxt1 -= ctxt2` for inplace operation cpMul = ctxt1 * ptxt2 # Calls HE.multiply_plain(ctxt1, ptxt2, in_new_ctxt=True) # `ctxt1 *= ctxt2` for inplace operation print("\n7. Secure operations") print(" Ciphertext-ciphertext: ") print("->\tctxt1 + ctxt2 = ccSum: ", ccSum) print("->\tctxt1 - ctxt2 = ccSub: ", ccSub) print("->\tctxt1 * ctxt2 = ccMul: ", ccMul) print(" Single ciphertext: ") print("->\tctxt1**2 = cSq : ", cSq ) print("->\t- ctxt1 = cNeg : ", cNeg ) print("->\tctxt1**3 = cPow : ", cPow ) print("->\tctxt1 >> 2 = cRotR: ", cRotR) print("->\tctxt1 << 2 = cRotL: ", cRotL) print(" Ciphertext-plaintext: ") print("->\tctxt1 + ptxt2 = cpSum: ", cpSum) print("->\tctxt1 - ptxt2 = cpSub: ", cpSub) print("->\tctxt1 * ptxt2 = cpMul: ", cpMul) .. rst-class:: sphx-glr-script-out .. code-block:: none 7. Secure operations Ciphertext-ciphertext: -> ctxt1 + ctxt2 = ccSum: -> ctxt1 - ctxt2 = ccSub: -> ctxt1 * ctxt2 = ccMul: Single ciphertext: -> ctxt1**2 = cSq : -> - ctxt1 = cNeg : -> ctxt1**3 = cPow : -> ctxt1 >> 2 = cRotR: -> ctxt1 << 2 = cRotL: Ciphertext-plaintext: -> ctxt1 + ptxt2 = cpSum: -> ctxt1 - ptxt2 = cpSub: -> ctxt1 * ptxt2 = cpMul: .. GENERATED FROM PYTHON SOURCE LINES 178-190 8. BGV Relinearization: What, why, when ------------------------------------------------------------------------------ Ciphertext-ciphertext multiplications increase the size of the polynoms representing the resulting ciphertext. To prevent this growth, the relinearization technique is used (typically right after each c-c mult) to reduce the size of a ciphertext back to the minimal size (two polynoms c0 & c1). For this, a special type of public key called Relinearization Key is used. In Pyfhel, you can either generate a relin key with HE.RelinKeyGen() or skip it and call HE.relinearize() directly, in which case a warning is issued. Note that HE.power performs relinearization after every multiplication. .. GENERATED FROM PYTHON SOURCE LINES 190-197 .. code-block:: Python print("\n8. Relinearization-> Right after each multiplication.") print(f"ccMul before relinearization (size {ccMul.size()}): {ccMul}") ~ccMul # Equivalent to HE.relinearize(ccMul). Relin always happens in-place. print(f"ccMul after relinearization (size {ccMul.size()}): {ccMul}") print(f"cPow after 2 mult&relin rounds: (size {cPow.size()}): {cPow}") .. rst-class:: sphx-glr-script-out .. code-block:: none 8. Relinearization-> Right after each multiplication. ccMul before relinearization (size 3): ccMul after relinearization (size 2): cPow after 2 mult&relin rounds: (size 2): .. GENERATED FROM PYTHON SOURCE LINES 198-203 9. Decrypt & Decode results ------------------------------------------------------------------------------ Time to decrypt results! We use HE.decryptBGV for this. HE.decrypt() could also be used, in which case the decryption type would be inferred from the ciphertext metadata. .. GENERATED FROM PYTHON SOURCE LINES 203-237 .. code-block:: Python r1 = HE.decryptBGV(ctxt1) r2 = HE.decryptBGV(ctxt2) rccSum = HE.decryptBGV(ccSum) rccSub = HE.decryptBGV(ccSub) rccMul = HE.decryptBGV(ccMul) rcSq = HE.decryptBGV(cSq ) rcNeg = HE.decryptBGV(cNeg ) rcPow = HE.decryptBGV(cPow ) rcRotR = HE.decryptBGV(cRotR) rcRotL = HE.decryptBGV(cRotL) rcpSum = HE.decryptBGV(cpSum) rcpSub = HE.decryptBGV(cpSub) rcpMul = HE.decryptBGV(cpMul) print("\n9. Decrypting results") print(" Original ciphertexts: ") print(" ->\tctxt1 --(decr)--> ", r1) print(" ->\tctxt2 --(decr)--> ", r2) print(" Ciphertext-ciphertext Ops: ") print(" ->\tctxt1 + ctxt2 = ccSum --(decr)--> ", rccSum) print(" ->\tctxt1 - ctxt2 = ccSub --(decr)--> ", rccSub) print(" ->\tctxt1 * ctxt2 = ccMul --(decr)--> ", rccMul) print(" Single ciphertext: ") print(" ->\tctxt1**2 = cSq --(decr)--> ", rcSq ) print(" ->\t- ctxt1 = cNeg --(decr)--> ", rcNeg ) print(" ->\tctxt1**3 = cPow --(decr)--> ", rcPow ) print(" ->\tctxt1 >> 2 = cRotR --(decr)--> ", rcRotR) print(" ->\tctxt1 << 2 = cRotL --(decr)--> ", rcRotL) print(" Ciphertext-plaintext ops: ") print(" ->\tctxt1 + ptxt2 = cpSum --(decr)--> ", rcpSum) print(" ->\tctxt1 - ptxt2 = cpSub --(decr)--> ", rcpSub) print(" ->\tctxt1 * ptxt2 = cpMul --(decr)--> ", rcpMul) # %% .. rst-class:: sphx-glr-script-out .. code-block:: none 9. Decrypting results Original ciphertexts: -> ctxt1 --(decr)--> [ 0 1 2 ... 8189 8190 8191] -> ctxt2 --(decr)--> [-32769 -1 1 ... 0 0 0] Ciphertext-ciphertext Ops: -> ctxt1 + ctxt2 = ccSum --(decr)--> [-32769 0 3 ... 8189 8190 8191] -> ctxt1 - ctxt2 = ccSub --(decr)--> [32769 2 1 ... 8189 8190 8191] -> ctxt1 * ctxt2 = ccMul --(decr)--> [ 0 -1 2 ... 0 0 0] Single ciphertext: -> ctxt1**2 = cSq --(decr)--> [ 0 1 4 ... -32824 -16445 -64] -> - ctxt1 = cNeg --(decr)--> [ 0 -1 -2 ... -8189 -8190 -8191] -> ctxt1**3 = cPow --(decr)--> [ 0 1 8 ... -425556 -499460 507969] -> ctxt1 >> 2 = cRotR --(decr)--> [4094 4095 0 ... 8187 8188 8189] -> ctxt1 << 2 = cRotL --(decr)--> [ 2 3 4 ... 8191 4096 4097] Ciphertext-plaintext ops: -> ctxt1 + ptxt2 = cpSum --(decr)--> [-32769 0 3 ... 8189 8190 8191] -> ctxt1 - ptxt2 = cpSub --(decr)--> [32769 2 1 ... 8189 8190 8191] -> ctxt1 * ptxt2 = cpMul --(decr)--> [ 0 -1 2 ... 0 0 0] .. rst-class:: sphx-glr-timing **Total running time of the script:** (0 minutes 6.620 seconds) **Estimated memory usage:** 10 MB .. _sphx_glr_download__autoexamples_Demo_9_Integer_BGV.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: Demo_9_Integer_BGV.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: Demo_9_Integer_BGV.py ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_