ibarrond / Pyfhel

PYthon For Homomorphic Encryption Libraries, perform encrypted computations such as sum, mult, scalar product or matrix multiplication in Python, with NumPy compatibility. Uses SEAL/PALISADE as backends, implemented using Cython.
https://pyfhel.readthedocs.io/
Apache License 2.0
489 stars 78 forks source link

How to perform Bitwise Calculation #75

Closed warren-zh closed 3 years ago

warren-zh commented 3 years ago

Hello,

I'm pretty new to this project and FHE. Please correct me if I'm wrong.

I'm trying to use this to perform bitwise calculation. However, I got unexpected result.


from Pyfhel import Pyfhel, PyPtxt, PyCtxt
HE = Pyfhel()
# p=2 creates a GF(2) which should allow me perform Bitwise XOR for addtion, and Bitwise AND for multiplication
HE.contextGen(p=2, m=2048, sec=128)     
HE.keyGen()
a = 97
b = 113
ctxt_a = HE.encryptInt(a)
ctxt_b = HE.encryptInt(b)
ctxt_XOR = ctxt_a + ctxt_b
res_XOR = HE.decryptInt(ctxt_XOR)
print(res_XOR)
ctxt_AND = ctxt_a * ctxt_b
res_AND = HE.decryptInt(ctxt_AND)
print(res_AND)

I'm expecting the result as 16, 97, respectively. However, I got -16, and -4625.

Please Help.

ibarrond commented 3 years ago

Hi @GreyFox-Z , in order to perform bitwise computations there are several things to take into account:

One example of this, for your inputs a and b:

import numpy as np
from Pyfhel import Pyfhel, PyPtxt, PyCtxt
HE = Pyfhel()
# p=2 creates a GF(2) which should allow me perform Bitwise XOR for addtion, and Bitwise AND for multiplication
HE.contextGen(p=2, m=2048, sec=128)     
HE.keyGen()
a = 97
b = 113
# Defining a function to perform bit decomposition
bit_decomposition = lambda x: np.array(list(map(int, bin(x)[2:])))

a_decomp = bit_decomposition(a)
b_decomp = bit_decomposition(b)

n_bits = len(a_decomp)

ctxt_a = np.empty(n_bits, dtype=PyCtxt)
ctxt_b = np.empty(n_bits, dtype=PyCtxt)

# Encrypting! This can be parallelized!
for i in np.arange(n_bits):
    ctxt_a[i] = HE.encryptInt(a_decomp[i])
    ctxt_b[i] = HE.encryptInt(b_decomp[i])

ctxt_XOR = ctxt_a + ctxt_b
ctxt_AND = ctxt_a * ctxt_b

res_OR = [HE.decryptInt(ctxt_XOR[i])%2 for i in np.arange(n_bits)]
res_AND = [HE.decryptInt(ctxt_AND[i])%2 for i in np.arange(n_bits)]

print(f'res_OR: {res_OR},    res_AND: {res_AND}' )

#>res_OR: [0, 0, 0, 0, 1, 0, 0],    res_AND: [1, 0, 0, 0, 0, 1, 1]

def bit_composition(x):
    res = 0
    for bit in x:
        res = (res << 1) | bit
    return res

print(f'res_OR: {bit_composition(res_OR)},    res_AND: {bit_composition(res_AND)}' )
#> res_OR: 16,    res_AND: 97

Since this looks like an interesting enough functionality, I'll add this example to the Demos.

warren-zh commented 3 years ago

Hello @ibarrond

Thank you so much for your clarification and detailed explanation.

It is truly appreciated.