** Pyfhel ** (** Py ** thon ** F ** or ** H ** omomorphic ** E ** encryption ** L ** ibraries) is a cheat sheet for a fully homomorphic encryption library.
With Pyfhel, it is possible to ** operate encrypted data as it is without decrypting it **.
--Document - Welcome to Pyfhel’s documentation! — Pyfhel 18/11/2018 documentation
pip3 install pyfhel
from Pyfhel import Pyfhel, PyCtxt, PyPtxt
Name | Memo |
---|---|
PyPtxt | Object for storing plain text data |
PyCtxt | Object for storing ciphertext |
Pyfhel | Functions for PyCtxt[^func]Objects that include |
[^ func]: Perform encryption, decryption, and arithmetic operations, etc.
Python version etc. are as follows
Python (3.4+) & Cython on top of C++17. (REQUIRED: Python must have been compiled with C++17: g++>=6 | clang++>=5.0, Visual Studio 2017.).
Please note that the installation takes a long time. [Google Colaboratory](https://www.google.com/url?sa=t&rct=j&q=1esrc=s&source=web&cd=1&cad=rja&uact=8&ved=2ahUKEwiGxd2wwvDlAhVYIIgKHU9jCSYQFjAAegQIAxAB&url=https%FjAAegQIAxAB&url=https % 2Fnotebooks% 2Fwelcome.ipynb% 3Fhl% 3Dja & usg = AOvVaw0qrT1c7i_vNFAUuRBp9c6M) takes about 5 minutes.
he = Pyfhel()
he.contextGen(p=65537, m=4096)
he.keyGen()
p: Plaintext modulus. Huge prime number, all processing is done with mod p
m: Coefficient modulus `m = pow (2,12)`
etc. should be specified
he.saveContext('context.txt') #Save context to file
he.savepublicKey('pub.key') #Save public key to file
he.savesecretKey('secret.key') #Save private key to file
he.restoreContext('context.txt') #Read context from file
he.restorepublicKey('pub.key') #Read public key from file
he.restoresecretKey('secret.key') #Read the private key from a file
--I'm not sure about the context, but it's probably information about the p and m parameters. --Restore Context and public Key is enough for encryption only --For decryption, it is enough to restore only Context and secretKey.
#int type encryption / decryption
enc_13 = he.encryptInt(13)
dec_13 = he.decryptInt(enc_13)
#Float type encryption / decryption
enc_11dot13 = he.encryptFrac(11.13)
dec_11dot13 = he.decryptFrac(enc_11dot13)
#array type encryption / decryption (when each element is int type)
he = Pyfhel()
he.contextGen(p=65537, m=4096, flagBatching=True) #Set flagBatching to True
enc_1113 = he.encryptBatch([1,1,1,3])
dec_1113 = he.decryptBatch(enc_1113)
print(enc_1113, dec_1113[len(enc_1113), len(dec_1113))
#Array type encryption / decryption (when each element is float type)
enc = [he.encryptFrac(d) for d in [0.1, 0.1, 0.1, 0.3]]
dec = [he.decryptFrac(d) for d in enc]
The data output by array type decoding is an m-dimensional vector. Therefore, if you want to retrieve the same data as the original input data, follow the steps below.
l = [1,1,1,3]
enc_1113 = he.encryptBatch(l)
dec_1113 = he.decryptBatch(enc_1113)
print(enc_1113, dec_1113[:len(l)],len(dec_1113))
Also, note that float type encryption / decryption contains an error in the output result.
enc = [he.encrypt(d) for d in [0.1, 0.1, 0.1, 0.3]]
#[<Pyfhel.PyCtxt.PyCtxt object at 0x7fb7e17e6870>, <Pyfhel.PyCtxt.PyCtxt object at 0x7fb7e04d7ca8>, <Pyfhel.PyCtxt.PyCtxt object at 0x7fb7e043f8b8>, <Pyfhel.PyCtxt.PyCtxt object at 0x7fb7e9082b40>]
dec = [he.decryptFrac(d) for d in enc]
#[0.10000000149011612, 0.10000000149011612, 0.10000000149011612, 0.30000001192092896]
Data encrypted with the same public key can be calculated as it is encrypted.
enc1 = he.encryptInt(11)
enc2 = he.encryptInt(13)
#As it is
print(enc1+enc2) # 24
print(enc1-enc2) # -2
print(enc1*enc2) # 143
#When using methods of the Pyfhel class
print(he.add(enc1, enc2, in_new_ctxt=True)) #24
print(he.sub(enc1, enc2, in_new_ctxt=True)) # -2
print(he.multiply(enc1, enc2, in_new_ctxt=True)) # 143
#Square number
print(he.decryptInt(he.square(enc1, True))) # 121
#Exponentiation
he.relinKeyGen(30, 5)
print(he.decryptInt(he.power(enc1, 2, True))) #121
#Positive / negative reversal
print(he.decryptInt(he.negate(enc1, in_new_ctxt=True))) # -11
Note that if in_new_ctxt = False, the value of enc1 will be updated as ʻenc1 = enc1 + enc2`.
print(he.getp()) #value of p
print(he.getm()) #value of m
print(he.getflagBatch()) #Whether Batch mode is enabled
print(he.getbase()) #Cardinal number used for encryption
print(he.getnSlots()) #Length of output data in Batch mode
print(he.getintDigits()) #int type spatial range?64 for int64
print(he.getfracDigits()) #Float type spatial range?
print(he.getsec()) #Equivalent AES encryption strength
I'm not sure about getintDigigts
enc1 = he.encryptInt(1)
print(he.noiseLevel(enc1)) # 82
If you calculate with the ciphertext as it is, noise will accumulate, and when the noise level becomes 0, the correct value will not be calculated.
%time
import pandas as pd
he = Pyfhel() #Create an empty Pyfhel object
he.contextGen(p=65537, m=2**13, flagBatching=True) #Initialize context m=8192
he.keyGen() #Generate key
enc1, enc2 = he.encryptInt(1), he.encryptInt(2)
enc3, enc4 = he.encryptInt(1), he.encryptInt(2)
plain1, plain2 = 1, 2
plain3, plain4 = 1, 2
df_add = pd.DataFrame(index=[],columns=['noiseLevel(add_Int)', 'result(add_Int)', 'grand truth(add_Int)'])
df_mul = pd.DataFrame(index=[],columns=['noiseLevel(mul_Int)', 'result(mul_Int)', 'grand truth(mul_Int)'])
for i in range(10):
he.add(enc1, enc2) #Addition
he.multiply(enc3, enc4) #Multiply
plain1 = plain1 + plain2
plain3 = plain3 * plain4
s1 = pd.Series([he.noiseLevel(enc1), he.decryptInt(enc1), plain1], index=df_add.columns)
s2 = pd.Series([he.noiseLevel(enc3), he.decryptInt(enc3), plain3], index=df_mul.columns)
df_add = df_add.append(s1, ignore_index=True )
df_mul = df_mul.append(s2, ignore_index=True )
display(df_add)
display(df_add.plot(subplots=True, figsize=(9,9)))
display(df_mul)
display(df_mul.plot(subplots=True, figsize=(9,9)))
Table | Graph |
---|---|
Noise accumulation by Int type addition![]() |
Visualization of the table on the left![]() |
Noise accumulation by Int type multiplication![]() |
Visualization of the table on the left![]() |
In this way, the noise level of multiplication is significantly lower than that of addition, and the calculation result is greatly deviated.
The following two measures can be mentioned.
--Decryption → re-encryption once before the noise level becomes 0
--Increase the value of parameter m
(the larger it is, the less noise is accumulated, but the longer the calculation time is).
By the way, even if you encrypt with floating point, you can get almost the same result.
enc_df = pd.DataFrame(data=[],columns=df.columns)
for colname in df:
enc_df[colname] = df[colname].apply(lambda x: he.encryptFrac(x))
Pandas arithmetic methods can also be executed encrypted
#Calculate with the average value encrypted
meanlist = []
for colname in enc_df:
meanlist.append(enc_df[colname].sum()*he.encryptFrac(1/len(df[colname])))
print(df.mean())
print([he.decryptFrac(x) for x in meanlist])
Output result
sepal length (cm) 5.843333
sepal width (cm) 3.057333
petal length (cm) 3.758000
petal width (cm) 1.199333
dtype: float64
[5.843333087628707, 3.0573331359773874, 3.757999788969755, 1.1993331399280578]
The notebook for this article has been uploaded to GitHub.
--Cryptography -Future cryptography that supports the cloud -[Secret calculation system and its principle](https://www.google.com/url?sa=t&rct=j&q=1esrc=s&source=web&cd=5&cad=rja&uact=8&ved=2ahUKEwjB9u_EwOHlAhV4y4sBHbDWBHYQFjAEegQIBxAC&url= .ntt.co.jp% 2Fsc% 2Fproject% 2Fdata-security% 2FNTT-himitsu-keisan.pdf & usg = AOvVaw1fsm4DXRITkLZ6v5NjOn1o) --Sample implementation - HE-Hackathon / he-hackathon-2019 · GitLab