def set_parms(self, parms: EncryptionParameters, print_parms=False): self._parms = parms self._context = SEALContext.Create(self._parms) self._encoder = IntegerEncoder(self._context) self._evaluator = Evaluator(self._context) if print_parms: self.print_parameters(self._context)
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 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 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 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 set_encoder( self, fractional_encoder=True, whole_sign_digits=32, decimal_sign_digits=32, base=3, ): if fractional_encoder: self.__enco = FractionalEncoder( self._cont.plain_modulus(), self._cont.poly_modulus(), whole_sign_digits, decimal_sign_digits, base, ) else: self.__enco = IntegerEncoder( self._cont.plain_modulus(), base, )
# to determine the security of their parameter choices. # Our recommended values for the coefficient modulus can be easily accessed # through the functions # coeff_modulus_128bit(int) # 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")
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)))
print("Importing dataset...") data = pd.read_csv('/app/Social_Network_Ads.csv') dataset = data.iloc[:, [2, 3, 4]].values #dataset1 = data.iloc [:, [2, 3]].values print("Done\n\n\n") print("Setting encryption parameters...") parms = EncryptionParameters() parms.set_poly_modulus("1x^16384 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(16384)) parms.set_plain_modulus(1 << 12) print("Done\n\n\n") context = SEALContext(parms) #print_parameters(context); 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) encoder = FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 2048, 32, 3) X, Y = splitDataset(dataset, 0.8) size = len(X) size_y = len(Y) X1 = [[0 for x in range(2)] for y in range(size)] Y1 = [[0 for x in range(2)] for y in range(size)]
Plaintext, \ SEALContext, \ EvaluationKeys, \ GaloisKeys, \ PolyCRTBuilder, \ ChooserEncoder, \ ChooserEvaluator, \ ChooserPoly parms = EncryptionParameters() parms.set_poly_modulus("1x^8192 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(8192)) parms.set_plain_modulus(1 << 21) context = SEALContext(parms) encoder = IntegerEncoder(context.plain_modulus()) keygen = KeyGenerator(context) public_key = keygen.public_key() secret_key = keygen.secret_key() ev_keys40 = EvaluationKeys() ev_keys20 = EvaluationKeys() #keygen.generate_evaluation_keys(40,5,ev_keys40) keygen.generate_evaluation_keys(20, 3, ev_keys20) encryptor = Encryptor(context, public_key) evaluator = Evaluator(context) decryptor = Decryptor(context, secret_key) A = [] n = int(input("Enter dimension: ")) for i in range(n):
for i in range(16): A.append(random.randint(0,64)) A+=[0]*(100-16) print(A) for i in range(16,32,5): A[i]=1 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) encoder = IntegerEncoder(context.plain_modulus()) encoderF =FractionalEncoder(context.plain_modulus(), context.poly_modulus(), 64, 32, 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) for i in range(len(A)): A_plain.append(encoder.encode(A[i])) A_cipherObject.append(Ciphertext()) encryptor.encrypt(A_plain[i],A_cipherObject[i]) print("Noise budget of "+ str(i)+" "+str((decryptor.invariant_noise_budget(A_cipherObject[i]))) + " bits") A_cipherObject=chunk(A_cipherObject)
def example_integer_encoder(): print("Example: Encoders / Integer Encoder") #[IntegerEncoder] (For BFV scheme only) # #The IntegerEncoder encodes integers to BFV plaintext polynomials as follows. #First, a binary expansion of the integer is computed. Next, a polynomial is #created with the bits as coefficients. For example, the integer # # 26 = 2^4 + 2^3 + 2^1 # #is encoded as the polynomial 1x^4 + 1x^3 + 1x^1. Conversely, plaintext #polynomials are decoded by evaluating them at x=2. For negative numbers the #IntegerEncoder simply stores all coefficients as either 0 or -1, where -1 is #represented by the unsigned integer plain_modulus - 1 in memory. # #Since encrypted computations operate on the polynomials rather than on the #encoded integers themselves, the polynomial coefficients will grow in the #course of such computations. For example, computing the sum of the encrypted #encoded integer 26 with itself will result in an encrypted polynomial with #larger coefficients: 2x^4 + 2x^3 + 2x^1. Squaring the encrypted encoded #integer 26 results also in increased coefficients due to cross-terms, namely, # # (2x^4 + 2x^3 + 2x^1)^2 = 1x^8 + 2x^7 + 1x^6 + 2x^5 + 2x^4 + 1x^2; # #further computations will quickly increase the coefficients much more. #Decoding will still work correctly in this case (evaluating the polynomial #at x=2), but since the coefficients of plaintext polynomials are really #integers modulo plain_modulus, implicit reduction modulo plain_modulus may #yield unexpected results. For example, adding 1x^4 + 1x^3 + 1x^1 to itself #plain_modulus many times will result in the constant polynomial 0, which is #clearly not equal to 26 * plain_modulus. It can be difficult to predict when #such overflow will take place especially when computing several sequential #multiplications. # #The IntegerEncoder is easy to understand and use for simple computations, #and can be a good tool to experiment with for users new to Microsoft SEAL. #However, advanced users will probably prefer more efficient approaches, #such as the BatchEncoder or the CKKSEncoder. parms = EncryptionParameters(scheme_type.BFV) poly_modulus_degree = 4096 parms.set_poly_modulus_degree(poly_modulus_degree) parms.set_coeff_modulus(CoeffModulus.BFVDefault(poly_modulus_degree)) #There is no hidden logic behind our choice of the plain_modulus. The only #thing that matters is that the plaintext polynomial coefficients will not #exceed this value at any point during our computation; otherwise the result #will be incorrect. parms.set_plain_modulus(512) context = SEALContext.Create(parms) print_parameters(context) 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) #We create an IntegerEncoder. encoder = IntegerEncoder(context) #First, we encode two integers as plaintext polynomials. Note that encoding #is not encryption: at this point nothing is encrypted. value1 = 5 plain1 = encoder.encode(value1) print("Encode {} as polynomial {} (plain1), ".format( value1, plain1.to_string())) value2 = -7 plain2 = encoder.encode(value2) print(" encode {} as polynomial {} (plain2)".format( value2, plain2.to_string())) #Now we can encrypt the plaintext polynomials. encrypted1 = Ciphertext() encrypted2 = Ciphertext() print("Encrypt plain1 to encrypted1 and plain2 to encrypted2.") encryptor.encrypt(plain1, encrypted1) encryptor.encrypt(plain2, encrypted2) print(" + Noise budget in encrypted1: {} bits".format( decryptor.invariant_noise_budget(encrypted1))) print(" + Noise budget in encrypted2: {} bits".format( decryptor.invariant_noise_budget(encrypted2))) #As a simple example, we compute (-encrypted1 + encrypted2) * encrypted2. encryptor.encrypt(plain2, encrypted2) encrypted_result = Ciphertext() print( "Compute encrypted_result = (-encrypted1 + encrypted2) * encrypted2.") evaluator.negate(encrypted1, encrypted_result) evaluator.add_inplace(encrypted_result, encrypted2) evaluator.multiply_inplace(encrypted_result, encrypted2) print(" + Noise budget in encrypted_result: {} bits".format( decryptor.invariant_noise_budget(encrypted_result))) plain_result = Plaintext() print("Decrypt encrypted_result to plain_result.") decryptor.decrypt(encrypted_result, plain_result) #Print the result plaintext polynomial. The coefficients are not even close #to exceeding our plain_modulus, 512. print(" + Plaintext polynomial: {}".format(plain_result.to_string())) #Decode to obtain an integer result. print("Decode plain_result.") print(" + Decoded integer: {} ...... Correct.".format( encoder.decode_int32(plain_result)))
class Encryption: ''' Provides encryption / encoding methods to realize the homomorphic operations ''' 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 encrypt_live_neighbours_grid(self, live_neighbours_grid, dim): ''' Encodes the live neighbor matrix by applying following rules. If the cell [i][j] has 2 neighbors with 0, 3 neighbors with 2, and otherwise with -2. Afterwards encrypt it using PySEAL and store in a list :param live_neighbours_grid: live neighbor matrix as np array :param dim: dimension of the board :return: List of encoded and encrypted neighbors ''' encrypted_live_neighbours_grid = [] # Loop through every element of the board and encrypt it for i in range(dim): for j in range(dim): # transformation / encoding rules if(live_neighbours_grid[i][j] == 2): value = 0 elif(live_neighbours_grid[i][j] == 3): value = 2 elif(live_neighbours_grid[i][j] > 3 or live_neighbours_grid[i][j] < 2): value = -2 # element-wise homomorphic encryption encrypted = Ciphertext() plain = self.encoder.encode(value) self.encryptor.encrypt(plain, encrypted) encrypted_live_neighbours_grid.append(encrypted) return encrypted_live_neighbours_grid def encrypt_old_grid(self, old_grid, dim): ''' Encrypts each element in the old board state using PySEAL and adds it to a list :param old_grid: :param dim: :return: ''' encrypted_old_grid = [] # Loop through every element of the board and encrypt it for i in range(dim): for j in range(dim): # element-wise homomorphic encryption encrypted = Ciphertext() value = old_grid[i][j] plain = self.encoder.encode(value) self.encryptor.encrypt(plain, encrypted) encrypted_old_grid.append(encrypted) return encrypted_old_grid def decrypt_new_grid(self, encrypted_new_grid, dim): ''' Decrypts a homomorphic-encrypted grid by looping through every element, decrypt it and then applying the decoding rules to get the new board state :param encrypted_new_grid: :param dim: :return: ''' new_grid = numpy.zeros(dim*dim, dtype='i').reshape(dim,dim) for i in range(dim): for j in range(dim): plain = Plaintext() encrypted = encrypted_new_grid[dim*i + j] self.decryptor.decrypt(encrypted, plain) value = self.encoder.decode_int32(plain) # transformation / decoding rules if(value <= 0): new_grid[i][j] = 0 elif(value > 0): new_grid[i][j] = 1 return new_grid
def example_basics_i(): print_example_banner("Example: Basics I") # In this example we demonstrate setting up encryption parameters and other # relevant objects for performing simple computations on encrypted integers. # SEAL uses the Fan-Vercauteren (FV) homomorphic encryption scheme. We refer to # https://eprint.iacr.org/2012/144 for full details on how the FV scheme works. # For better performance, SEAL implements the "FullRNS" optimization of FV, as # described in https://eprint.iacr.org/2016/510. # The first task is to set up an instance of the EncryptionParameters class. # It is critical to understand how these different parameters behave, how they # affect the encryption scheme, performance, and the security level. There are # three encryption parameters that are necessary to set: # - poly_modulus (polynomial modulus); # - coeff_modulus ([ciphertext] coefficient modulus); # - plain_modulus (plaintext modulus). # A fourth parameter -- noise_standard_deviation -- has a default value of 3.19 # and should not be necessary to modify unless the user has a specific reason # to and knows what they are doing. # The encryption scheme implemented in SEAL cannot perform arbitrary computations # on encrypted data. Instead, each ciphertext has a specific quantity called the # `invariant noise budget' -- or `noise budget' for short -- measured in bits. # The noise budget of a freshly encrypted ciphertext (initial noise budget) is # determined by the encryption parameters. Homomorphic operations consume the # noise budget at a rate also determined by the encryption parameters. In SEAL # the two basic homomorphic operations are additions and multiplications, of # which additions can generally be thought of as being nearly free in terms of # noise budget consumption compared to multiplications. Since noise budget # consumption is compounding in sequential multiplications, the most significant # factor in choosing appropriate encryption parameters is the multiplicative # depth of the arithmetic circuit that needs to be evaluated. Once the noise # budget in a ciphertext reaches zero, it becomes too corrupted to be decrypted. # Thus, it is essential to choose the parameters to be large enough to support # the desired computation; otherwise the result is impossible to make sense of # even with the secret key. parms = EncryptionParameters() # We first set the polynomial modulus. This must be a power-of-2 cyclotomic # polynomial, i.e. a polynomial of the form "1x^(power-of-2) + 1". The polynomial # modulus should be thought of mainly affecting the security level of the scheme; # larger polynomial modulus makes the scheme more secure. At the same time, it # makes ciphertext sizes larger, and consequently all operations slower. # Recommended degrees for poly_modulus are 1024, 2048, 4096, 8192, 16384, 32768, # but it is also possible to go beyond this. Since we perform only a very small # computation in this example, it suffices to use a very small polynomial modulus parms.set_poly_modulus("1x^2048 + 1") # Next we choose the [ciphertext] coefficient modulus (coeff_modulus). The size # of the coefficient modulus should be thought of as the most significant factor # in determining the noise budget in a freshly encrypted ciphertext: bigger means # more noise budget. Unfortunately, a larger coefficient modulus also lowers the # security level of the scheme. Thus, if a large noise budget is required for # complicated computations, a large coefficient modulus needs to be used, and the # reduction in the security level must be countered by simultaneously increasing # the polynomial modulus. # To make parameter selection easier for the user, we have constructed sets of # largest allowed coefficient moduli for 128-bit and 192-bit security levels # for different choices of the polynomial modulus. These recommended parameters # follow the Security white paper at http://HomomorphicEncryption.org. However, # due to the complexity of this topic, we highly recommend the user to directly # consult an expert in homomorphic encryption and RLWE-based encryption schemes # to determine the security of their parameter choices. # Our recommended values for the coefficient modulus can be easily accessed # through the functions # coeff_modulus_128bit(int) # coeff_modulus_192bit(int) # for 128-bit and 192-bit security levels. The integer parameter is the degree # of the polynomial modulus. # In SEAL the coefficient modulus is a positive composite number -- a product # of distinct primes of size up to 60 bits. When we talk about the size of the # coefficient modulus we mean the bit length of the product of the small primes. # The small primes are represented by instances of the SmallModulus class; for # example coeff_modulus_128bit(int) returns a vector of SmallModulus instances. # It is possible for the user to select their own small primes. Since SEAL uses # the Number Theoretic Transform (NTT) for polynomial multiplications modulo the # factors of the coefficient modulus, the factors need to be prime numbers # congruent to 1 modulo 2*degree(poly_modulus). We have generated a list of such # prime numbers of various sizes, that the user can easily access through the # functions # small_mods_60bit(int) # small_mods_50bit(int) # small_mods_40bit(int) # small_mods_30bit(int) # each of which gives access to an array of primes of the denoted size. These # primes are located in the source file util/globals.cpp. # Performance is mainly affected by the size of the polynomial modulus, and the # number of prime factors in the coefficient modulus. Thus, it is important to # use as few factors in the coefficient modulus as possible. # In this example we use the default coefficient modulus for a 128-bit security # level. Concretely, this coefficient modulus consists of only one 56-bit prime # factor: 0xfffffffff00001. 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. In fact, in many cases one might instead want it to # be a prime number; we will see this in example_batching(). The plaintext # modulus determines the size of the plaintext data type, but it also affects # the noise budget in a freshly encrypted ciphertext, and the consumption of # the noise budget in homomorphic multiplication. Thus, it is essential to try # to keep the plaintext data type as small as possible for good performance. # The noise budget in a freshly encrypted ciphertext is # ~ log2(coeff_modulus/plain_modulus) (bits) # and the noise budget consumption in a homomorphic multiplication is of the # form log2(plain_modulus) + (other terms). parms.set_plain_modulus(1 << 8) # Now that all parameters are set, we are ready to construct a SEALContext # object. This is a heavy class that checks the validity and properties of # the parameters we just set, and performs and stores several important # pre-computations. context = SEALContext(parms) # Print the parameters that we have chosen print_parameters(context) # Plaintexts in the FV scheme are polynomials with coefficients integers modulo # plain_modulus. To encrypt for example integers instead, one can use an # `encoding scheme' to represent the integers as such polynomials. SEAL comes # with a few basic encoders: # [IntegerEncoder] # Given an integer base b, encodes integers as plaintext polynomials as follows. # First, a base-b expansion of the integer is computed. This expansion uses # a `balanced' set of representatives of integers modulo b as the coefficients. # Namely, when b is odd the coefficients are integers between -(b-1)/2 and # (b-1)/2. When b is even, the integers are between -b/2 and (b-1)/2, except # when b is two and the usual binary expansion is used (coefficients 0 and 1). # Decoding amounts to evaluating the polynomial at x=b. For example, if b=2, # the integer # 26 = 2^4 + 2^3 + 2^1 # is encoded as the polynomial 1x^4 + 1x^3 + 1x^1. When b=3, # 26 = 3^3 - 3^0 # is encoded as the polynomial 1x^3 - 1. In memory polynomial coefficients are # always stored as unsigned integers by storing their smallest non-negative # representatives modulo plain_modulus. To create a base-b integer encoder, # use the constructor IntegerEncoder(plain_modulus, b). If no b is given, b=2 # is used. # [FractionalEncoder] # The FractionalEncoder encodes fixed-precision rational numbers as follows. # It expands the number in a given base b, possibly truncating an infinite # fractional part to finite precision, e.g. # 26.75 = 2^4 + 2^3 + 2^1 + 2^(-1) + 2^(-2) # when b=2. For the sake of the example, suppose poly_modulus is 1x^1024 + 1. # It then represents the integer part of the number in the same way as in # IntegerEncoder (with b=2 here), and moves the fractional part instead to the # highest degree part of the polynomial, but with signs of the coefficients # changed. In this example we would represent 26.75 as the polynomial # -1x^1023 - 1x^1022 + 1x^4 + 1x^3 + 1x^1. # In memory the negative coefficients of the polynomial will be represented as # their negatives modulo plain_modulus. # [PolyCRTBuilder] # If plain_modulus is a prime congruent to 1 modulo 2*degree(poly_modulus), the # plaintext elements can be viewed as 2-by-(degree(poly_modulus) / 2) matrices # with elements integers modulo plain_modulus. When a desired computation can be # vectorized, using PolyCRTBuilder can result in massive performance improvements # over naively encrypting and operating on each input number separately. Thus, # in more complicated computations this is likely to be by far the most important # and useful encoder. In example_batching() we show how to use and operate on # encrypted matrix plaintexts. # For performance reasons, in homomorphic encryption one typically wants to keep # the plaintext data types as small as possible, which can make it challenging to # prevent data type overflow in more complicated computations, especially when # operating on rational numbers that have been scaled to integers. When using # PolyCRTBuilder estimating whether an overflow occurs is a fairly standard task, # as the matrix slots are integers modulo plain_modulus, and each slot is operated # on independently of the others. When using IntegerEncoder or FractionalEncoder # it is substantially more difficult to estimate when an overflow occurs in the # plaintext, and choosing the plaintext modulus very carefully to be large enough # is critical to avoid unexpected results. Specifically, one needs to estimate how # large the largest coefficient in the polynomial view of all of the plaintext # elements becomes, and choose the plaintext modulus to be larger than this value. # SEAL comes with an automatic parameter selection tool that can help with this # task, as is demonstrated in example_parameter_selection(). # Here we choose to create an IntegerEncoder with base b=2. encoder = IntegerEncoder(context.plain_modulus()) # We are now ready to generate the secret and public keys. For this purpose we need # an instance of the KeyGenerator class. Constructing a KeyGenerator automatically # generates the public and secret key, which can then be read to local variables. # 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() # 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: ") encryptor.encrypt(plain1, encrypted1) print("Done (encrypted1)") print("Encrypting plain2: ") encryptor.encrypt(plain2, encrypted2) print("Done (encrypted2)") # 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)))
Plaintext, \ SEALContext, \ EvaluationKeys, \ GaloisKeys, \ PolyCRTBuilder, \ ChooserEncoder, \ ChooserEvaluator, \ ChooserPoly parms = EncryptionParameters() parms.set_poly_modulus("1x^4096 + 1") parms.set_coeff_modulus(seal.coeff_modulus_128(4096)) parms.set_plain_modulus(1 << 10) 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) A=[] X=[] n=int(input("Enter dimension: ")) for i in range(n): a=[] x=[] for j in range(n):