[PYTHON] Secret calculation beginning 2019

** 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 **.

About Pyfhel

--Document - Welcome to Pyfhel’s documentation! — Pyfhel 18/11/2018 documentation

Installation

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.

Key generation

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

Writing / reading keys

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.

Encryption / decryption

#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] 

Arithmetic operations

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`.

Check various settings

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

Checking the noise level

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 additionScreenshot 2019-11-11 13.16.55.png Visualization of the table on the leftadd1.png
Noise accumulation by Int type multiplicationScreenshot 2019-11-11 13.16.30.png Visualization of the table on the leftmul1.png

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.

Pandas data frame encryption

enc_df = pd.DataFrame(data=[],columns=df.columns)
for colname in df:
    enc_df[colname] = df[colname].apply(lambda x: he.encryptFrac(x))
スクリーンショット 2019-11-17 14.17.11.png

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]

reference

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

Recommended Posts

Secret calculation beginning 2019
systemd-nspawn beginning
1st KPMG Japan Secret Calculation Hackathon Report