コード例 #1
0
 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)
コード例 #2
0
    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
コード例 #3
0
 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
コード例 #4
0
ファイル: encrypt4.py プロジェクト: overgter/CrimeCamera
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)))
コード例 #5
0
ファイル: example_5_rotation.py プロジェクト: exii-com/PySEAL
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)
コード例 #6
0
    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
コード例 #7
0
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
コード例 #8
0
    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)
コード例 #9
0
 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)
コード例 #10
0
    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")
コード例 #11
0
ファイル: homenc.py プロジェクト: phapdn/HE_LogReg
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)
コード例 #12
0
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
コード例 #13
0
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
コード例 #14
0
ファイル: simply_parallel.py プロジェクト: snugghash/bHEnk
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
コード例 #15
0
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")
コード例 #16
0
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
コード例 #17
0
    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)
コード例 #18
0
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
コード例 #19
0
 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("✔")
コード例 #20
0
	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)
コード例 #21
0
ファイル: encrypt4.py プロジェクト: overgter/CrimeCamera
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))
コード例 #22
0
ファイル: matmul.py プロジェクト: adityachivu/pyseal-project
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)
コード例 #23
0
ファイル: matmul.py プロジェクト: adityachivu/pyseal-project
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)))
コード例 #24
0
ファイル: linearRegressionHE.py プロジェクト: tanu17/PySEAL
	########################## 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:]
コード例 #25
0
ファイル: example_5_rotation.py プロジェクト: exii-com/PySEAL
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)
コード例 #26
0
 def __init__(self, secret_key, context):
     self.vec_decryptor = np.vectorize(
         Decryptor(context, secret_key).decrypt)
コード例 #27
0
ファイル: encrypt4.py プロジェクト: overgter/CrimeCamera
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))))
コード例 #28
0
	########################## 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)
コード例 #29
0
#     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
コード例 #30
0
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)