def binop_test(self, binop): for i in range(ITERATIONS): a = self.rand() b = self.rand() self.assertAlmostEqual(decrypt(binop(encrypt(a), encrypt(b))), binop(a, b), places=3)
def __init__(self, value): if isinstance(value, EncryptedValue): value = value._ciphertext if not isinstance(value, Ciphertext): value = simplefhe.encrypt(value)._ciphertext self._ciphertext = value self._mode = simplefhe._mode
def test_running_sum(self): true = 0 target = 0 for i in range(ITERATIONS): x = self.rand() true += x target += encrypt(x) self.assertAlmostEqual(decrypt(target), true, places=3)
def smart_product(values: List[EncryptedValue]) -> EncryptedValue: if not values: return simplefhe.encrypt(1) if len(values) == 1: return values[0] new = [] for i in range(len(values) // 2): new.append(values[2 * i] * values[2 * i + 1]) if len(values) % 2 == 1: new.append(values[-1]) return smart_product(new)
def _binop(self, other, cipher_func, plain_func=None, _is_mult: bool = False): """ Returns the result of a binary operation between self and other. :param cipher_func: Must take three Ciphertext arguments. The function must apply a binary operation to the first two arguments, and overwrite the third argument with the result. :param plain_func: Optional. Used when `other` is an unencrypted value for performance improvement. If omitted, `other` will be encrypted and passed into `cipher_func`. """ result = Ciphertext() if isinstance(other, EncryptedValue): other = other._ciphertext # Must normalize floats to same scale before adding/subtracting evaluator = simplefhe._evaluator # If adding, we need to match scale and modulus if self._is_float: target_scale = simplefhe._mode['default_scale'] if not _is_mult: self._ciphertext.scale(target_scale) parms = self._ciphertext.parms_id() def normalize(x, p=parms): simplefhe._evaluator.mod_switch_to_inplace(x, p) # After each multiplication, we should relinearize def renormalize(x): if _is_mult: evaluator.relinearize_inplace(x, simplefhe._relin_keys) if self._is_float: evaluator.rescale_to_next_inplace(x) # Determine type of other operand if not isinstance(other, Ciphertext): from simplefhe.encryptors import encode_item if plain_func is not None: # Use plain_func for performance pt = encode_item(other) if self._is_float: normalize(pt) plain_func(self._ciphertext, pt, result) renormalize(result) return EncryptedValue(result) else: # Fallback to encrypting and using cipher_func other = simplefhe.encrypt(other)._ciphertext # If adding, we need to match scale and modulus #if self._is_float and not _is_mult: if self._is_float: other.scale(target_scale) try: normalize(other) except: self._ciphertext.scale(target_scale) normalize(self._ciphertext, p=other.parms_id()) # Compute binary operation cipher_func(self._ciphertext, other, result) renormalize(result) return EncryptedValue(result)
generate_keypair, set_public_key, set_private_key, set_relin_keys, display_config ) # In a real application, the keypair would be generated once, # and only the public key would be provided to the server. # A more realistic example is given later. display_config() public_key, private_key, relin_keys = generate_keypair() set_public_key(public_key) set_relin_keys(relin_keys) display_config() set_private_key(private_key) display_config() # The server def process(x): return x**3 - 3*x + 1 # The client sensitive_data = [-30, -5, 17, 28] for entry in sensitive_data: encrypted = encrypt(entry) # Encrypt the data... result = process(encrypted) # Process the encrypted data on the server... print(entry, decrypt(result)) # Decrypt the result on the client.
def test_pow(self): for i in range(ITERATIONS): a = self.rand() / 500 b = random.randint(0, 4) self.assertAlmostEqual(decrypt(encrypt(a)**b), a**b, places=3)
def test_div(self): for i in range(ITERATIONS): a = self.rand() b = (2 * random.randint(0, 1) - 1) * random.uniform(1, 100) self.assertAlmostEqual(decrypt(encrypt(a) / b), a / b, places=3)
def test_pow(self): for i in range(ITERATIONS): a = random.randint(-7, 7) b = random.randint(0, 6) self.assertEqual(decrypt(encrypt(a)**b), a**b)
def test_pow_error(self): a = lambda: encrypt(3)**-1 self.assertRaises(TypeError, a)
def binop_test(self, binop): for i in range(ITERATIONS): a = self.randint() b = self.randint() self.assertEqual(decrypt(binop(encrypt(a), encrypt(b))), binop(a, b))
from pathlib import Path from simplefhe import encrypt, load_public_key, load_relin_keys, display_config load_public_key('keys/public.key') load_relin_keys('keys/relin.key') display_config() # Encrypt our data (client-side) sensitive_data = [-30, -5, 17, 28] Path('inputs').mkdir(exist_ok=True) for i, entry in enumerate(sensitive_data): encrypted = encrypt(entry) encrypted.save(f'inputs/{i}.dat') print(f'[CLIENT] Input {entry} encrypted to inputs/{i}.dat') # We may then safely send these files to the server # over a (possibly insecure) network connection
from simplefhe import (encrypt, decrypt, generate_keypair, set_public_key, set_private_key, set_relin_keys, initialize, display_config) initialize('int') public_key, private_key, relin_key = generate_keypair() set_private_key(private_key) set_public_key(public_key) set_relin_keys(relin_key) display_config() # The server def process(x): return x**21 # The client sensitive_data = [-3, 1, 3, 10] for entry in sensitive_data: insecure_result = process(entry) secure_result = decrypt(process(encrypt(entry))) print(entry, insecure_result, secure_result)
from simplefhe import initialize, encrypt, load_public_key, load_relin_keys # Initialization and keys initialize('float') load_public_key('keys/public.key') load_relin_keys('keys/relin.key') Path('inputs').mkdir(exist_ok=True) # We generate example datapoints according to a linear model: # y = 3.2 x1 - 1.7 x2 + 0.8 x3 + noise COEFFICIENTS = np.array([3.2, -1.7, 0.8]) def generate_point(): xs = np.random.normal(size=3, scale=10) noise = np.random.normal(scale=0.2) y = np.inner(xs, COEFFICIENTS) + noise return (xs, y) # Generate and save encrypted datapoints N_DATAPOINTS = 50 for i in range(N_DATAPOINTS): print(f'Generating datapoint {i+1} of {N_DATAPOINTS}') xs, y = generate_point() encrypt(y).save(f'inputs/y-{i}.dat') for j, x in enumerate(xs): encrypt(x).save(f'inputs/x{j}-{i}.dat')
def test_repr(self): a = encrypt(3) self.assertEqual(repr(a), '<encrypted int>')