def set_parms(self, parms: EncryptionParameters, print_parms=False): super().set_parms(parms, print_parms) keygen = KeyGenerator(self._context) self.public_key = keygen.public_key() self.secret_key = keygen.secret_key() self._encryptor = Encryptor(self._context, self._public_key) self._decryptor = Decryptor(self._context, self._secret_key)
def initialize(self, use_old_keys=False): # Initialize encryption params self.init_encrypt_params() # Check if the public & private key files exist if os.path.isfile(defs.FN_FHE_PUBLIC_KEY) and \ os.path.isfile(defs.FN_FHE_PRIVATE_KEY) and \ use_old_keys == True: self.log("Keys already exist. Reusing them instead.") if not self.load_keys(): self.log("Failed to load keys") return False else: # If not, then attempt to generate new ones if not self.generate_keys(): self.log("Failed to generate keys") return False # Setup the rest of the crypto engine self.encryptor = Encryptor(self.context, self.public_key) self.evaluator = Evaluator(self.context) self.decryptor = Decryptor(self.context, self.private_key) # Set the initialized flag self.initialized = True return True
def __init__(self, context): """ Class providing decryption operation :param context: Context initialising HE parameters """ self.context = context.context self.secret_key = context.secret_key self.decryptor = Decryptor(self.context, self.secret_key) self.encoder = FractionalEncoder(self.context.plain_modulus(), self.context.poly_modulus(), 64, 32, 3) self.evaluator = context.evaluator
def test2(): img_path = '00001.jpg' img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) print("Original image dimensions {}".format(img.shape)) sift = cv2.xfeatures2d.SIFT_create() (kps, desc) = sift.detectAndCompute(img, None) desc = preprocessing.normalize(np.array( desc.flatten()[:DESC_SIZE]).reshape(1, DESC_SIZE), norm='l2') descriptor_vec1 = desc descriptor_vec2 = descriptor_vec1 context = config() public_key, secret_key = keygen(context) encoder = IntegerEncoder(context.plain_modulus()) encryptor = Encryptor(context, public_key) crtbuilder = PolyCRTBuilder(context) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) slot_count = (int)(crtbuilder.slot_count()) print("slot count {}".format(slot_count)) print("Plaintext shape", descriptor_vec1.shape) plain_matrix = decompose_plain(slot_count, descriptor_vec1, crtbuilder) for i in range(10000): encrypted_matrix = Ciphertext() print("Encrypting: ") time_start = time.time() encryptor.encrypt(plain_matrix, encrypted_matrix) time_end = time.time() print("Done in time {}".format((str)(1000 * (time_end - time_start)))) print("Square:") time_start = time.time() evaluator.square(encrypted_matrix) time_end = time.time() print("Square is done in {} miliseconds".format( (str)(1000 * (time_end - time_start)))) plain_result = Plaintext() print("Decryption plain: ") time_start = time.time() decryptor.decrypt(encrypted_matrix, plain_result) time_end = time.time() print("Decryption is done in {} miliseconds".format( (str)(1000 * (time_end - time_start)))) # print("Plaintext polynomial: {}".format(plain_result.to_string())) # print("Decoded integer: {}".format(encoder.decode_int32(plain_result))) print("Noise budget {} bits".format( decryptor.invariant_noise_budget(encrypted_matrix)))
def example_rotation_ckks(): print("Example: Rotation / Rotation in CKKS"); #Rotations in the CKKS scheme work very similarly to rotations in BFV. parms = EncryptionParameters(scheme_type.CKKS) poly_modulus_degree = 8192 parms.set_poly_modulus_degree(poly_modulus_degree) parms.set_coeff_modulus(CoeffModulus.Create( poly_modulus_degree, IntVector([40, 40, 40, 40, 40]))) context = SEALContext.Create(parms) print_parameters(context) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() relin_keys = keygen.relin_keys() gal_keys = keygen.galois_keys() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) ckks_encoder = CKKSEncoder(context) slot_count = ckks_encoder.slot_count() print("Number of slots: {}".format(slot_count)) step_size = 1.0 / (slot_count - 1) input = DoubleVector(list(map(lambda x: x*step_size, range(0, slot_count)))) print_vector(input) scale = 2.0**50 print("Encode and encrypt.") plain = Plaintext() ckks_encoder.encode(input, scale, plain) encrypted = Ciphertext() encryptor.encrypt(plain, encrypted) rotated = Ciphertext() print("Rotate 2 steps left.") evaluator.rotate_vector(encrypted, 2, gal_keys, rotated) print(" + Decrypt and decode ...... Correct.") decryptor.decrypt(rotated, plain) result = DoubleVector() ckks_encoder.decode(plain, result) print_vector(result)
def _update_cryptors(self, keygen): """ :param keygen: :return: """ self.keygen = keygen self.public_key = keygen.public_key() self.secret_key = keygen.secret_key() self.encryptor = Encryptor(self.context, self.public_key) self.decryptor = Decryptor(self.context, self.secret_key) return
def seal_obj(): # params obj params = EncryptionParameters() # set params params.set_poly_modulus("1x^4096 + 1") params.set_coeff_modulus(seal.coeff_modulus_128(4096)) params.set_plain_modulus(1 << 16) # get context context = SEALContext(params) # get evaluator evaluator = Evaluator(context) # gen keys keygen = KeyGenerator(context) public_key = keygen.public_key() private_key = keygen.secret_key() # evaluator keys ev_keys = EvaluationKeys() keygen.generate_evaluation_keys(30, ev_keys) # get encryptor and decryptor encryptor = Encryptor(context, public_key) decryptor = Decryptor(context, private_key) # float number encoder encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 64, 32, 3) return evaluator, encoder.encode, encoder.decode, encryptor.encrypt, decryptor.decrypt, ev_keys
def __init__(self): # set parameters for encryption parms = EncryptionParameters() parms.set_poly_modulus("1x^2048 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) parms.set_plain_modulus(1 << 8) self.context = SEALContext(parms) keygen = KeyGenerator(self.context) self.encoder = IntegerEncoder(self.context.plain_modulus()) public_key = keygen.public_key() self.encryptor = Encryptor(self.context, public_key) secret_key = keygen.secret_key() self.decryptor = Decryptor(self.context, secret_key)
def __init__(self, context: SEALContext, scale: float, poly_modulus_degree: int, public_key: PublicKey = None, secret_key: SecretKey = None, relin_keys: RelinKeys = None, galois_keys: GaloisKeys = None): self.scale = scale self.context = context self.encoder = CKKSEncoder(context) self.evaluator = Evaluator(context) self.encryptor = Encryptor(context, public_key) self.decryptor = Decryptor(context, secret_key) self.relin_keys = relin_keys self.galois_keys = galois_keys self.poly_modulus_degree_log = np.log2(poly_modulus_degree)
def __init__(self, matrix=None): """ :param matrix: numpy.ndarray to be encrypted. """ self.parms = EncryptionParameters() self.parms.set_poly_modulus("1x^2048 + 1") self.parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) self.parms.set_plain_modulus(1 << 8) self.context = SEALContext(self.parms) # self.encoder = IntegerEncoder(self.context.plain_modulus()) self.encoder = FractionalEncoder(self.context.plain_modulus(), self.context.poly_modulus(), 64, 32, 3) self.keygen = KeyGenerator(self.context) self.public_key = self.keygen.public_key() self.secret_key = self.keygen.secret_key() self.encryptor = Encryptor(self.context, self.public_key) self.decryptor = Decryptor(self.context, self.secret_key) self.evaluator = Evaluator(self.context) self._saved = False self._encrypted = False self._id = '{0:04d}'.format(np.random.randint(1000)) if matrix is not None: assert len( matrix.shape) == 2, "Only 2D numpy matrices accepted currently" self.matrix = np.copy(matrix) self.encrypted_matrix = np.empty(self.matrix.shape, dtype=object) for i in range(self.matrix.shape[0]): for j in range(self.matrix.shape[1]): self.encrypted_matrix[i, j] = Ciphertext() else: self.matrix = None self.encrypted_matrix = None print(self._id, "Created")
def initialize_fractional( poly_modulus_degree=4096, security_level_bits=128, plain_modulus_power_of_two=10, plain_modulus=None, encoder_integral_coefficients=1024, encoder_fractional_coefficients=3072, encoder_base=2 ): parameters = EncryptionParameters() poly_modulus = "1x^" + str(poly_modulus_degree) + " + 1" parameters.set_poly_modulus(poly_modulus) if security_level_bits == 128: parameters.set_coeff_modulus(seal.coeff_modulus_128(poly_modulus_degree)) elif security_level_bits == 192: parameters.set_coeff_modulus(seal.coeff_modulus_192(poly_modulus_degree)) else: parameters.set_coeff_modulus(seal.coeff_modulus_128(poly_modulus_degree)) print("Info: security_level_bits unknown - using default security_level_bits = 128") if plain_modulus is None: plain_modulus = 1 << plain_modulus_power_of_two parameters.set_plain_modulus(plain_modulus) context = SEALContext(parameters) print_parameters(context) global encoder encoder = FractionalEncoder( context.plain_modulus(), context.poly_modulus(), encoder_integral_coefficients, encoder_fractional_coefficients, encoder_base ) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() global encryptor encryptor = Encryptor(context, public_key) global evaluator evaluator = Evaluator(context) global decryptor decryptor = Decryptor(context, secret_key) global evaluation_keys evaluation_keys = EvaluationKeys() keygen.generate_evaluation_keys(16, evaluation_keys)
class FractionalDecryptorUtils: def __init__(self, context): """ Class providing decryption operation :param context: Context initialising HE parameters """ self.context = context.context self.secret_key = context.secret_key self.decryptor = Decryptor(self.context, self.secret_key) self.encoder = FractionalEncoder(self.context.plain_modulus(), self.context.poly_modulus(), 64, 32, 3) self.evaluator = context.evaluator def decrypt(self, encrypted_res): plain_result = Plaintext() self.decryptor.decrypt(encrypted_res, plain_result) result = self.encoder.decode(plain_result) return result
def decryptMatrix(cm): r, c = len(cm), len(cm[0]) m = [[0] * c for i in range(r)] decryptor = Decryptor(context, secret_key) plain_result = Plaintext() for i in range(r): for j in range(c): decryptor.decrypt(cm[i][j], plain_result) m[i][j] = encoder.decode(plain_result) return m # m1 = [[1,3,2],[4,3,1]] # m2 = [[2,2,1]] # cm1, cm2 = encryptMatrix(m1), encryptMatrix(m2) # cm = matrixProduct(cm1, cm2) # print(decryptMatrix(cm)) # need to multiply encrypted matrix by encrypted vector
def do_per_amount(amount, subtract_from=15): """ Called on every message in the stream """ print("Transaction amount ", amount) parms = EncryptionParameters() parms.set_poly_modulus("1x^2048 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) parms.set_plain_modulus(1 << 8) context = SEALContext(parms) # Encode encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 64, 32, 3) # To create a fresh pair of keys one can call KeyGenerator::generate() at any time. keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() encryptor = Encryptor(context, public_key) plain1 = encoder.encode(amount) encoded2 = encoder.encode(subtract_from) # Encrypt encrypted1 = Ciphertext(parms) encryptor.encrypt(plain1, encrypted1) # Evaluate evaluator = Evaluator(context) evaluated = evaluate_subtraction_from_plain(evaluator, encrypted1, encoded2) # Decrypt and decode decryptor = Decryptor(context, secret_key) plain_result = Plaintext() decryptor.decrypt(evaluated, plain_result) result = encoder.decode(plain_result) str_result = "Amount left = " + str(result) print(str_result) return str_result
def encryption(value): # IntegerEncoder with base 2 encoder = IntegerEncoder(context.plain_modulus()) # generate public/private keys keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() # encrypts public key encryptor = Encryptor(context, public_key) # perform computations on ciphertexts evaluator = Evaluator(context) # decrypts secret key decryptor = Decryptor(context, secret_key) # perform encryptions plaintext = encoder.encode(value) # convert into encrypted ciphertext encrypt = Ciphertext() encryptor.encrypt(plaintext, encrypt) print("Encryption successful!") print("Encrypted ciphertext: " + (str)(value) + " as " + plaintext.to_string()) # noise budget of fresh encryptions print("Noise budget: " + (str)(decryptor.invariant_noise_budget(encrypt)) + " bits") # decrypts result result = Plaintext() decryptor.decrypt(encrypt, result) print("Decryption successful!") print("Plaintext: " + result.to_string()) # decode for original integer print("Original node: " + (str)(encoder.decode_int32(result)) + "\n")
def inner_product(cypher1, cypher2): # We also set up an Encryptor, Evaluator, and Decryptor here. evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) for i in range(len(cypher1)): evaluator.multiply(cypher1[i], cypher2[i]) encrypted_result = Ciphertext() evaluator.add_many(cypher1, encrypted_result) return encrypted_result
def _setup_members(self, context, config): self._encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), config['int_coeff'], config['fract_coeff'], config['base']) self._evaluator = Evaluator(context) pk, sk, self._ev_key = self._create_keys(context) self._encryptor = Encryptor(context, pk) self._decryptor = Decryptor(context, sk)
def initialize_encryption(): print_example_banner("Example: Basics I"); parms = EncryptionParameters() parms.set_poly_modulus("1x^2048 + 1") # factor: 0xfffffffff00001. parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) parms.set_plain_modulus(1 << 8) context = SEALContext(parms) print_parameters(context); # Here we choose to create an IntegerEncoder with base b=2. encoder = IntegerEncoder(context.plain_modulus()) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) return encryptor, evaluator, decryptor, encoder, context
def configure_params(self): with yaspin( text= 'Initializing Encryptor, Eavaluator, Decryptor with generated parameters.....', spinner='dots') as sp2: self.frac_encoder = FractionalEncoder(self.context.plain_modulus(), self.context.poly_modulus(), 64, 32, 3) self.encryptor = Encryptor(self.context, self.public_key) self.evaluator = Evaluator(self.context) self.decryptor = Decryptor(self.context, self.secret_key) if self.batching: self.crtbuilder = PolyCRTBuilder(self.context) sp2.hide() print(HTML( u'<b>></b> <msg> The system is ready to start encryption.</msg>' ), style=style) sp2.show() sp2.ok("✔")
def __init__(self, poly_modulus = 2048 ,bit_strength = 128 ,plain_modulus = 1<<8, integral_coeffs = 64, fractional_coeffs = 32, fractional_base = 3): parms = EncryptionParameters() parms.set_poly_modulus("1x^{} + 1".format(poly_modulus)) if (bit_strength == 128): parms.set_coeff_modulus(seal.coeff_modulus_128(poly_modulus)) else: parms.set_coeff_modulus(seal.coeff_modulus_192(poly_modulus)) parms.set_plain_modulus(plain_modulus) self.parms = parms context = SEALContext(parms) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() self.encryptor = Encryptor(context, public_key) self.evaluator = Evaluator(context) self.decryptor = Decryptor(context, secret_key) self.encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), integral_coeffs, fractional_coeffs, fractional_base)
def test1(): #TODO make the function taking dir name, iterating through all the descriptors, encrypting #TODO add progress bar - the videos are decrypted #TODO make a function taking a query and passing it to the sklearn function KNN with custom similarity funtion #TODO add progress bar - the search is performed (calculate distance between encrypted vectors) #TODO return results #TODO Vlad Display. The results are true. The same as we would compute similarity between ncrypted vectors. # In the same time we did not have to decrypt vectors stored in 3rd parity service to perform a search. #TODO get the metrics #TODO how to opimize a = np.ones((1, DESC_SIZE)) b = np.ones((1, DESC_SIZE)) for i in range(a.shape[1]): a[0, i] = random.uniform(0.0, 1.0) b[0, i] = random.uniform(0.0, 1.0) print("Descriptors for images a and b are: ") print("a ", a) print("b ", b) context, params = config() public_key, secret_key = keygen(context) frac_encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 64, 32, 3) encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) e1 = encrypt_vec(params, encryptor, frac_encoder, a) e3 = encrypt_vec(params, encryptor, frac_encoder, b) print("Calculate distance between plain a and b:") sim = euclidean_dist(a, b) print("Similarity between a and b: {}".format(sim)) print("Calculating distance between encrypted a and b:") time_start = time.time() enc_res = euclidean_dist_enc(evaluator, e1, e3) time_end = time.time() print("Done. Time: {} miliseconds".format( (str)(1000 * (time_end - time_start)))) plain_result = Plaintext() print("Decrypting result: ") time_start = time.time() decryptor.decrypt(enc_res, plain_result) res = frac_encoder.encode(plain_result) time_end = time.time() print("Done. Time: {} miliseconds".format( (str)(1000 * (time_end - time_start)))) print( "........................................................................" ) print("Noise budget {} bits".format( decryptor.invariant_noise_budget(enc_res))) print("Decrypted result {}, original result {}".format(res, sim)) print("Calculate distance between plain a and a:") sim = euclidean_dist(a, a.copy()) print("Similarity between a and b: {}".format(sim)) print("Calculating distance between encrypted a and a:") time_start = time.time() enc_res = euclidean_dist_enc(evaluator, e1, e1) time_end = time.time() print("Done. Time: {} miliseconds".format( (str)(1000 * (time_end - time_start)))) plain_result = Plaintext() print("Decrypting result: ") time_start = time.time() decryptor.decrypt(enc_res, plain_result) res = frac_encoder.encode(plain_result) time_end = time.time() print("Done. Time: {} miliseconds".format( (str)(1000 * (time_end - time_start)))) print( "........................................................................." ) print("Noise budget {} bits".format( decryptor.invariant_noise_budget(enc_res))) print("Decrypted result {}, original result {}".format(res, sim))
def dot_product(): print("Example: Weighted Average") # In this example we demonstrate the FractionalEncoder, and use it to compute # a weighted average of 10 encrypted rational numbers. In this computation we # perform homomorphic multiplications of ciphertexts by plaintexts, which is # much faster than regular multiplications of ciphertexts by ciphertexts. # Moreover, such `plain multiplications' never increase the ciphertext size, # which is why we have no need for evaluation keys in this example. # We start by creating encryption parameters, setting up the SEALContext, keys, # and other relevant objects. Since our computation has multiplicative depth of # only two, it suffices to use a small poly_modulus. parms = EncryptionParameters() parms.set_poly_modulus("1x^2048 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) parms.set_plain_modulus(1 << 8) context = SEALContext(parms) print_parameters(context) keygen = KeyGenerator(context) keygen2 = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() secret_key2 = keygen.secret_key() # We also set up an Encryptor, Evaluator, and Decryptor here. encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key2) # Create a vector of 10 rational numbers (as doubles). # rational_numbers = [3.1, 4.159, 2.65, 3.5897, 9.3, 2.3, 8.46, 2.64, 3.383, 2.795] rational_numbers = np.random.rand(10) # Create a vector of weights. # coefficients = [0.1, 0.05, 0.05, 0.2, 0.05, 0.3, 0.1, 0.025, 0.075, 0.05] coefficients = np.random.rand(10) my_result = np.dot(rational_numbers, coefficients) # We need a FractionalEncoder to encode the rational numbers into plaintext # polynomials. In this case we decide to reserve 64 coefficients of the # polynomial for the integral part (low-degree terms) and expand the fractional # part to 32 digits of precision (in base 3) (high-degree terms). These numbers # can be changed according to the precision that is needed; note that these # choices leave a lot of unused space in the 2048-coefficient polynomials. encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 64, 32, 3) # We create a vector of ciphertexts for encrypting the rational numbers. encrypted_rationals = [] rational_numbers_string = "Encoding and encrypting: " for i in range(10): # We create our Ciphertext objects into the vector by passing the # encryption parameters as an argument to the constructor. This ensures # that enough memory is allocated for a size 2 ciphertext. In this example # our ciphertexts never grow in size (plain multiplication does not cause # ciphertext growth), so we can expect the ciphertexts to remain in the same # location in memory throughout the computation. In more complicated examples # one might want to call a constructor that reserves enough memory for the # ciphertext to grow to a specified size to avoid costly memory moves when # multiplications and relinearizations are performed. encrypted_rationals.append(Ciphertext(parms)) encryptor.encrypt(encoder.encode(rational_numbers[i]), encrypted_rationals[i]) rational_numbers_string += (str)(rational_numbers[i])[:6] if i < 9: rational_numbers_string += ", " print(rational_numbers_string) # Next we encode the coefficients. There is no reason to encrypt these since they # are not private data. encoded_coefficients = [] encoded_coefficients_string = "Encoding plaintext coefficients: " encrypted_coefficients =[] for i in range(10): encoded_coefficients.append(encoder.encode(coefficients[i])) encrypted_coefficients.append(Ciphertext(parms)) encryptor.encrypt(encoded_coefficients[i], encrypted_coefficients[i]) encoded_coefficients_string += (str)(coefficients[i])[:6] if i < 9: encoded_coefficients_string += ", " print(encoded_coefficients_string) # We also need to encode 0.1. Multiplication by this plaintext will have the # effect of dividing by 10. Note that in SEAL it is impossible to divide # ciphertext by another ciphertext, but in this way division by a plaintext is # possible. div_by_ten = encoder.encode(0.1) # Now compute each multiplication. prod_result = [Ciphertext() for i in range(10)] prod_result2 = [Ciphertext() for i in range(10)] print("Computing products: ") for i in range(10): # Note how we use plain multiplication instead of usual multiplication. The # result overwrites the first argument in the function call. evaluator.multiply_plain(encrypted_rationals[i], encoded_coefficients[i], prod_result[i]) evaluator.multiply(encrypted_rationals[i], encrypted_coefficients[i], prod_result2[i]) print("Done") # To obtain the linear sum we need to still compute the sum of the ciphertexts # in encrypted_rationals. There is an easy way to add together a vector of # Ciphertexts. encrypted_result = Ciphertext() encrypted_result2 = Ciphertext() print("Adding up all 10 ciphertexts: ") evaluator.add_many(prod_result, encrypted_result) evaluator.add_many(prod_result2, encrypted_result2) print("Done") # Perform division by 10 by plain multiplication with div_by_ten. # print("Dividing by 10: ") # evaluator.multiply_plain(encrypted_result, div_by_ten) # print("Done") # How much noise budget do we have left? print("Noise budget in result: " + (str)(decryptor.invariant_noise_budget(encrypted_result)) + " bits") # Decrypt, decode, and print result. plain_result = Plaintext() plain_result2 = Plaintext() print("Decrypting result: ") decryptor.decrypt(encrypted_result, plain_result) decryptor.decrypt(encrypted_result2, plain_result2) print("Done") result = encoder.decode(plain_result) print("Weighted average: " + (str)(result)[:8]) result2 = encoder.decode(plain_result2) print("Weighted average: " + (str)(result2)[:8]) print('\n\n', my_result)
def pickle_ciphertext(): parms = EncryptionParameters() parms.set_poly_modulus("1x^2048 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) parms.set_plain_modulus(1 << 8) context = SEALContext(parms) # Print the parameters that we have chosen print_parameters(context); encoder = IntegerEncoder(context.plain_modulus()) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() # To be able to encrypt, we need to construct an instance of Encryptor. Note that # the Encryptor only requires the public key. encryptor = Encryptor(context, public_key) # Computations on the ciphertexts are performed with the Evaluator class. evaluator = Evaluator(context) # We will of course want to decrypt our results to verify that everything worked, # so we need to also construct an instance of Decryptor. Note that the Decryptor # requires the secret key. decryptor = Decryptor(context, secret_key) # We start by encoding two integers as plaintext polynomials. value1 = 5; plain1 = encoder.encode(value1); print("Encoded " + (str)(value1) + " as polynomial " + plain1.to_string() + " (plain1)") value2 = -7; plain2 = encoder.encode(value2); print("Encoded " + (str)(value2) + " as polynomial " + plain2.to_string() + " (plain2)") # Encrypting the values is easy. encrypted1 = Ciphertext() encrypted2 = Ciphertext() print("Encrypting plain1: ", encrypted1) encryptor.encrypt(plain1, encrypted1) print("Done (encrypted1)", encrypted1) print("Encrypting plain2: ") encryptor.encrypt(plain2, encrypted2) print("Done (encrypted2)") # output = open('ciphertest.pkl', 'wb') # dill.dumps(encrypted_save, output) # output.close() # encrypted1 = dill.load(open('ciphertest.pkl', 'rb')) output = open('session.pkl', 'wb') dill.dump_session('session.pkl') del encrypted1 sill.load_session('session.pkl') # To illustrate the concept of noise budget, we print the budgets in the fresh # encryptions. print("Noise budget in encrypted1: " + (str)(decryptor.invariant_noise_budget(encrypted1)) + " bits") print("Noise budget in encrypted2: " + (str)(decryptor.invariant_noise_budget(encrypted2)) + " bits") # As a simple example, we compute (-encrypted1 + encrypted2) * encrypted2. # Negation is a unary operation. evaluator.negate(encrypted1) # Negation does not consume any noise budget. print("Noise budget in -encrypted1: " + (str)(decryptor.invariant_noise_budget(encrypted1)) + " bits") # Addition can be done in-place (overwriting the first argument with the result, # or alternatively a three-argument overload with a separate destination variable # can be used. The in-place variants are always more efficient. Here we overwrite # encrypted1 with the sum. evaluator.add(encrypted1, encrypted2) # It is instructive to think that addition sets the noise budget to the minimum # of the input noise budgets. In this case both inputs had roughly the same # budget going on, and the output (in encrypted1) has just slightly lower budget. # Depending on probabilistic effects, the noise growth consumption may or may # not be visible when measured in whole bits. print("Noise budget in -encrypted1 + encrypted2: " + (str)(decryptor.invariant_noise_budget(encrypted1)) + " bits") # Finally multiply with encrypted2. Again, we use the in-place version of the # function, overwriting encrypted1 with the product. evaluator.multiply(encrypted1, encrypted2) # Multiplication consumes a lot of noise budget. This is clearly seen in the # print-out. The user can change the plain_modulus to see its effect on the # rate of noise budget consumption. print("Noise budget in (-encrypted1 + encrypted2) * encrypted2: " + (str)( decryptor.invariant_noise_budget(encrypted1)) + " bits") # Now we decrypt and decode our result. plain_result = Plaintext() print("Decrypting result: ") decryptor.decrypt(encrypted1, plain_result) print("Done") # Print the result plaintext polynomial. print("Plaintext polynomial: " + plain_result.to_string()) # Decode to obtain an integer result. print("Decoded integer: " + (str)(encoder.decode_int32(plain_result)))
########################## paramaters required ################################# parms = EncryptionParameters() parms.set_poly_modulus("1x^16384 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(16384)) parms.set_plain_modulus(1 << 16) context = SEALContext(parms) encoderF = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 30, 34, 3) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) num_cores = multiprocessing.cpu_count() -1 ########################## processing main matrix ################################ t1 = time.time() dir_path=os.path.dirname(os.path.realpath(__file__)) snp = open(dir_path+"/snpMat.txt","r+") S=[] for row in snp.readlines(): S.append(row.strip().split()) S=S[1:]
def example_rotation_bfv(): print("Example: Rotation / Rotation in BFV") parms = EncryptionParameters(scheme_type.BFV) poly_modulus_degree = 8192 parms.set_poly_modulus_degree(poly_modulus_degree) parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree)) parms.set_plain_modulus(PlainModulus.Batching(poly_modulus_degree, 20)) context = SEALContext.Create(parms) print_parameters(context) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() relin_keys = keygen.relin_keys() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) batch_encoder = BatchEncoder(context) slot_count = batch_encoder.slot_count() row_size = int(slot_count / 2) print("Plaintext matrix row size: {}".format(row_size)) pod_matrix = UInt64Vector([0]*slot_count) pod_matrix[0] = 0 pod_matrix[1] = 1 pod_matrix[2] = 2 pod_matrix[3] = 3 pod_matrix[row_size] = 4 pod_matrix[row_size + 1] = 5 pod_matrix[row_size + 2] = 6 pod_matrix[row_size + 3] = 7 print("Input plaintext matrix:") print_matrix(pod_matrix, row_size) #First we use BatchEncoder to encode the matrix into a plaintext. We encrypt #the plaintext as usual. plain_matrix = Plaintext() print("Encode and encrypt.") batch_encoder.encode(pod_matrix, plain_matrix) encrypted_matrix = Ciphertext() encryptor.encrypt(plain_matrix, encrypted_matrix) print(" + Noise budget in fresh encryption: {} bits".format( decryptor.invariant_noise_budget(encrypted_matrix))) #Rotations require yet another type of special key called `Galois keys'. These #are easily obtained from the KeyGenerator. gal_keys = keygen.galois_keys() #Now rotate both matrix rows 3 steps to the left, decrypt, decode, and print. print("Rotate rows 3 steps left.") evaluator.rotate_rows_inplace(encrypted_matrix, 3, gal_keys) plain_result = Plaintext() print(" + Noise budget after rotation: {} bits".format( decryptor.invariant_noise_budget(encrypted_matrix))) print(" + Decrypt and decode ...... Correct.") decryptor.decrypt(encrypted_matrix, plain_result) batch_encoder.decode(plain_result, pod_matrix) print_matrix(pod_matrix, row_size) #We can also rotate the columns, i.e., swap the rows. print("Rotate columns.") evaluator.rotate_columns_inplace(encrypted_matrix, gal_keys) print(" + Noise budget after rotation: {} bits".format( decryptor.invariant_noise_budget(encrypted_matrix))) print(" + Decrypt and decode ...... Correct.") decryptor.decrypt(encrypted_matrix, plain_result) batch_encoder.decode(plain_result, pod_matrix) print_matrix(pod_matrix, row_size) #Finally, we rotate the rows 4 steps to the right, decrypt, decode, and print. print("Rotate rows 4 steps right.") evaluator.rotate_rows_inplace(encrypted_matrix, -4, gal_keys) print(" + Noise budget after rotation: {} bits".format( decryptor.invariant_noise_budget(encrypted_matrix))) print(" + Decrypt and decode ...... Correct.") decryptor.decrypt(encrypted_matrix, plain_result) batch_encoder.decode(plain_result, pod_matrix) print_matrix(pod_matrix, row_size)
def __init__(self, secret_key, context): self.vec_decryptor = np.vectorize( Decryptor(context, secret_key).decrypt)
def encrypt(input_file): X = np.load(input_file) print("Starting the system....\n") y, people_descriptors = search_for_centroids(X) X, y = unison_shuffled_copies(X, y) np.save(input_file, X) np.save('./class_lables.npy', np.array(y)) np.save('./centroids.npy', np.array(people_descriptors)) print( "All necessary dependencies are installed. Everything is up-to-date!") print("The encryption mode is entered!") time_start = time.time() print('Configuring the encryption parameters') context, params = config() time_end = time.time() #TODO time for the report. Put to the slides #TODO Remove from logs print("Parameters are configured. Time: {}\n".format( (str)(1000 * (time_end - time_start)))) #print("Key generation .....") time_start = time.time() with yaspin(text='Generating public/private key pair', spinner='dots') as sp1: public_key, secret_key = keygen(context) time.sleep(1) sp1.hide() print(HTML( u'<b>></b> <msg>public/private key pair </msg> <sub-msg>is generated</sub-msg>' ), style=style) sp1.show() sp1.ok("✔") time_end = time.time() print("Keys are generated! Time: {}".format( (str)(1000 * (time_end - time_start)))) with yaspin( text= 'Initializing Encryptor, Eavaluator, Decryptor with generated parameters.....', spinner='dots') as sp2: frac_encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 64, 32, 3) encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) p = [] p.append(params) p.append(context) p.append(frac_encoder) p.append(encryptor) p.append(evaluator) p.append(decryptor) ENC_CONF = p sp2.hide() print(HTML( u'<b>></b> <msg> The system is ready to start encryption.</msg>'), style=style) sp2.show() sp2.ok("✔") #enc_X = np.empty(X.shape, dtype=object) with yaspin(text='Subscribing to the online-video stream.', spinner='clock') as sp3: time.sleep(5) sp3.hide() print(HTML(u'<b>></b> <msg> Success</msg>'), style=style) sp3.show() sp3.ok("✔") print("Detecting people") time_start = time.time() with yaspin( text='Encrypting the descriptor').white.bold.shark.on_blue as sp4: for i, x in enumerate(X): sp4.write("Got descriptor of 1x{} dimension for a person".format( x.shape[0])) time.sleep(1) #enc_X[i, :] = encrypt_vec(params, encryptor, frac_encoder, x) encrypt_vec(params, encryptor, frac_encoder, x) dists = [] for j in range(people_descriptors.shape[0]): dist1 = euclidean_dist(people_descriptors[j], x) dists.append(dist1) sp4.write( "Distance between 'Current' person and Person #{}: {:.2f} - Person #{}: {:.2f} - Person #{}: {:.2f} - Person #{}: {:.2f}" .format(0, dists[0], 1, dists[1], 2, dists[2], 3, dists[3])) time.sleep(1) dists = np.array(dists) if (dists[dists < 0.6] is not None): d = np.sort(dists)[0] sp4.write( "🔦FOUND descriptor similar to Person #{}".format(d)) time.sleep(1) sp4.hide() print(HTML( u'<b>�</b> <msg>descriptor {}</msg> <sub-msg>encryption complete</sub-msg>' .format(i)), style=style) sp4.show() #sp.ok("All stream is encrypted") sp4.ok("✔") time_end = time.time() print("Encryption is complete.") print( "ALL {} descriptors are successfully encrypted and sent to the CLOUD.". format(X.shape[0])) print("Time: {}".format((str)(1000 * (time_end - time_start))))
########################## paramaters required ################################# parms = EncryptionParameters() parms.set_poly_modulus("1x^16384 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(8192)) parms.set_plain_modulus(1 << 25) context = SEALContext(parms) encoderF = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 34, 30, 3) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) num_cores = multiprocessing.cpu_count() -1 N=4 b = numpy.random.random_integers(1,10,size=(N,N)) X = (b + b.T)/2 print("\nX:") print(X) print(numpy.linalg.det(X)) print("Inverse by Numpy:") print(numpy.linalg.inv(X)) print("\nMain program: \n") X= encrypting_Matrix(X) X_inv=inverseMatrix(X) SymetricMatrixCompletion(X_inv)
# coeff_modulus_192bit(int) parms.set_coeff_modulus(seal.coeff_modulus_128(2048)) # The plaintext modulus can be any positive integer, even though here we take # it to be a power of two. parms.set_plain_modulus(1 << 8) # 1 << 8 is bitwise operation which means 1 shifted eight times ie 2^8=256 context = SEALContext(parms) encoder = IntegerEncoder(context.plain_modulus()) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) value=7 plain1 = encoder.encode(value1) print("Encoded " + (str)(value) + " as polynomial " + plain1.to_string() + " (plain1)") encrypted _data= Ciphertext() encryptor.encrypt(plain, encrypted_data) print("Noise budget in encrypted1: " + (str)(decryptor.invariant_noise_budget(encrypted_data)) + " bits") # operations that can be performed ---> # result stored in encrypted1 data evaluator.negate(encrypted1_data) # result stored in encrypted1 data, encrpyted1 is modified
def example_ckks_basics(): print("Example: CKKS Basics"); #In this example we demonstrate evaluating a polynomial function # # PI*x^3 + 0.4*x + 1 # #on encrypted floating-point input data x for a set of 4096 equidistant points #in the interval [0, 1]. This example demonstrates many of the main features #of the CKKS scheme, but also the challenges in using it. # # We start by setting up the CKKS scheme. parms = EncryptionParameters(scheme_type.CKKS) #We saw in `2_encoders.cpp' that multiplication in CKKS causes scales #in ciphertexts to grow. The scale of any ciphertext must not get too close #to the total size of coeff_modulus, or else the ciphertext simply runs out of #room to store the scaled-up plaintext. The CKKS scheme provides a `rescale' #functionality that can reduce the scale, and stabilize the scale expansion. # #Rescaling is a kind of modulus switch operation (recall `3_levels.cpp'). #As modulus switching, it removes the last of the primes from coeff_modulus, #but as a side-effect it scales down the ciphertext by the removed prime. #Usually we want to have perfect control over how the scales are changed, #which is why for the CKKS scheme it is more common to use carefully selected #primes for the coeff_modulus. # #More precisely, suppose that the scale in a CKKS ciphertext is S, and the #last prime in the current coeff_modulus (for the ciphertext) is P. Rescaling #to the next level changes the scale to S/P, and removes the prime P from the #coeff_modulus, as usual in modulus switching. The number of primes limits #how many rescalings can be done, and thus limits the multiplicative depth of #the computation. # #It is possible to choose the initial scale freely. One good strategy can be #to is to set the initial scale S and primes P_i in the coeff_modulus to be #very close to each other. If ciphertexts have scale S before multiplication, #they have scale S^2 after multiplication, and S^2/P_i after rescaling. If all #P_i are close to S, then S^2/P_i is close to S again. This way we stabilize the #scales to be close to S throughout the computation. Generally, for a circuit #of depth D, we need to rescale D times, i.e., we need to be able to remove D #primes from the coefficient modulus. Once we have only one prime left in the #coeff_modulus, the remaining prime must be larger than S by a few bits to #preserve the pre-decimal-point value of the plaintext. # #Therefore, a generally good strategy is to choose parameters for the CKKS #scheme as follows: # # (1) Choose a 60-bit prime as the first prime in coeff_modulus. This will # give the highest precision when decrypting; # (2) Choose another 60-bit prime as the last element of coeff_modulus, as # this will be used as the special prime and should be as large as the # largest of the other primes; # (3) Choose the intermediate primes to be close to each other. # #We use CoeffModulus::Create to generate primes of the appropriate size. Note #that our coeff_modulus is 200 bits total, which is below the bound for our #poly_modulus_degree: CoeffModulus::MaxBitCount(8192) returns 218. poly_modulus_degree = 8192 parms.set_poly_modulus_degree(poly_modulus_degree) parms.set_coeff_modulus(CoeffModulus.Create( poly_modulus_degree, IntVector([60, 40, 40, 60]))) #We choose the initial scale to be 2^40. At the last level, this leaves us #60-40=20 bits of precision before the decimal point, and enough (roughly #10-20 bits) of precision after the decimal point. Since our intermediate #primes are 40 bits (in fact, they are very close to 2^40), we can achieve #scale stabilization as described above. scale = 2.0**40 context = SEALContext.Create(parms) print_parameters(context) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() relin_keys = keygen.relin_keys() encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) encoder = CKKSEncoder(context) slot_count = encoder.slot_count() print("Number of slots: {}".format(slot_count)) step_size = 1.0 / (slot_count - 1) input = DoubleVector(list(map(lambda x: x*step_size, range(0, slot_count)))) print("Input vector: ") print_vector(input) print("Evaluating polynomial PI*x^3 + 0.4x + 1 ...") #We create plaintexts for PI, 0.4, and 1 using an overload of CKKSEncoder::encode #that encodes the given floating-point value to every slot in the vector. plain_coeff3 = Plaintext() plain_coeff1 = Plaintext() plain_coeff0 = Plaintext() encoder.encode(3.14159265, scale, plain_coeff3) encoder.encode(0.4, scale, plain_coeff1) encoder.encode(1.0, scale, plain_coeff0) x_plain = Plaintext() print("Encode input vectors.") encoder.encode(input, scale, x_plain) x1_encrypted = Ciphertext() encryptor.encrypt(x_plain, x1_encrypted) #To compute x^3 we first compute x^2 and relinearize. However, the scale has #now grown to 2^80. x3_encrypted = Ciphertext() print("Compute x^2 and relinearize:") evaluator.square(x1_encrypted, x3_encrypted) evaluator.relinearize_inplace(x3_encrypted, relin_keys) print(" + Scale of x^2 before rescale: {} bits".format(log2(x3_encrypted.scale()))) #Now rescale; in addition to a modulus switch, the scale is reduced down by #a factor equal to the prime that was switched away (40-bit prime). Hence, the #new scale should be close to 2^40. Note, however, that the scale is not equal #to 2^40: this is because the 40-bit prime is only close to 2^40. print("Rescale x^2.") evaluator.rescale_to_next_inplace(x3_encrypted) print(" + Scale of x^2 after rescale: {} bits".format(log2(x3_encrypted.scale()))) #Now x3_encrypted is at a different level than x1_encrypted, which prevents us #from multiplying them to compute x^3. We could simply switch x1_encrypted to #the next parameters in the modulus switching chain. However, since we still #need to multiply the x^3 term with PI (plain_coeff3), we instead compute PI*x #first and multiply that with x^2 to obtain PI*x^3. To this end, we compute #PI*x and rescale it back from scale 2^80 to something close to 2^40. print("Compute and rescale PI*x.") x1_encrypted_coeff3 = Ciphertext() evaluator.multiply_plain(x1_encrypted, plain_coeff3, x1_encrypted_coeff3) print(" + Scale of PI*x before rescale: {} bits".format(log2(x1_encrypted_coeff3.scale()))) evaluator.rescale_to_next_inplace(x1_encrypted_coeff3) print(" + Scale of PI*x after rescale: {} bits".format(log2(x1_encrypted_coeff3.scale()))) #Since x3_encrypted and x1_encrypted_coeff3 have the same exact scale and use #the same encryption parameters, we can multiply them together. We write the #result to x3_encrypted, relinearize, and rescale. Note that again the scale #is something close to 2^40, but not exactly 2^40 due to yet another scaling #by a prime. We are down to the last level in the modulus switching chain. print("Compute, relinearize, and rescale (PI*x)*x^2.") evaluator.multiply_inplace(x3_encrypted, x1_encrypted_coeff3) evaluator.relinearize_inplace(x3_encrypted, relin_keys) print(" + Scale of PI*x^3 before rescale: {} bits".format(log2(x3_encrypted.scale()))) evaluator.rescale_to_next_inplace(x3_encrypted) print(" + Scale of PI*x^3 after rescale: {} bits".format(log2(x3_encrypted.scale()))) #Next we compute the degree one term. All this requires is one multiply_plain #with plain_coeff1. We overwrite x1_encrypted with the result. print("Compute and rescale 0.4*x.") evaluator.multiply_plain_inplace(x1_encrypted, plain_coeff1) print(" + Scale of 0.4*x before rescale: {} bits".format(log2(x1_encrypted.scale()))) evaluator.rescale_to_next_inplace(x1_encrypted) print(" + Scale of 0.4*x after rescale: {} bits".format(log2(x1_encrypted.scale()))) #Now we would hope to compute the sum of all three terms. However, there is #a serious problem: the encryption parameters used by all three terms are #different due to modulus switching from rescaling. # #Encrypted addition and subtraction require that the scales of the inputs are #the same, and also that the encryption parameters (parms_id) match. If there #is a mismatch, Evaluator will throw an exception. print("Parameters used by all three terms are different.") print(" + Modulus chain index for x3_encrypted: {}".format( context.get_context_data(x3_encrypted.parms_id()).chain_index())) print(" + Modulus chain index for x1_encrypted: {}".format( context.get_context_data(x1_encrypted.parms_id()).chain_index())) print(" + Modulus chain index for plain_coeff0: {}".format( context.get_context_data(plain_coeff0.parms_id()).chain_index())) #Let us carefully consider what the scales are at this point. We denote the #primes in coeff_modulus as P_0, P_1, P_2, P_3, in this order. P_3 is used as #the special modulus and is not involved in rescalings. After the computations #above the scales in ciphertexts are: # # - Product x^2 has scale 2^80 and is at level 2; # - Product PI*x has scale 2^80 and is at level 2; # - We rescaled both down to scale 2^80/P_2 and level 1; # - Product PI*x^3 has scale (2^80/P_2)^2; # - We rescaled it down to scale (2^80/P_2)^2/P_1 and level 0; # - Product 0.4*x has scale 2^80; # - We rescaled it down to scale 2^80/P_2 and level 1; # - The contant term 1 has scale 2^40 and is at level 2. # #Although the scales of all three terms are approximately 2^40, their exact #values are different, hence they cannot be added together. print("The exact scales of all three terms are different:") print(" + Exact scale in PI*x^3: {0:0.10f}".format(x3_encrypted.scale())) print(" + Exact scale in 0.4*x: {0:0.10f}".format(x1_encrypted.scale())) print(" + Exact scale in 1: {0:0.10f}".format(plain_coeff0.scale())) #There are many ways to fix this problem. Since P_2 and P_1 are really close #to 2^40, we can simply "lie" to Microsoft SEAL and set the scales to be the #same. For example, changing the scale of PI*x^3 to 2^40 simply means that we #scale the value of PI*x^3 by 2^120/(P_2^2*P_1), which is very close to 1. #This should not result in any noticeable error. # #Another option would be to encode 1 with scale 2^80/P_2, do a multiply_plain #with 0.4*x, and finally rescale. In this case we would need to additionally #make sure to encode 1 with appropriate encryption parameters (parms_id). # #In this example we will use the first (simplest) approach and simply change #the scale of PI*x^3 and 0.4*x to 2^40. print("Normalize scales to 2^40.") x3_encrypted.set_scale(2.0**40) x1_encrypted.set_scale(2.0**40) #We still have a problem with mismatching encryption parameters. This is easy #to fix by using traditional modulus switching (no rescaling). CKKS supports #modulus switching just like the BFV scheme, allowing us to switch away parts #of the coefficient modulus when it is simply not needed. print("Normalize encryption parameters to the lowest level.") last_parms_id = x3_encrypted.parms_id() evaluator.mod_switch_to_inplace(x1_encrypted, last_parms_id) evaluator.mod_switch_to_inplace(plain_coeff0, last_parms_id) #All three ciphertexts are now compatible and can be added. print("Compute PI*x^3 + 0.4*x + 1.") encrypted_result = Ciphertext() evaluator.add(x3_encrypted, x1_encrypted, encrypted_result) evaluator.add_plain_inplace(encrypted_result, plain_coeff0) #First print the true result. plain_result = Plaintext() print("Decrypt and decode PI*x^3 + 0.4x + 1.") print(" + Expected result:") true_result = DoubleVector(list(map(lambda x: (3.14159265 * x * x + 0.4)* x + 1, input))) print_vector(true_result) #Decrypt, decode, and print the result. decryptor.decrypt(encrypted_result, plain_result) result = DoubleVector() encoder.decode(plain_result, result) print(" + Computed result ...... Correct.") print_vector(result)