diff --git a/.gitignore b/.gitignore index 62c8935..a4c4443 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -.idea/ \ No newline at end of file +.idea/ +*.pyc +venv/ +myenv/ +__pycache__/ diff --git a/DemoCodes/ExampleTestSSE/Data/.gitkeep b/DemoCodes/ExampleTestSSE/Data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/ExampleTestSSE/Data/AnaHorvat.json b/DemoCodes/ExampleTestSSE/Data/AnaHorvat.json new file mode 100644 index 0000000..f38b77d --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Data/AnaHorvat.json @@ -0,0 +1,22 @@ +{ + "type": "patient", + "personal": { + "birthDate": "17. 11. 1975", + "firstName": "Ana", + "lastName": "Horvat", + "address": { + "street": "Mariborska cesta", + "houseNumber": "300", + "city": "Maribor", + "country": "Slovenia", + "postCode": "2000" + } + }, + "health": { + "temperature": "36.1", + "heartRate": "66", + "systolic": "133", + "diastolic": "77", + "spO2": "97" + } +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Data/JanezNovak.json b/DemoCodes/ExampleTestSSE/Data/JanezNovak.json new file mode 100644 index 0000000..70ff921 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Data/JanezNovak.json @@ -0,0 +1,22 @@ +{ + "type": "patient", + "personal": { + "birthDate": "19. 09. 1950", + "firstName": "Janez", + "lastName": "Novak", + "address": { + "street": "\u010copova ulica", + "houseNumber": "25", + "city": "Ljubljana", + "country": "Slovenia", + "postCode": "1000" + } + }, + "health": { + "temperature": "36.4", + "heartRate": "72", + "systolic": "140", + "diastolic": "88", + "spO2": "98" + } +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Data/MarkoZeman.json b/DemoCodes/ExampleTestSSE/Data/MarkoZeman.json new file mode 100644 index 0000000..c282804 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Data/MarkoZeman.json @@ -0,0 +1,22 @@ +{ + "type": "patient", + "personal": { + "birthDate": "25. 08. 1995", + "firstName": "Marko", + "lastName": "Zeman", + "address": { + "street": "TKS", + "houseNumber": "15", + "city": "Ljubljana", + "country": "Slovenia", + "postCode": "1000" + } + }, + "health": { + "temperature": "35.9", + "heartRate": "50", + "systolic": "115", + "diastolic": "75", + "spO2": "99" + } +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/IVs/ivs.json b/DemoCodes/ExampleTestSSE/Private/IVs/ivs.json new file mode 100644 index 0000000..d41ffab --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/IVs/ivs.json @@ -0,0 +1,5 @@ +{ + "0": "bn\u00d6\u00beT\u00cf\u00ceG\u0006j\u0088\u0011F\u001e\u00b0{", + "1": "\u00ef\u00fc`\u0087\u00a7\u00d4\u00aaS\u00ff\u0082\u0012\u00ce\u00e97\u00cbk", + "2": "\u0019k,7\u00ba\u0086\u00e2\u001e\u008cBr\u0003\u00d9F\u00bd\u000e" +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/document_index.json b/DemoCodes/ExampleTestSSE/Private/document_index.json new file mode 100644 index 0000000..9d17637 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/document_index.json @@ -0,0 +1,6 @@ +{ + "AnaHorvat.json": 0, + "JanezNovak.json": 1, + "MarkoZeman.json": 2, + "current_value": 3 +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/inverted_index.json b/DemoCodes/ExampleTestSSE/Private/inverted_index.json new file mode 100644 index 0000000..1bb5e69 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/inverted_index.json @@ -0,0 +1,116 @@ +{ + "health//diastolic//75": [ + 2 + ], + "health//diastolic//77": [ + 0 + ], + "health//diastolic//88": [ + 1 + ], + "health//heartRate//50": [ + 2 + ], + "health//heartRate//66": [ + 0 + ], + "health//heartRate//72": [ + 1 + ], + "health//spO2//97": [ + 0 + ], + "health//spO2//98": [ + 1 + ], + "health//spO2//99": [ + 2 + ], + "health//systolic//115": [ + 2 + ], + "health//systolic//133": [ + 0 + ], + "health//systolic//140": [ + 1 + ], + "health//temperature//35.9": [ + 2 + ], + "health//temperature//36.1": [ + 0 + ], + "health//temperature//36.4": [ + 1 + ], + "personal//address//city//Ljubljana": [ + 1, + 2 + ], + "personal//address//city//Maribor": [ + 0 + ], + "personal//address//country//Slovenia": [ + 0, + 1, + 2 + ], + "personal//address//houseNumber//15": [ + 2 + ], + "personal//address//houseNumber//25": [ + 1 + ], + "personal//address//houseNumber//300": [ + 0 + ], + "personal//address//postCode//1000": [ + 1, + 2 + ], + "personal//address//postCode//2000": [ + 0 + ], + "personal//address//street//Mariborska cesta": [ + 0 + ], + "personal//address//street//TKS": [ + 2 + ], + "personal//address//street//\u010copova ulica": [ + 1 + ], + "personal//birthDate//17. 11. 1975": [ + 0 + ], + "personal//birthDate//19. 09. 1950": [ + 1 + ], + "personal//birthDate//25. 08. 1995": [ + 2 + ], + "personal//firstName//Ana": [ + 0 + ], + "personal//firstName//Janez": [ + 1 + ], + "personal//firstName//Marko": [ + 2 + ], + "personal//lastName//Horvat": [ + 0 + ], + "personal//lastName//Novak": [ + 1 + ], + "personal//lastName//Zeman": [ + 2 + ], + "type//patient": [ + 0, + 1, + 2 + ] +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/keys/.gitkeep b/DemoCodes/ExampleTestSSE/Private/keys/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/ExampleTestSSE/Private/keys/document_key b/DemoCodes/ExampleTestSSE/Private/keys/document_key new file mode 100644 index 0000000..7fb4b75 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/keys/document_key @@ -0,0 +1 @@ +=fE^mǞdt䞽";( \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/keys/document_key.txt b/DemoCodes/ExampleTestSSE/Private/keys/document_key.txt new file mode 100644 index 0000000..ac5475f --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/keys/document_key.txt @@ -0,0 +1 @@ +=fÂE‰^ÿmÄê㮸ÎǞdt¤ð䞽°"«È;(ý \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/keys/index_key b/DemoCodes/ExampleTestSSE/Private/keys/index_key new file mode 100644 index 0000000..1d7eda4 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/keys/index_key @@ -0,0 +1 @@ +B:^nr,s1^pzb# \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/keys/index_key.txt b/DemoCodes/ExampleTestSSE/Private/keys/index_key.txt new file mode 100644 index 0000000..db96533 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/keys/index_key.txt @@ -0,0 +1 @@ +éB:^ÿónrºã,s1^°p䰏zÑb#þ¡ \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Private/values_index.json b/DemoCodes/ExampleTestSSE/Private/values_index.json new file mode 100644 index 0000000..1f78164 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Private/values_index.json @@ -0,0 +1,174 @@ +{ + "health//diastolic//": [ + [ + "75", + 1 + ], + [ + "77", + 1 + ], + [ + "88", + 1 + ] + ], + "health//heartRate//": [ + [ + "50", + 1 + ], + [ + "66", + 1 + ], + [ + "72", + 1 + ] + ], + "health//spO2//": [ + [ + "97", + 1 + ], + [ + "98", + 1 + ], + [ + "99", + 1 + ] + ], + "health//systolic//": [ + [ + "115", + 1 + ], + [ + "133", + 1 + ], + [ + "140", + 1 + ] + ], + "health//temperature//": [ + [ + "35.9", + 1 + ], + [ + "36.1", + 1 + ], + [ + "36.4", + 1 + ] + ], + "personal//address//city//": [ + [ + "Ljubljana", + 2 + ], + [ + "Maribor", + 1 + ] + ], + "personal//address//country//": [ + [ + "Slovenia", + 3 + ] + ], + "personal//address//houseNumber//": [ + [ + "15", + 1 + ], + [ + "25", + 1 + ], + [ + "300", + 1 + ] + ], + "personal//address//postCode//": [ + [ + "1000", + 2 + ], + [ + "2000", + 1 + ] + ], + "personal//address//street//": [ + [ + "Mariborska cesta", + 1 + ], + [ + "TKS", + 1 + ], + [ + "\u010copova ulica", + 1 + ] + ], + "personal//birthDate//": [ + [ + "17. 11. 1975", + 1 + ], + [ + "19. 09. 1950", + 1 + ], + [ + "25. 08. 1995", + 1 + ] + ], + "personal//firstName//": [ + [ + "Ana", + 1 + ], + [ + "Janez", + 1 + ], + [ + "Marko", + 1 + ] + ], + "personal//lastName//": [ + [ + "Horvat", + 1 + ], + [ + "Novak", + 1 + ], + [ + "Zeman", + 1 + ] + ], + "type//": [ + [ + "patient", + 3 + ] + ] +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/SSE/src/OPE.py b/DemoCodes/ExampleTestSSE/SSE/src/OPE.py new file mode 100644 index 0000000..026f72b --- /dev/null +++ b/DemoCodes/ExampleTestSSE/SSE/src/OPE.py @@ -0,0 +1,246 @@ +from shutil import copy +from functools import cmp_to_key + +from help_functions import * + +from SSE import SSE + + +class OPE: + def __init__(self): + self.sse = SSE() + + def create_inverted_keyword_index(self): + index_json = read_json_file(get_longer_path('doc_index')) + + all_words = [] + keyword_index = {} + files = os.listdir(get_longer_path('data')) + for file in files: + json = read_json_file(get_longer_path('data') + file) + + content = self.recursive_json(json, [], []) + + keyword_index[index_json[file]] = content + all_words += content + + all_words = list(set(all_words)) # make words list unique + + inverted_index = {word: [txt for txt, words in keyword_index.items() if word in words] for word in all_words} + + # sort according to data type + ordered_inverted_index = OrderedDict(sorted(inverted_index.items(), key=cmp_to_key(sort_values))) + + write_obj_to_json_file(ordered_inverted_index, get_longer_path('inverted_index')) + + + def create_index_of_values(self): + inverted_index = read_ordered_json_file(get_longer_path('inverted_index')) + + index_of_values = {} + for key, val in inverted_index.items(): + split_key = key.split('//') + property_path = '//'.join(split_key[:-1]) + '//' + value = split_key[-1] + num_of_values = len(val) + + if (property_path in index_of_values): + index_of_values[property_path].append([value, num_of_values]) + else: + index_of_values[property_path] = [[value, num_of_values]] + + write_obj_to_json_file(index_of_values, get_longer_path('values_index')) + + + def recursive_json(self, json, path, file_content): + for key, value in json.items(): + curr_path = path[:] + if not isinstance(value, dict): + path.extend((str(key), str(value))) + file_content.append('//'.join(path)) + else: + path.append(str(key)) + self.recursive_json(value, path, file_content) + path = curr_path + return file_content + + + def encrypt_index(self): + inverted_index = read_ordered_json_file(get_longer_path('inverted_index')) + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + encrypted_index = [] + encrypted_index.append(('number_of_files', len(os.listdir(get_longer_path('data'))))) + for word, document_ids in inverted_index.items(): + for doc_id in document_ids: + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + ciphertext = bytes_2_string(self.sse.encrypt(index_key, iv, [word])[0]) + encrypted_index.append((ciphertext, doc_id)) + + ordered_encrypted_index = OrderedDict(encrypted_index) + # print(ordered_encrypted_index) + + write_obj_to_json_file(ordered_encrypted_index, get_longer_path('encrypted_index')) + + + def encrypt_documents(self): + # files_source is path to directory with files + files_source = get_longer_path('data') + # files_destination is path to directory with newly encrypted files + files_destination = get_longer_path('server') + + key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + doc_index = read_json_file(get_longer_path('doc_index')) + + files = os.listdir(files_source) + for file in files: + json = read_ordered_json_file(files_source + file) + + doc_id = doc_index[file] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + encryption_list = self.recursive_encryption(json, [], [], key, iv) + + # print(file) + # print(type(encryption_list), encryption_list) + # print('+++++++++++++++++++++++++++++++++++') + + enc_list = '|||'.join(encryption_list) + # print(enc_list) + + new_path = files_destination + file.split('.')[0] + '.txt' + write_to_file(new_path, repr(enc_list)) + + + def recursive_encryption(self, json, path, lst, doc_key, iv): + for key, value in json.items(): + saved_path = path[:] + curr_path = str(list(json.keys()).index(key)) + path.append(curr_path) + cipherbytes = self.sse.encrypt(doc_key, iv, [key]) + ciphertext = bytes_2_string(cipherbytes[0]) + lst.append(''.join(path) + '--' + ciphertext) + + if isinstance(value, dict): + self.recursive_encryption(value, path, lst, doc_key, iv) + else: + path.append('*') + cipherbytes = self.sse.encrypt(doc_key, iv, [value]) + ciphertext = bytes_2_string(cipherbytes[0]) + lst.append(''.join(path) + '--' + ciphertext) + + path = saved_path + + return lst + + + def generate_search_token(self, keyword): + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + search_token = [] + for doc_id, iv in ivs.items(): + search_token += self.sse.encrypt(index_key, string_2_bytes(iv, 'latin-1'), [keyword]) + + return search_token + + + def search(self, search_token, operator, position): + # possible operators: 'eq', 'ne', 'gt', 'gte', 'lt', 'lte' + encrypted_index = read_ordered_json_file(get_longer_path('encrypted_index')) + str_search_token = [bytes_2_string(token) for token in search_token] + + doc_ids2return = get_docs2return(encrypted_index, str_search_token, operator, position) + + return set(doc_ids2return) + + + def copy_encrypted_files_to_user(self, doc_ids2return): + doc_index_switched = read_json_file(get_longer_path('doc_index_switched')) + + print(doc_ids2return) + + for doc_id in doc_ids2return: + file = doc_index_switched[str(doc_id)] + filepath = get_longer_path('server') + file.split('.')[0] + '.txt' + # copy encrypted file to user + copy(filepath, get_longer_path('user_enc')) + + return len(doc_ids2return) + + + def decrypt_documents(self): + doc_index = read_json_file(get_longer_path('doc_index')) + doc_key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + + files = os.listdir(get_longer_path('user_enc')) + + for file in files: + file_name = str(file.split('.')[0]) + '.json' + + doc_id = doc_index[file_name] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + content = read_file_string(get_longer_path('user_enc') + file).split('|||') + + ordered_dict = OrderedDict() + for item in content: + item_split = item.split('--') + item_num = item_split[0] + item_hash = string_2_bytes(item_split[1], 'latin-1') + item_hash = remove_double_backslashes(item_hash) + + decrypted_hash = self.sse.decrypt(doc_key, iv, [item_hash]) + + ordered_dict[item_num] = decrypted_hash[0] + + json = make_json_from_decrypted_file(ordered_dict) + + # write to decrypted folder + write_obj_to_json_file(json, get_longer_path('user_dec') + file_name) + + + def delete_user_directories(self): + enc_path = get_longer_path('user_enc') + dec_path = get_longer_path('user_dec') + enc_files = os.listdir(enc_path) + dec_files = os.listdir(dec_path) + [os.remove(enc_path + f) for f in enc_files] + [os.remove(dec_path + f) for f in dec_files] + + + + +if __name__ == '__main__': + ope = OPE() + + # ope.create_inverted_keyword_index() + # ope.create_index_of_values() + # ope.encrypt_index() + # ope.encrypt_documents() + + # ope.delete_user_directories() + # + # token = ope.generate_search_token(path_strings('heartRate') + '50') + # doc_ids_1 = ope.search(token, 'gt') + # print(doc_ids_1) + + # token = ope.generate_search_token(path_strings('spO2') + '99') + # doc_ids_2 = ope.search(token, 'eq') + # print(doc_ids_2) + # + # intersection = doc_ids_1.intersection(doc_ids_2) + # print('intersection', intersection) + + # union = doc_ids_1.union(doc_ids_2) + # print('union', union) + + + # ope.copy_encrypted_files_to_user(union) + # ope.decrypt_documents() + + + diff --git a/DemoCodes/ExampleTestSSE/SSE/src/SSE.py b/DemoCodes/ExampleTestSSE/SSE/src/SSE.py new file mode 100644 index 0000000..968cb7e --- /dev/null +++ b/DemoCodes/ExampleTestSSE/SSE/src/SSE.py @@ -0,0 +1,417 @@ +from Crypto.Cipher import AES + +from shutil import copy + +from help_functions import * + + +class SSE: + def __init__(self): + # self.generate_and_save_keys() + # self.update_IVs_and_doc_index() + pass + + def generate_and_save_keys(self): + self.index_key = get_random_bytes(32) + self.document_key = get_random_bytes(32) + + # write keys to binary files + keys = [self.index_key, self.document_key] + paths = [get_longer_path('index_key'), get_longer_path('document_key')] + write_keys_to_file(keys, paths, bin=True) + + # write keys to text files + keys = [bytes_2_string(self.index_key), bytes_2_string(self.document_key)] + paths = [get_longer_path('index_key_txt'), get_longer_path('document_key_txt')] + write_keys_to_file(keys, paths) + + def update_IVs_and_doc_index(self): + obj, changed_ids = self.make_document_index(get_longer_path('data')) + + curr_ivs = read_json_file(get_longer_path('ivs')) + + # -1 because of 'current_value' key + if (len(obj)-1 == len(curr_ivs)): + print('IVs are already up to date.') + return + + diff = len(obj)-1 - len(curr_ivs) + if (diff > 0): + for i in range(diff): + iv = get_random_bytes(16) + curr_ivs[changed_ids[i]] = bytes_2_string(iv) + else: + for ch_id in changed_ids: + curr_ivs.pop(str(ch_id), None) + + write_obj_to_json_file(curr_ivs, get_longer_path('ivs')) + + def make_document_index(self, path): + files = os.listdir(path) + + curr_json = read_json_file(get_longer_path('doc_index')) + + # -1 because of 'current_value' key + if (len(files) == len(curr_json) - 1): + print('Document index is already up to date.') + return [curr_json, []] + + # update json file + changed_ids = [] + current_value = curr_json['current_value'] if 'current_value' in curr_json else 0 + if (len(files) > len(curr_json) - 1): + for i in range(len(files)): + if (files[i] not in curr_json): + curr_json[files[i]] = current_value + changed_ids.append(current_value) + current_value += 1 + curr_json['current_value'] = current_value + else: + delete_keys = [] + for key, value in curr_json.items(): + if (key not in files and key != 'current_value'): + delete_keys.append(key) + changed_ids.append(value) + + for del_key in delete_keys: + curr_json.pop(del_key, None) + + # save updated version + write_obj_to_json_file(curr_json, get_longer_path('doc_index')) + + # update switched document index + self.create_switched_document_index() + + return [curr_json, changed_ids] + + def encrypt(self, key, IV, message): + # key and IV are bytes, message is list of strings + # returns list of bytes + aes = AES.new(key, AES.MODE_OFB, IV) + + ciphertext = [] + for m in message: + cipher = aes.encrypt(pad(string_2_bytes(m, 'utf-8'))) + ciphertext.append(cipher) + return ciphertext + + def decrypt(self, key, IV, ciphertext): + # key and IV are bytes, ciphertext is list of bytes + # returns string + aes = AES.new(key, AES.MODE_OFB, IV) + + plaintext = [] + for cipher in ciphertext: + if (len(cipher) > 0): + plain = unpad(aes.decrypt(cipher)).decode() + plaintext.append(plain) + else: + print('Cipher has no length.') + return plaintext + + def create_switched_document_index(self): + curr_json = read_json_file(get_longer_path('doc_index')) + switched_json = {value: key for key, value in curr_json.items() if (key != 'current_value')} + write_obj_to_json_file(switched_json, get_longer_path('doc_index_switched')) + + def create_inverted_keyword_index(self, files_destination): + # files_destination is path to directory with files + index_json = read_json_file(get_longer_path('doc_index')) + + all_words = [] + keyword_index = {} + files = os.listdir(files_destination) + for file in files: + content = read_file(files_destination + file) + # print(file, content) + keyword_index[index_json[file]] = content + all_words += content + + all_words = list(set(all_words)) # make words list unique + # print(keyword_index) + # print(all_words) + + inverted_index = {word: [txt for txt, words in keyword_index.items() if word in words] for word in all_words} + # print(inverted_index) + + write_obj_to_json_file(inverted_index, get_longer_path('inverted_index')) + + def update_inverted_keyword_index(self, changed_files): + # can apply to only new files + # changed_files is a list of strings + index_json = read_json_file(get_longer_path('doc_index')) + + all_words = [] + new_keyword_index = {} + for path in changed_files: + content = read_file(get_longer_path('data') + path) + # print(path, content) + new_keyword_index[index_json[path]] = content + all_words += content + + all_words = list(set(all_words)) # make words list unique + + curr_inverted_index = read_json_file(get_longer_path('inverted_index')) + new_inverted_index = {word: [txt for txt, words in new_keyword_index.items() if word in words] for word in all_words} + # print(curr_inverted_index) + # print(new_inverted_index) + + for key, value in new_inverted_index.items(): + if (key in curr_inverted_index): + curr_inverted_index[key] += value + curr_inverted_index[key] = list(set(curr_inverted_index[key])) + else: + curr_inverted_index[key] = value + + # print(curr_inverted_index) + write_obj_to_json_file(curr_inverted_index, get_longer_path('inverted_index')) + + def encrypt_index(self): + inverted_index = read_json_file(get_longer_path('inverted_index')) + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + encrypted_index = {} + for word, document_ids in inverted_index.items(): + for doc_id in document_ids: + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + ciphertext = bytes_2_string(self.encrypt(index_key, iv, [word])[0]) + encrypted_index[ciphertext] = doc_id + # print(len(encrypted_index), encrypted_index) + + write_obj_to_json_file(encrypted_index, get_longer_path('encrypted_index')) + + def encrypt_documents(self, files_source, files_destination): + # files_source is path to directory with files + # files_destination is path to directory with newly encrypted files + + key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + doc_index = read_json_file(get_longer_path('doc_index')) + + files = os.listdir(files_source) + for file in files: + content = read_file(files_source + file) + + doc_id = doc_index[file] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + cipherbytes = self.encrypt(key, iv, content) + + ciphertext = [bytes_2_string(cb) for cb in cipherbytes] + ciphertext = ' '.join(map(str, ciphertext)) + + new_path = files_destination + file + write_to_file(new_path, ciphertext) + + def generate_search_token(self, keyword): + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + search_token = [] + + for doc_id, iv in ivs.items(): + search_token += self.encrypt(index_key, string_2_bytes(iv, 'latin-1'), [keyword]) + + return search_token + + def search(self, search_token): + encrypted_index = read_json_file(get_longer_path('encrypted_index')) + doc_index_switched = read_json_file(get_longer_path('doc_index_switched')) + str_search_token = [bytes_2_string(token) for token in search_token] + + doc_ids2return = [encrypted_index[token] for token in str_search_token if token in encrypted_index] + print(doc_ids2return) + + for doc_id in doc_ids2return: + file = doc_index_switched[str(doc_id)] + filepath = get_longer_path('server') + file + # copy encrypted file to user + copy(filepath, get_longer_path('user_enc')) + + def decrypt_documents(self): + doc_index = read_json_file(get_longer_path('doc_index')) + doc_key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + + files = os.listdir(get_longer_path('user_enc')) + + for file in files: + content = enc_read_file(get_longer_path('user_enc') + file) + content = [string_2_bytes(c, 'latin-1') for c in content] + content = repair_data(content) + + doc_id = doc_index[file] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + plaintext = self.decrypt(doc_key, iv, content) + + stripped_plaintext = [] + for p in plaintext: + if not p.isalnum() and len(p) > 1: + strip_p = re.sub(r'[^0-9a-žA-Ž\-]+', '', p) + stripped_plaintext.append(strip_p) + else: + stripped_plaintext.append(p) + + stripped_plaintext = ' '.join(map(str, stripped_plaintext)) + + # write to decrypted folder + write_to_file(get_longer_path('user_dec') + file, stripped_plaintext) + + def delete_user_directories(self): + enc_path = get_longer_path('user_enc') + dec_path = get_longer_path('user_dec') + enc_files = os.listdir(enc_path) + dec_files = os.listdir(dec_path) + [os.remove(enc_path + f) for f in enc_files] + [os.remove(dec_path + f) for f in dec_files] + + def delete_server_text_files(self): + server_path = get_longer_path('server') + text_files = os.listdir(server_path) + [os.remove(server_path + txt) for txt in text_files if txt.endswith(".txt")] + + + + +if __name__ == '__main__': + sse = SSE() + + # sse.generate_and_save_keys() + # sse.update_IVs_and_doc_index() + # sse.create_switched_document_index() + # sse.create_inverted_keyword_index(get_longer_path('data')) + # sse.encrypt_index() + # sse.encrypt_documents(get_longer_path('data'), get_longer_path('server')) + + # sse.delete_user_directories() + + # token = sse.generate_search_token('kruh') + # sse.search(token) + # sse.decrypt_documents() + + + + # sse.delete_user_directories() + # sse.delete_server_text_files() + + + + + ''' + key = read_bin_file(get_longer_path('document_key')) + + res = read_json_file(get_longer_path('ivs')) + # print(res) + iv = string_2_bytes(res['2'], 'latin-1') + + # mess = read_file(get_longer_path('data') + 'text_SLO_ZGO.txt') + # print('mess', mess) + + a = sse.encrypt(key, iv, ['personal//name//Marko']) + b = sse.encrypt(key, iv, ['personal//name//Janez']) + + dec_A = sse.decrypt(key, get_random_bytes(16), a) + dec_B = sse.decrypt(key, iv, b) + + print(a) + print(b) + print(dec_A) + print(dec_B) + + + cipher = sse.encrypt(key, iv, mess) + print('cipher', cipher) + + print(len(mess), len(cipher)) + + ciphertext = [bytes_2_string(c) for c in cipher] + ciphertext = ' '.join(map(str, ciphertext)) + # print('ciphertext') + # print(ciphertext) + + write_to_file('../testing.txt', ciphertext) + + content = enc_read_file('../testing.txt') + # print('read content') + # print(content) + + content = [string_2_bytes(c, 'latin-1') for c in content] + # print('\ncontent') + # print(content) + # print('len content: ', len(content)) + + repaired = [] + i = 0 + while (i < len(content)): + val = content[i] + if (i < len(content)-1): + next_val = content[i+1] + if (len(val) == 15 and len(next_val) != 0): + if (b'\n' in val): + new_val = val.replace(b'\n', b'\r\n') + else: + new_val = b' ' + val + + # print('\nval', val) + # print('rep', new_val) + # print(new_val == val) + # print(len(next_val)) + + repaired.append(new_val) + i += 1 + continue + elif not (len(val) / 16).is_integer(): + # print(i) + # print(val, next_val) + repaired.append(val + string_2_bytes(' ', 'latin-1') + next_val) + i += 1 + elif (len(val) != 0): + repaired.append(val) + i += 1 + + print('\n\nrepaired', len(repaired), repaired) + + # repaired[87] = b' \xa9*\xf4U,\xa2V\xc2\x8a\xa8tf=E\xf3' + + + # for i in range(len(repaired)): + # if (len(repaired[i]) != len(cipher[i])): + # print(i) + # print(len(repaired[i]), len(cipher[i])) + # print(repaired[i], cipher[i]) + # print(mess[i]) + # print() + + # print(len(cipher)) + # print(len(repaired)) + # print(len(cipher) == len(repaired)) + + + sse_2 = SSE() + plain = sse_2.decrypt(key, iv, repaired) + print(plain) + ''' + + + + ''' + key = read_bin_file(get_longer_path('document_key')) + + res = read_json_file(get_longer_path('ivs')) + # print(res) + iv = string_2_bytes(res['2'], 'latin-1') + + mess = ['riž', 'bučka', 'češnja'] + + cipher = sse.encrypt(key, iv, mess) + print(cipher) + + + sse_2 = SSE() + + plain = sse_2.decrypt(key, iv, cipher) + print(plain) + ''' + diff --git a/DemoCodes/ExampleTestSSE/SSE/src/__init__.py b/DemoCodes/ExampleTestSSE/SSE/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/ExampleTestSSE/SSE/src/help_functions.py b/DemoCodes/ExampleTestSSE/SSE/src/help_functions.py new file mode 100644 index 0000000..9a777ce --- /dev/null +++ b/DemoCodes/ExampleTestSSE/SSE/src/help_functions.py @@ -0,0 +1,450 @@ +import os +import json +import re +import ast +import math +import bisect +from collections import OrderedDict +from dateutil.parser import parse + + +def remove_double_backslashes(b): + return ast.literal_eval(str(b).replace('\\\\', '\\')) + + +def write_to_file(filepath, content): + f = open(filepath, 'w', encoding="utf-8") + f.write(content) + f.close() + + +def write_to_bin_file(filepath, content): + f = open(filepath, 'wb') + f.write(content) + f.close() + + +def read_file(filepath): + regex_remove = re.compile('[,\.\[\]!?():;]') + with open(filepath) as f: + content = f.read().split() + content = [regex_remove.sub('', c) for c in content] + return content + + +def enc_read_file(filepath): + with open(filepath) as f: + content = f.read().split(' ') + # content = [c.replace('\n', '\r') for c in content] + return content + + +def read_bin_file(filepath): + with open(filepath, "rb") as f: + B = [] + byte = f.read(1) + B.append(byte) + while byte != b"": + byte = f.read(1) + B.append(byte) + return b''.join(B) + + +def read_file_string(filepath): + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read().strip("'\"") + return content + + +def pad(data): + length = 16 - (len(data) % 16) + data += bytes([length]) * length + return data + + +def unpad(data): + return data[:-data[-1]] + + +def get_random_bytes(length): + return os.urandom(length) + + +def bytes_2_string(B): + return B.decode("latin-1") + + +def string_2_bytes(s, encoding): + return bytes(s, encoding) + + +def write_keys_to_file(keys, filepaths, bin=False): + if (len(keys) != len(filepaths)): + raise SyntaxError('Keys and filepaths lists must have same length.') + for i in range(len(keys)): + if (bin): + write_to_bin_file(filepaths[i], keys[i]) + else: + write_to_file(filepaths[i], keys[i]) + + +def write_obj_to_json_file(obj, filepath): + with open(filepath, 'w') as outfile: + json.dump(obj, outfile, indent=4) + + +def read_json_file(filepath): + try: + with open(filepath) as data_file: + return json.load(data_file) + except IOError: + print('Cannot open file at: ', filepath) + return False + + +def read_ordered_json_file(filepath): + try: + with open(filepath) as data_file: + return json.load(data_file, object_pairs_hook=OrderedDict) + except IOError: + print('Cannot open file at: ', filepath) + return False + + +def repair_data(content): + repaired = [] + i = 0 + while (i < len(content)): + val = content[i] + if (i < len(content) - 1): + next_val = content[i + 1] + + if (len(val) == 15 and len(next_val) != 0): + if (b'\n' in val): + new_val = val.replace(b'\n', b'\r\n') + else: + new_val = b' ' + val + repaired.append(new_val) + elif not (len(val) / 16).is_integer(): + repaired.append(val + string_2_bytes(' ', 'latin-1') + next_val) + i += 1 + elif (len(val) != 0): + repaired.append(val) + i += 1 + + return repaired + + +def get_path(short_path): + paths = { + 'doc_index': os.path.join('..', 'Private', 'document_index.json'), + 'doc_index_switched': os.path.join('..', 'Server', 'document_index_switched.json'), + + 'inverted_index': os.path.join('..', 'Private', 'inverted_index.json'), + 'values_index': os.path.join('..', 'Private', 'values_index.json'), + + 'index_key': os.path.join('..', 'Private', 'keys', 'index_key'), + 'index_key_txt': os.path.join('..', 'Private', 'keys', 'index_key.txt'), + 'document_key': os.path.join('..', 'Private', 'keys', 'document_key'), + 'document_key_txt': os.path.join('..', 'Private', 'keys', 'document_key.txt'), + + 'ivs': os.path.join('..', 'Private', 'IVs', 'ivs.json'), + + 'encrypted_index': os.path.join('..', 'Server', 'encrypted_index.json'), + + 'data': os.path.join('..', 'Data') + os.sep, + 'server': os.path.join('..', 'Server') + os.sep, + 'user_enc': os.path.join('..', 'User', 'encrypted') + os.sep, + 'user_dec': os.path.join('..', 'User', 'decrypted') + os.sep, + } + return paths[short_path] + + +def get_longer_path(short_path): + return os.path.join('..', get_path(short_path)) + + +def make_json_from_decrypted_file(ordered_dict): + json = OrderedDict() + for key, value in ordered_dict.items(): + if (len(key) == 1): + json[value] = OrderedDict() + else: + last_char = key[-1] + if (last_char != '*'): + key_chars_without_last = key[:-1] + keys = [ordered_dict[key_chars_without_last[:(i + 1)]] for i in range(len(key_chars_without_last))] + curr_json = json + for k in keys: + curr_json = curr_json[k] + + curr_json[value] = OrderedDict() + + else: # final data + key_chars_without_last_2 = key[:-2] + keys = [ordered_dict[key_chars_without_last[:(i + 1)]] for i in range(len(key_chars_without_last_2))] + curr_json = json + for k in keys: + curr_json = curr_json[k] + + curr_json[ordered_dict[key[:-1]]] = value + + return json + + +def path_strings(string): + paths = { + 'birthDate': 'personal//birthDate//', + 'firstName': 'personal//firstName//', + 'lastName': 'personal//lastName//', + + 'houseNum': 'personal//address//houseNumber//', + 'street': 'personal//address//street//', + 'country': 'personal//address//country//', + 'city': 'personal//address//city//', + 'postCode': 'personal//address//postCode//', + + 'type': 'type//', + + 'temperature': 'health//temperature//', + 'heartRate': 'health//heartRate//', + 'diastolic': 'health//diastolic//', + 'systolic': 'health//systolic//', + 'spO2': 'health//spO2//', + } + return paths[string] + + +def get_docs2return(encrypted_index, str_search_token, operator, position): + if (operator == 'eq'): + doc_ids2return = [encrypted_index[token] for token in str_search_token if token in encrypted_index] + else: + num_of_files = encrypted_index['number_of_files'] + indices = [] + for token in str_search_token: + if (token in encrypted_index): + index = list(encrypted_index.keys()).index(token) + indices.append(index) + + indices.sort() + + if (operator == 'lt' or operator == 'gte' or operator == 'ne'): # for 'ne' it doesn't matter + index = indices[0] + elif (operator == 'gt' or operator == 'lte'): + index = indices[-1] + + if (index % num_of_files != 0): + start_index = math.floor(index / num_of_files) * num_of_files + 1 + end_index = math.ceil(index / num_of_files) * num_of_files + else: + start_index = index - num_of_files + 1 + end_index = index + + ordered_list = list(encrypted_index.items()) + + doc_ids2return = [] + if (position == 'exact'): + if (operator == 'ne'): + from_start_to_end = set([i for i in range(start_index, end_index + 1)]) + indices_set = set(indices) + not_equals = from_start_to_end - indices_set + for ind in not_equals: + doc_ids2return.append(ordered_list[ind][1]) + elif (operator == 'gt'): + for i in range(index + 1, end_index + 1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'gte'): + for i in range(index, end_index + 1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'lt'): + for i in range(index - 1, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'lte'): + for i in range(index, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + elif (position == 'left'): + if (operator == 'ne'): + from_start_to_end = set([i for i in range(start_index, end_index + 1)]) + for ind in from_start_to_end: + doc_ids2return.append(ordered_list[ind][1]) + elif (operator == 'gt' or operator == 'gte'): + for i in range(index, end_index + 1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'lt' or operator == 'lte'): + for i in range(index - 1, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + elif (position == 'right'): + if (operator == 'ne'): + from_start_to_end = set([i for i in range(start_index, end_index + 1)]) + for ind in from_start_to_end: + doc_ids2return.append(ordered_list[ind][1]) + elif (operator == 'gt' or operator == 'gte'): + doc_ids2return = [] + elif (operator == 'lt' or operator == 'lte'): + for i in range(index, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + + return doc_ids2return + + +def matched_brackets(string): + count = 0 + for i in string: + if i == "(": + count += 1 + elif i == ")": + count -= 1 + if count < 0: + return False + return count == 0 + + +def is_number(s): + try: + float(s) + return True + except ValueError: + return False + + +def is_date_format(string): + date_reg_exp = re.compile('\d{4}[-]\d{2}[-]\d{2}') + match = re.match(date_reg_exp, string) + if (match is not None): + try: + parse(string) + return True + except ValueError: + return False + return False + + +def operator_string(operator): + op = { + '=': 'eq', + '≠': 'ne', + '<': 'lt', + '≤': 'lte', + '>': 'gt', + '≥': 'gte', + } + return op[operator] + + +def combine_sets(set_1, set_2, operator): + if (operator == 'OR'): + return set_1.union(set_2) + elif (operator == 'AND'): + return set_1.intersection(set_2) + + +def solve_expression(exp): + ok = True + while (ok and len(exp) != 1): + # find ANDs + while ('AND' in exp): + and_index = exp.index('AND') + if (and_index-1 >= 0 and and_index+1 < len(exp)): + combination = combine_sets(exp[and_index - 1], exp[and_index + 1], 'AND') + del exp[and_index - 1:and_index + 2] + exp.insert(and_index - 1, combination) + else: + ok = False + break + + # find ORs + while ('OR' in exp): + or_index = exp.index('OR') + if (or_index-1 >= 0 and or_index+1 < len(exp)): + combination = combine_sets(exp[or_index - 1], exp[or_index + 1], 'OR') + del exp[or_index - 1:or_index + 2] + exp.insert(or_index - 1, combination) + else: + ok = False + break + + return exp + + +def find_closest_value(path_string, operator, value, value_type): + # 'left', 'right' or 'exact' tells position of chosen value to curr_val + pos = None + if (operator != '='): + values_index = read_json_file(get_longer_path('values_index')) + all_values = values_index[path_string] + all_values_1d = [float(v[0]) if is_number(v[0]) else v[0] for v in all_values] + value = float(value) if is_number(value) and value_type != 'string' else value + + index = bisect.bisect_left(all_values_1d, value) + if (index == len(all_values_1d)): + curr_val = all_values_1d[-1] + pos = 'right' + elif (all_values_1d[index] == value): + curr_val = value + pos = 'exact' + else: + curr_val = all_values_1d[index] + pos = 'left' + else: + curr_val = value + + if (value_type == 'int'): + curr_val = int(curr_val) + + return [str(curr_val), pos] + + +def sort_values(a, b): + a_path = a[0].split('//')[:-1] + a_val = a[0].split('//')[-1] + b_path = b[0].split('//')[:-1] + b_val = b[0].split('//')[-1] + + if (a_path == b_path): + if (is_number(a_val) and is_number(b_val)): + return float(a_val) - float(b_val) + else: + if (a_val < b_val): + return -1 + else: + return 1 + elif (a_path < b_path): + return -1 + else: + return 1 + + +def property_type(property): + t = { + 'birthDate': 'date', + 'firstName': 'string', + 'lastName': 'string', + + 'houseNum': 'int', + 'street': 'string', + 'country': 'string', + 'city': 'string', + 'postCode': 'int', + + 'type': 'string', + + 'temperature': 'float', + 'heartRate': 'int', + 'diastolic': 'int', + 'systolic': 'int', + 'spO2': 'int', + } + return t[property] + + +def type_of_var(string): + dates = ['birthDate'] + numbers = ['houseNum', 'postCode', 'temperature', 'heartRate', 'diastolic', 'systolic', 'spO2'] + strings = ['firstName', 'lastName', 'street', 'country', 'city', 'type'] + + if (string in dates): + return ['d', 'YYYY-MM-DD'] + elif (string in numbers): + return ['n', '36.2'] + elif (string in strings): + return ['s', 'some string'] diff --git a/src/create_patient_json.py b/DemoCodes/ExampleTestSSE/SSE/src/main00-create_patient_json.py similarity index 100% rename from src/create_patient_json.py rename to DemoCodes/ExampleTestSSE/SSE/src/main00-create_patient_json.py diff --git a/DemoCodes/ExampleTestSSE/SSE/src/main01.py b/DemoCodes/ExampleTestSSE/SSE/src/main01.py new file mode 100644 index 0000000..7638231 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/SSE/src/main01.py @@ -0,0 +1,5 @@ +from SSE import SSE +sse = SSE() + +sse.generate_and_save_keys() +sse.update_IVs_and_doc_index() \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/SSE/src/main02.py b/DemoCodes/ExampleTestSSE/SSE/src/main02.py new file mode 100644 index 0000000..cecc454 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/SSE/src/main02.py @@ -0,0 +1,7 @@ +from SSE import SSE +from OPE import OPE +sse = SSE() +ope = OPE() + +sse.create_switched_document_index() +ope.create_inverted_keyword_index() \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/SSE/src/main03.py b/DemoCodes/ExampleTestSSE/SSE/src/main03.py new file mode 100644 index 0000000..1ffd175 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/SSE/src/main03.py @@ -0,0 +1,6 @@ +from OPE import OPE +ope = OPE() + +ope.create_index_of_values() +ope.encrypt_index() +ope.encrypt_documents() \ No newline at end of file diff --git a/src/GUI.py b/DemoCodes/ExampleTestSSE/SSE/src/main04-GUI.py similarity index 100% rename from src/GUI.py rename to DemoCodes/ExampleTestSSE/SSE/src/main04-GUI.py diff --git a/DemoCodes/ExampleTestSSE/Server/.gitkeep b/DemoCodes/ExampleTestSSE/Server/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/ExampleTestSSE/Server/AnaHorvat.txt b/DemoCodes/ExampleTestSSE/Server/AnaHorvat.txt new file mode 100644 index 0000000..2926536 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Server/AnaHorvat.txt @@ -0,0 +1 @@ +'0--\x8cq\x88ï\x82¼TN\x86(ç»Sg\x03¹|||0*--\x88i\x8cãëÞ,K\x83-â¾Vb\x06¼|||1--\x88m\x8aùáÞ9.\x82,ã¿Wc\x07½|||10--\x9aa\x8aþæô96ï#ì°Xl\x08²|||10*--É?Öª¿\x81vb»\x1dÜ\x82[o\x0b±|||11--\x9ea\x8aùúþ9/ï#ì°Xl\x08²|||11*--¹f\x99\x87\x83½UO\x87)æºRf\x02¸|||12--\x94i\x8bþÀÑ5\'\x82,ã¿Wc\x07½|||12*--°g\x8aüïÄRH\x80.á½Ua\x05¿|||13--\x99l\x9cøëÃ+K\x83-â¾Vb\x06¼|||130--\x8b|\x8aïëÄRH\x80.á½Ua\x05¿|||130*--µi\x8aãìß*1áEËÔ:\x18{Ôó@L=\x81\x02À\x80DåÐ6Ïî"ý|||131--\x90g\x8dùëþ-/èA\x99²Zn\n°|||131*--Ë8È\x87\x83½UO\x87)æºRf\x02¸|||132--\x9ba\x8có\x82¼TN\x86(ç»Sg\x03¹|||132*--µi\x8aãìß*K\x83-â¾Vb\x06¼|||133--\x9bg\x8däúÂ!K\x83-â¾Vb\x06¼|||133*--«d\x97üëÞ1#\x82,ã¿Wc\x07½|||134--\x88g\x8bþÍß<\'\x82,ã¿Wc\x07½|||134*--Ê8Ⱥ\x82¼TN\x86(ç»Sg\x03¹|||2--\x90m\x99æúØRH\x80.á½Ua\x05¿|||20--\x8cm\x95úëÂ96ÿV\x8e²Zn\n°|||20*--Ë>Ö»\x82¼TN\x86(ç»Sg\x03¹|||21--\x90m\x99øúâ96ï#ì°Xl\x08²|||21*--Î>ö\x84\x80¾VL\x84*å¹Qe\x01»|||22--\x8bq\x8bþáÜ1!\x82,ã¿Wc\x07½|||22*--É;Ë\x87\x83½UO\x87)æºRf\x02¸|||23--\x9ca\x99ùúß4+é#ì°Xl\x08²|||23*--Ï?ö\x84\x80¾VL\x84*å¹Qe\x01»|||24--\x8bx·¸\x82¼TN\x86(ç»Sg\x03¹|||24*--Á?ö\x84\x80¾VL\x84*å¹Qe\x01»' \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Server/JanezNovak.txt b/DemoCodes/ExampleTestSSE/Server/JanezNovak.txt new file mode 100644 index 0000000..2e0fad8 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Server/JanezNovak.txt @@ -0,0 +1 @@ +'0--Hæõ\x87æPá\x05ÔõAXO\x00èx|||0*--Lþñ\x8b\x8f2\x99\x00ÑðD]J\x05í}|||1--Lú÷\x91\x852\x8ceÐñE\\K\x04ì||||10--^ö÷\x96\x82\x18\x8c}½þJSD\x0bãs|||10*--\r¦«ÂÚeÃ)éÀxdG\x08àp|||11--Zö÷\x91\x9e\x12\x8cd½þJSD\x0bãs|||11*--vþë\x87\x90Wæ\x02ÓòF_H\x07ï\x7f|||12--Pþö\x96¤=\x80lÐñE\\K\x04ì||||12*--rðó\x83\x81Wæ\x02ÓòF_H\x07ï\x7f|||13--]ûá\x90\x8f/\x9e\x00ÑðD]J\x05í}|||130--Oë÷\x87\x8f(ç\x03ÒóG^I\x06î~|||130*--ø\x13ê\x92\x85*\x8c)\xad\x95$7"\x0fçw|||131--Tðð\x91\x8f\x12\x98dº\x9c?QF\táq|||131*--\x0eª\x8bìäRã\x07Ö÷CZM\x02êz|||132--_öñ\x9bæPá\x05ÔõAXO\x00èx|||132*--põð\x80\x866\x8cg¹þJSD\x0bãs|||133--_ðð\x8c\x9e.\x94\x00ÑðD]J\x05í}|||133*--oóê\x94\x8f2\x84hÐñE\\K\x04ì||||134--Lðö\x96©3\x89lÐñE\\K\x04ì||||134*--\r¯µÒæPá\x05ÔõAXO\x00èx|||2--Túä\x8e\x9e4ç\x03ÒóG^I\x06î~|||20--Húè\x92\x8f.\x8c}\xad\x8b(QF\táq|||20*--\x0f©«ÖæPá\x05ÔõAXO\x00èx|||21--Túä\x90\x9e\x0e\x8c}½þJSD\x0bãs|||21*--\x0b\xad\x8bìäRã\x07Ö÷CZM\x02êz|||22--Oæö\x96\x850\x84jÐñE\\K\x04ì||||22*--\r«µïçQà\x04Õô@YN\x01éy|||23--Xöä\x91\x9e3\x81`»þJSD\x0bãs|||23*--\x04§\x8bìäRã\x07Ö÷CZM\x02êz|||24--OïÊÐæPá\x05ÔõAXO\x00èx|||24*--\x05§\x8bìäRã\x07Ö÷CZM\x02êz' \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Server/MarkoZeman.txt b/DemoCodes/ExampleTestSSE/Server/MarkoZeman.txt new file mode 100644 index 0000000..c46878b --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Server/MarkoZeman.txt @@ -0,0 +1 @@ +'0--»òÇ·\x1cºúwD¯\x8dº÷¹ÃÄ|||0*--¿êûuØ\x82rAª\x88¿ò¼ÆÁ|||1--¿îÅ¡\x7fØ\x97\x17@«\x89¾ó½ÇÀ|||10--\xadâŦxò\x97\x0f-¤\x86±ü²ÈÏ|||10*--ý¾\x99ò \x8eØ[y\x9a¸\x83ÿ±ËÌ|||11--©âÅ¡dø\x97\x16-¤\x86±ü²ÈÏ|||11*--\x82êŹ\x7f½ýpC¨\x8a½ð¾ÄÃ|||12--£êĦ^×\x9b\x1e@«\x89¾ó½ÇÀ|||12*--\x95îÚ³~½ýpC¨\x8a½ð¾ÄÃ|||13--®ïÓ\xa0uÅ\x85rAª\x88¿ò¼ÆÁ|||130--¼ÿÅ·uÂüqB©\x8b¼ñ¿ÅÂ|||130*--\x9bÀäß\x1d»ûvE®\x8c»ö¸ÂÅ|||131--§ä¡uø\x83\x16*Æó³þ°ÊÍ|||131*--þ¾¹Ü\x1e¸øuF\xad\x8f¸õ»ÁÆ|||132--¬âë\x1cºúwD¯\x8dº÷¹ÃÄ|||132*--\x83á°|Ü\x97\x15)¤\x86±ü²ÈÏ|||133--¬ä¼dÄ\x8frAª\x88¿ò¼ÆÁ|||133*--\x9cçØ¤uØ\x9f\x1a@«\x89¾ó½ÇÀ|||134--¿äĦSÙ\x92\x1e@«\x89¾ó½ÇÀ|||134*--þ»\x87â\x1cºúwD¯\x8dº÷¹ÃÄ|||2--§îÖ¾dÞüqB©\x8b¼ñ¿ÅÂ|||20--»îÚ¢uÄ\x97\x0f=Ñä³þ°ÊÍ|||20*--ü¾\x99ë\x1cºúwD¯\x8dº÷¹ÃÄ|||21--§îÖ\xa0dä\x97\x0f-¤\x86±ü²ÈÏ|||21*--ú»¹Ü\x1e¸øuF\xad\x8f¸õ»ÁÆ|||22--¼òĦ\x7fÚ\x9f\x18@«\x89¾ó½ÇÀ|||22*--þº\x82ß\x1d»ûvE®\x8c»ö¸ÂÅ|||23--«âÖ¡dÙ\x9a\x12+¤\x86±ü²ÈÏ|||23*--ø¾¹Ü\x1e¸øuF\xad\x8f¸õ»ÁÆ|||24--¼ûøà\x1cºúwD¯\x8dº÷¹ÃÄ|||24*--ö²¹Ü\x1e¸øuF\xad\x8f¸õ»ÁÆ' \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Server/document_index_switched.json b/DemoCodes/ExampleTestSSE/Server/document_index_switched.json new file mode 100644 index 0000000..56385ec --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Server/document_index_switched.json @@ -0,0 +1,5 @@ +{ + "0": "AnaHorvat.json", + "1": "JanezNovak.json", + "2": "MarkoZeman.json" +} \ No newline at end of file diff --git a/DemoCodes/ExampleTestSSE/Server/encrypted_index.json b/DemoCodes/ExampleTestSSE/Server/encrypted_index.json new file mode 100644 index 0000000..e4bcc77 --- /dev/null +++ b/DemoCodes/ExampleTestSSE/Server/encrypted_index.json @@ -0,0 +1,45 @@ +{ + "number_of_files": 3, + "\u0001\u0089\u0089\u0012\u00ce\u001c5\u00ac\u00da\u00ce\u00b7\u00b6\u00c6\u00a7\u00a8E\u001e\u00f4\u00a9\u00c8\u0003\u00a7\u00df\u00b5\u0002\u0081\u00e2\u000e+1f\u001d": 2, + "\u0080+\n\u00a9\n\u00ee\u00a1\u0006\u00ae\u0002\u00d6__B\u00d9\u00e9/Fbc\u0088\u00b7\u0088\u0092p\u00ac\t+\u00a0\bQM": 0, + "\u008c\u001b\"u\u00e9\u00a8C\u0097\u0083\f*\u00fa\u00dd\u00b4\u0083\u009c\u00e6\u00d1&D~7\u00bf\u00ba\"\u0085@\u00d9\u001cr\u009d\u009e": 1, + "\u0001\u0089\u0089\u0012\u00ce\u001c5\u00ac\u00d6\u00c2\u00b7\u00b7\u00c6\u009a\u00a5X\u0018\u00f4\u00a9\u00ca\u0006\u00a7\u00df\u00b5\u0002\u0081\u00e2\u000e+1f\u001d": 2, + "\u0080+\n\u00a9\n\u00ee\u00a1\u0006\u00a2\u000e\u00d6^_\u007f\u00d4\u00f4)Fbb\u0089\u00b7\u0088\u0092p\u00ac\t+\u00a0\bQM": 0, + "\u008c\u001b\"u\u00e9\u00a8C\u0097\u008f\u0000*\u00fb\u00dd\u0089\u008e\u0081\u00e0\u00d1&Kt7\u00bf\u00ba\"\u0085@\u00d9\u001cr\u009d\u009e": 1, + "\u0080+\n\u00a9\n\u00ee\u00a1\u0006\u00b9\u001b\u00f8\u001e\u0004\u0002\u008c\u00b7\\y]D\u00af\u00ac\u0093\u0089k\u00b7\u00120\u00bb\u0013JV": 0, + "\u008c\u001b\"u\u00e9\u00a8C\u0097\u0094\u0015\u0004\u00bb\u0086\u00f4\u00d6\u00cd\u0095\u00ee\u0019lV,\u00a4\u00a19\u009e[\u00c2\u0007i\u0086\u0085": 1, + "\u0001\u0089\u0089\u0012\u00ce\u001c5\u00ac\u00cd\u00d7\u0099\u00f7\u009d\u00e7\u00fd\u0015m\u00cb\u0096\u00ef&\u00bc\u00c4\u00ae\u0019\u009a\u00f9\u00150*}\u0006": 2, + "\u0001\u0089\u0089\u0012\u00ce\u001c5\u00ac\u00cd\u00de\u00a5\u00b1\u00dd\u00a4\u00adOR\u00f4\u00b7\u00ce\u0003\u00a7\u00df\u00b5\u0002\u0081\u00e2\u000e+1f\u001d": 2, + "\u0080+\n\u00a9\n\u00ee\u00a1\u0006\u00b9\u0012\u00c4XDA\u00dc\u00e3cF|g\u008c\u00b7\u0088\u0092p\u00ac\t+\u00a0\bQM": 0, + "\u008c\u001b\"u\u00e9\u00a8C\u0097\u0094\u001c8\u00fd\u00c6\u00b7\u0086\u0096\u00aa\u00d18Hv7\u00bf\u00ba\"\u0085@\u00d9\u001cr\u009d\u009e": 1, + "\u0001\u0089\u0089\u0012\u00ce\u001c5\u00ac\u00ca\u00c2\u00bb\u00b5\u00d7\u00ba\u00a5X\b\u00a9\u00e3\u00d0\u0019\u009f\u00e1\u00900\u008d\u00ee\u0002'=j\u0011": 2, + "\u0080+\n\u00a9\n\u00ee\u00a1\u0006\u00be\u000e\u00da\\N_\u00d4\u00f49\u001b({\u0090\u008f\u00b5\u00b7J\u00a0\u0005'\u00ac\u0004]A": 0, + "\u008c\u001b\"u\u00e9\u00a8C\u0097\u0093\u0000&\u00f9\u00cc\u00a9\u008e\u0081\u00f0\u008clSi\u000f\u0082\u009f\u001d\u0089L\u00d5\u0010~\u0091\u0092": 1, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J*\u00ed\u00cd\u00a9\u008a\u0086\u00f6\u00d1&\u001f/H\u00cd\u009e\u0006\u00c2!\u00a7u\u0015\u00fc\u00f4\u00d2\u00fa\u00a5\u00f9y\u00d1 %\u00e0\u00ad?|\u0096/\u00ba\u00e5": 1, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b7\u00a1\u00d6\u00ba\u00a1_\u000e\u00f4\u00a9\u009c_\u00d8\u00ad\u0091&\u00c6\u0083pBV\u0007wWv\u00b2\u00e1\u00b4W;/\u0099\f\u00f3\u00a8}*\u0093\u00b1": 2, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d6HO_\u00d0\u00f3?Fb7\u00d6\u00c8\u00fa\u00b6T\u00eacR\u00c2a54,\u0011\u00bd8D8:\u0015L\u00ed\u00d9$\u000f\u00d9\u00e5\u001f": 0, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d6HO_\u00d0\u00f3?Fb7\u00d0\u00c9\u00ed\u00ed\t\u00de-\u000f\u00f8o50Yo\u00c4IX$&\tP\u00f1\u00c58\u0013\u00c5\u00f9\u0003": 0, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J*\u00ed\u00cd\u00a9\u008a\u0086\u00f6\u00d1&\u001f)I\u00da\u00c5[\u00f7d\u00fdD\u0015\u00f9\u00e3\u00d9\u00f5\u00c2\u0096{\u00d3\"'\u00e2\u00af=~\u0094-\u00b8\u00e7": 1, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b7\u00a1\u00d6\u00ba\u00a1_\u000e\u00f4\u00a9\u009cY\u00d9\u00ba\u00ca{\u00f3\u00c6*sV\u0002`\\y\u00d5\u008e\u00b6U9-\u009b\u000e\u00f1\u00aa\u007f(\u0091\u00b3": 2, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b7\u00a1\u00d6\u00ba\u00a1_\u000e\u00f4\u00a9\u0097Y\u00d9\u00a7\u00dbG\u00ff\u0084gEHB9\b\"\u00b2\u00e1\u00b4W;/\u0099\f\u00f3\u00a8}*\u0093\u00b1": 2, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J*\u00ed\u00cd\u00a9\u008a\u0086\u00f6\u00d1&\u0014)I\u00c7\u00d4g\u00fb&\u00b0r\u000b\u00b9\u00ba\u008e\u00ae\u00a5\u00f9y\u00d1 %\u00e0\u00ad?|\u0096/\u00ba\u00e5": 1, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d6HO_\u00d0\u00f3?Fb<\u00d0\u00c9\u00f0\u00fc5\u00d2oB\u00cequi\u000f1\u009d%Y%'\bQ\u00f0\u00c49\u0012\u00c4\u00f8\u0002": 0, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J*\u00ed\u00cd\u00a9\u008a\u0086\u00f6\u00d1&\f)O\u00c0\u00f2F\u00ea.\u00fd8H\u00a6\u00a5\u008c\u0094\u00a4\u00f8x\u00d0!$\u00e1\u00ac>}\u0097.\u00bb\u00e4": 1, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b7\u00a1\u00d6\u00ba\u00a1_\u000e\u00f4\u00a9\u008fY\u00df\u00a0\u00fdf\u00ee\u008c*\u000f\u000b]&\t\u0018\u00b3\u00e0\u00b5V:.\u0098\r\u00f2\u00a9|+\u0092\u00b0": 2, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d6HO_\u00d0\u00f3?Fb$\u00d0\u00cf\u00f7\u00da\u0014\u00c3g\u000f\u00841jv\f\u000e\u00a2'['%\nS\u00f2\u00c6;\u0010\u00c6\u00fa\u0000": 0, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d6HO_\u00d0\u00f3?Fb'\u00cb\u00ce\u00e6\u00fc\u000f\u0088-m\u00caq3$Ss\u00deC5\bI`/\u0089\u00a81\u001a\u00cc\u00f0\n": 0, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b7\u00a1\u00d6\u00ba\u00a1_\u000e\u00f4\u00a9\u008cB\u00de\u00b1\u00db}\u00a5\u00c6Qkio\u0014": 2, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J*\u00ed\u00cd\u00a9\u008a\u0086\u00f6\u00d1&\u000f2N\u00d1\u00d4]\u00a1d\u0016\u009b\u0016\u00e6\u00fa\u00ca\u00fa\u008b\u0082\u001b\u00b6MJ\u00e6\u00ab9z\u0090)\u00bc\u00e3": 1, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d5EYY\u00dd\u00c4-\u001d({\u0090\u008d\u00b4\u00b7[\u00963\u000e\u008b2cq\t\u000e\u00a2'['%\nS\u00f2\u00c6;\u0010\u00c6\u00fa\u0000": 0, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J)\u00e0\u00db\u00af\u0087\u00b1\u00e4\u008alSi\r\u008d\u009f\t\u00ber\u00fc7H\u00af\u00a0\u008c\u0094\u00a4\u00f8x\u00d0!$\u00e1\u00ac>}\u0097.\u00bb\u00e4": 1, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b4\u00ac\u00c0\u00bc\u00ach\u001c\u00af\u00e3\u00d0\u0019\u009e\u00e1\u0090)\u00ba\u00d1+\u0000\u000bT/\f\u0018\u00b3\u00e0\u00b5V:.\u0098\r\u00f2\u00a9|+\u0092\u00b0": 2, + "\u0098+\u0019\u00b6\u0011\u00e8\u00efE\u00e5D\u00d1EY^\u00c1\u00ce-\u0004({\u0090\u00fd\u00ed\u00f8s\u00af\n(\u00a3\u000bRN": 0, + "\u0094\u001b1j\u00f2\u00ae\r\u00d4\u00c8J-\u00e0\u00db\u00a8\u009b\u00bb\u00e4\u0093lSiv\u00d5\u00dfL\u00f4M\u00d4\u0011\u007f\u0090\u0093": 1, + "\u0019\u0089\u009a\r\u00d5\u001a{\u00ef\u0091\u0088\u00b0\u00ac\u00c0\u00bb\u00b0b\u001c\u00b6\u00e3\u00d0\u0019\u00e1\u00b5\u00ccb\u00e5\u00ef\u0003&Ö»\x82¼TN\x86(ç»Sg\x03¹|||21--\x90m\x99øúâ96ï#ì°Xl\x08²|||21*--Î>ö\x84\x80¾VL\x84*å¹Qe\x01»|||22--\x8bq\x8bþáÜ1!\x82,ã¿Wc\x07½|||22*--É;Ë\x87\x83½UO\x87)æºRf\x02¸|||23--\x9ca\x99ùúß4+é#ì°Xl\x08²|||23*--Ï?ö\x84\x80¾VL\x84*å¹Qe\x01»|||24--\x8bx·¸\x82¼TN\x86(ç»Sg\x03¹|||24*--Á?ö\x84\x80¾VL\x84*å¹Qe\x01»' \ No newline at end of file diff --git a/DemoCodes/FreshCopy/Data/.gitkeep b/DemoCodes/FreshCopy/Data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/FreshCopy/Private/IVs/ivs.json b/DemoCodes/FreshCopy/Private/IVs/ivs.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/DemoCodes/FreshCopy/Private/IVs/ivs.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/DemoCodes/FreshCopy/Private/document_index.json b/DemoCodes/FreshCopy/Private/document_index.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/DemoCodes/FreshCopy/Private/document_index.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/DemoCodes/FreshCopy/Private/keys/.gitkeep b/DemoCodes/FreshCopy/Private/keys/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/FreshCopy/README.md b/DemoCodes/FreshCopy/README.md new file mode 100644 index 0000000..4c1caef --- /dev/null +++ b/DemoCodes/FreshCopy/README.md @@ -0,0 +1,9 @@ +# Searchable Symmetric Encryption +### Steps to run +* Go to `SSE/src/` +* Run serially + 1. main00-create_patient_json.py + 2. main01.py + 3. main02.py + 4. main03.py + 5. main04-GUI.py diff --git a/DemoCodes/FreshCopy/SSE/src/OPE.py b/DemoCodes/FreshCopy/SSE/src/OPE.py new file mode 100644 index 0000000..026f72b --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/OPE.py @@ -0,0 +1,246 @@ +from shutil import copy +from functools import cmp_to_key + +from help_functions import * + +from SSE import SSE + + +class OPE: + def __init__(self): + self.sse = SSE() + + def create_inverted_keyword_index(self): + index_json = read_json_file(get_longer_path('doc_index')) + + all_words = [] + keyword_index = {} + files = os.listdir(get_longer_path('data')) + for file in files: + json = read_json_file(get_longer_path('data') + file) + + content = self.recursive_json(json, [], []) + + keyword_index[index_json[file]] = content + all_words += content + + all_words = list(set(all_words)) # make words list unique + + inverted_index = {word: [txt for txt, words in keyword_index.items() if word in words] for word in all_words} + + # sort according to data type + ordered_inverted_index = OrderedDict(sorted(inverted_index.items(), key=cmp_to_key(sort_values))) + + write_obj_to_json_file(ordered_inverted_index, get_longer_path('inverted_index')) + + + def create_index_of_values(self): + inverted_index = read_ordered_json_file(get_longer_path('inverted_index')) + + index_of_values = {} + for key, val in inverted_index.items(): + split_key = key.split('//') + property_path = '//'.join(split_key[:-1]) + '//' + value = split_key[-1] + num_of_values = len(val) + + if (property_path in index_of_values): + index_of_values[property_path].append([value, num_of_values]) + else: + index_of_values[property_path] = [[value, num_of_values]] + + write_obj_to_json_file(index_of_values, get_longer_path('values_index')) + + + def recursive_json(self, json, path, file_content): + for key, value in json.items(): + curr_path = path[:] + if not isinstance(value, dict): + path.extend((str(key), str(value))) + file_content.append('//'.join(path)) + else: + path.append(str(key)) + self.recursive_json(value, path, file_content) + path = curr_path + return file_content + + + def encrypt_index(self): + inverted_index = read_ordered_json_file(get_longer_path('inverted_index')) + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + encrypted_index = [] + encrypted_index.append(('number_of_files', len(os.listdir(get_longer_path('data'))))) + for word, document_ids in inverted_index.items(): + for doc_id in document_ids: + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + ciphertext = bytes_2_string(self.sse.encrypt(index_key, iv, [word])[0]) + encrypted_index.append((ciphertext, doc_id)) + + ordered_encrypted_index = OrderedDict(encrypted_index) + # print(ordered_encrypted_index) + + write_obj_to_json_file(ordered_encrypted_index, get_longer_path('encrypted_index')) + + + def encrypt_documents(self): + # files_source is path to directory with files + files_source = get_longer_path('data') + # files_destination is path to directory with newly encrypted files + files_destination = get_longer_path('server') + + key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + doc_index = read_json_file(get_longer_path('doc_index')) + + files = os.listdir(files_source) + for file in files: + json = read_ordered_json_file(files_source + file) + + doc_id = doc_index[file] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + encryption_list = self.recursive_encryption(json, [], [], key, iv) + + # print(file) + # print(type(encryption_list), encryption_list) + # print('+++++++++++++++++++++++++++++++++++') + + enc_list = '|||'.join(encryption_list) + # print(enc_list) + + new_path = files_destination + file.split('.')[0] + '.txt' + write_to_file(new_path, repr(enc_list)) + + + def recursive_encryption(self, json, path, lst, doc_key, iv): + for key, value in json.items(): + saved_path = path[:] + curr_path = str(list(json.keys()).index(key)) + path.append(curr_path) + cipherbytes = self.sse.encrypt(doc_key, iv, [key]) + ciphertext = bytes_2_string(cipherbytes[0]) + lst.append(''.join(path) + '--' + ciphertext) + + if isinstance(value, dict): + self.recursive_encryption(value, path, lst, doc_key, iv) + else: + path.append('*') + cipherbytes = self.sse.encrypt(doc_key, iv, [value]) + ciphertext = bytes_2_string(cipherbytes[0]) + lst.append(''.join(path) + '--' + ciphertext) + + path = saved_path + + return lst + + + def generate_search_token(self, keyword): + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + search_token = [] + for doc_id, iv in ivs.items(): + search_token += self.sse.encrypt(index_key, string_2_bytes(iv, 'latin-1'), [keyword]) + + return search_token + + + def search(self, search_token, operator, position): + # possible operators: 'eq', 'ne', 'gt', 'gte', 'lt', 'lte' + encrypted_index = read_ordered_json_file(get_longer_path('encrypted_index')) + str_search_token = [bytes_2_string(token) for token in search_token] + + doc_ids2return = get_docs2return(encrypted_index, str_search_token, operator, position) + + return set(doc_ids2return) + + + def copy_encrypted_files_to_user(self, doc_ids2return): + doc_index_switched = read_json_file(get_longer_path('doc_index_switched')) + + print(doc_ids2return) + + for doc_id in doc_ids2return: + file = doc_index_switched[str(doc_id)] + filepath = get_longer_path('server') + file.split('.')[0] + '.txt' + # copy encrypted file to user + copy(filepath, get_longer_path('user_enc')) + + return len(doc_ids2return) + + + def decrypt_documents(self): + doc_index = read_json_file(get_longer_path('doc_index')) + doc_key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + + files = os.listdir(get_longer_path('user_enc')) + + for file in files: + file_name = str(file.split('.')[0]) + '.json' + + doc_id = doc_index[file_name] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + content = read_file_string(get_longer_path('user_enc') + file).split('|||') + + ordered_dict = OrderedDict() + for item in content: + item_split = item.split('--') + item_num = item_split[0] + item_hash = string_2_bytes(item_split[1], 'latin-1') + item_hash = remove_double_backslashes(item_hash) + + decrypted_hash = self.sse.decrypt(doc_key, iv, [item_hash]) + + ordered_dict[item_num] = decrypted_hash[0] + + json = make_json_from_decrypted_file(ordered_dict) + + # write to decrypted folder + write_obj_to_json_file(json, get_longer_path('user_dec') + file_name) + + + def delete_user_directories(self): + enc_path = get_longer_path('user_enc') + dec_path = get_longer_path('user_dec') + enc_files = os.listdir(enc_path) + dec_files = os.listdir(dec_path) + [os.remove(enc_path + f) for f in enc_files] + [os.remove(dec_path + f) for f in dec_files] + + + + +if __name__ == '__main__': + ope = OPE() + + # ope.create_inverted_keyword_index() + # ope.create_index_of_values() + # ope.encrypt_index() + # ope.encrypt_documents() + + # ope.delete_user_directories() + # + # token = ope.generate_search_token(path_strings('heartRate') + '50') + # doc_ids_1 = ope.search(token, 'gt') + # print(doc_ids_1) + + # token = ope.generate_search_token(path_strings('spO2') + '99') + # doc_ids_2 = ope.search(token, 'eq') + # print(doc_ids_2) + # + # intersection = doc_ids_1.intersection(doc_ids_2) + # print('intersection', intersection) + + # union = doc_ids_1.union(doc_ids_2) + # print('union', union) + + + # ope.copy_encrypted_files_to_user(union) + # ope.decrypt_documents() + + + diff --git a/DemoCodes/FreshCopy/SSE/src/README.txt b/DemoCodes/FreshCopy/SSE/src/README.txt new file mode 100644 index 0000000..a4e1b56 --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/README.txt @@ -0,0 +1,6 @@ +Run serially +1. main00-create_patient_json.py +2. main01.py +3. main02.py +4. main03.py +5. main04-GUI.py \ No newline at end of file diff --git a/DemoCodes/FreshCopy/SSE/src/SSE.py b/DemoCodes/FreshCopy/SSE/src/SSE.py new file mode 100644 index 0000000..968cb7e --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/SSE.py @@ -0,0 +1,417 @@ +from Crypto.Cipher import AES + +from shutil import copy + +from help_functions import * + + +class SSE: + def __init__(self): + # self.generate_and_save_keys() + # self.update_IVs_and_doc_index() + pass + + def generate_and_save_keys(self): + self.index_key = get_random_bytes(32) + self.document_key = get_random_bytes(32) + + # write keys to binary files + keys = [self.index_key, self.document_key] + paths = [get_longer_path('index_key'), get_longer_path('document_key')] + write_keys_to_file(keys, paths, bin=True) + + # write keys to text files + keys = [bytes_2_string(self.index_key), bytes_2_string(self.document_key)] + paths = [get_longer_path('index_key_txt'), get_longer_path('document_key_txt')] + write_keys_to_file(keys, paths) + + def update_IVs_and_doc_index(self): + obj, changed_ids = self.make_document_index(get_longer_path('data')) + + curr_ivs = read_json_file(get_longer_path('ivs')) + + # -1 because of 'current_value' key + if (len(obj)-1 == len(curr_ivs)): + print('IVs are already up to date.') + return + + diff = len(obj)-1 - len(curr_ivs) + if (diff > 0): + for i in range(diff): + iv = get_random_bytes(16) + curr_ivs[changed_ids[i]] = bytes_2_string(iv) + else: + for ch_id in changed_ids: + curr_ivs.pop(str(ch_id), None) + + write_obj_to_json_file(curr_ivs, get_longer_path('ivs')) + + def make_document_index(self, path): + files = os.listdir(path) + + curr_json = read_json_file(get_longer_path('doc_index')) + + # -1 because of 'current_value' key + if (len(files) == len(curr_json) - 1): + print('Document index is already up to date.') + return [curr_json, []] + + # update json file + changed_ids = [] + current_value = curr_json['current_value'] if 'current_value' in curr_json else 0 + if (len(files) > len(curr_json) - 1): + for i in range(len(files)): + if (files[i] not in curr_json): + curr_json[files[i]] = current_value + changed_ids.append(current_value) + current_value += 1 + curr_json['current_value'] = current_value + else: + delete_keys = [] + for key, value in curr_json.items(): + if (key not in files and key != 'current_value'): + delete_keys.append(key) + changed_ids.append(value) + + for del_key in delete_keys: + curr_json.pop(del_key, None) + + # save updated version + write_obj_to_json_file(curr_json, get_longer_path('doc_index')) + + # update switched document index + self.create_switched_document_index() + + return [curr_json, changed_ids] + + def encrypt(self, key, IV, message): + # key and IV are bytes, message is list of strings + # returns list of bytes + aes = AES.new(key, AES.MODE_OFB, IV) + + ciphertext = [] + for m in message: + cipher = aes.encrypt(pad(string_2_bytes(m, 'utf-8'))) + ciphertext.append(cipher) + return ciphertext + + def decrypt(self, key, IV, ciphertext): + # key and IV are bytes, ciphertext is list of bytes + # returns string + aes = AES.new(key, AES.MODE_OFB, IV) + + plaintext = [] + for cipher in ciphertext: + if (len(cipher) > 0): + plain = unpad(aes.decrypt(cipher)).decode() + plaintext.append(plain) + else: + print('Cipher has no length.') + return plaintext + + def create_switched_document_index(self): + curr_json = read_json_file(get_longer_path('doc_index')) + switched_json = {value: key for key, value in curr_json.items() if (key != 'current_value')} + write_obj_to_json_file(switched_json, get_longer_path('doc_index_switched')) + + def create_inverted_keyword_index(self, files_destination): + # files_destination is path to directory with files + index_json = read_json_file(get_longer_path('doc_index')) + + all_words = [] + keyword_index = {} + files = os.listdir(files_destination) + for file in files: + content = read_file(files_destination + file) + # print(file, content) + keyword_index[index_json[file]] = content + all_words += content + + all_words = list(set(all_words)) # make words list unique + # print(keyword_index) + # print(all_words) + + inverted_index = {word: [txt for txt, words in keyword_index.items() if word in words] for word in all_words} + # print(inverted_index) + + write_obj_to_json_file(inverted_index, get_longer_path('inverted_index')) + + def update_inverted_keyword_index(self, changed_files): + # can apply to only new files + # changed_files is a list of strings + index_json = read_json_file(get_longer_path('doc_index')) + + all_words = [] + new_keyword_index = {} + for path in changed_files: + content = read_file(get_longer_path('data') + path) + # print(path, content) + new_keyword_index[index_json[path]] = content + all_words += content + + all_words = list(set(all_words)) # make words list unique + + curr_inverted_index = read_json_file(get_longer_path('inverted_index')) + new_inverted_index = {word: [txt for txt, words in new_keyword_index.items() if word in words] for word in all_words} + # print(curr_inverted_index) + # print(new_inverted_index) + + for key, value in new_inverted_index.items(): + if (key in curr_inverted_index): + curr_inverted_index[key] += value + curr_inverted_index[key] = list(set(curr_inverted_index[key])) + else: + curr_inverted_index[key] = value + + # print(curr_inverted_index) + write_obj_to_json_file(curr_inverted_index, get_longer_path('inverted_index')) + + def encrypt_index(self): + inverted_index = read_json_file(get_longer_path('inverted_index')) + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + encrypted_index = {} + for word, document_ids in inverted_index.items(): + for doc_id in document_ids: + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + ciphertext = bytes_2_string(self.encrypt(index_key, iv, [word])[0]) + encrypted_index[ciphertext] = doc_id + # print(len(encrypted_index), encrypted_index) + + write_obj_to_json_file(encrypted_index, get_longer_path('encrypted_index')) + + def encrypt_documents(self, files_source, files_destination): + # files_source is path to directory with files + # files_destination is path to directory with newly encrypted files + + key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + doc_index = read_json_file(get_longer_path('doc_index')) + + files = os.listdir(files_source) + for file in files: + content = read_file(files_source + file) + + doc_id = doc_index[file] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + cipherbytes = self.encrypt(key, iv, content) + + ciphertext = [bytes_2_string(cb) for cb in cipherbytes] + ciphertext = ' '.join(map(str, ciphertext)) + + new_path = files_destination + file + write_to_file(new_path, ciphertext) + + def generate_search_token(self, keyword): + index_key = read_bin_file(get_longer_path('index_key')) + ivs = read_json_file(get_longer_path('ivs')) + + search_token = [] + + for doc_id, iv in ivs.items(): + search_token += self.encrypt(index_key, string_2_bytes(iv, 'latin-1'), [keyword]) + + return search_token + + def search(self, search_token): + encrypted_index = read_json_file(get_longer_path('encrypted_index')) + doc_index_switched = read_json_file(get_longer_path('doc_index_switched')) + str_search_token = [bytes_2_string(token) for token in search_token] + + doc_ids2return = [encrypted_index[token] for token in str_search_token if token in encrypted_index] + print(doc_ids2return) + + for doc_id in doc_ids2return: + file = doc_index_switched[str(doc_id)] + filepath = get_longer_path('server') + file + # copy encrypted file to user + copy(filepath, get_longer_path('user_enc')) + + def decrypt_documents(self): + doc_index = read_json_file(get_longer_path('doc_index')) + doc_key = read_bin_file(get_longer_path('document_key')) + ivs = read_json_file(get_longer_path('ivs')) + + files = os.listdir(get_longer_path('user_enc')) + + for file in files: + content = enc_read_file(get_longer_path('user_enc') + file) + content = [string_2_bytes(c, 'latin-1') for c in content] + content = repair_data(content) + + doc_id = doc_index[file] + iv = string_2_bytes(ivs[str(doc_id)], 'latin-1') + + plaintext = self.decrypt(doc_key, iv, content) + + stripped_plaintext = [] + for p in plaintext: + if not p.isalnum() and len(p) > 1: + strip_p = re.sub(r'[^0-9a-žA-Ž\-]+', '', p) + stripped_plaintext.append(strip_p) + else: + stripped_plaintext.append(p) + + stripped_plaintext = ' '.join(map(str, stripped_plaintext)) + + # write to decrypted folder + write_to_file(get_longer_path('user_dec') + file, stripped_plaintext) + + def delete_user_directories(self): + enc_path = get_longer_path('user_enc') + dec_path = get_longer_path('user_dec') + enc_files = os.listdir(enc_path) + dec_files = os.listdir(dec_path) + [os.remove(enc_path + f) for f in enc_files] + [os.remove(dec_path + f) for f in dec_files] + + def delete_server_text_files(self): + server_path = get_longer_path('server') + text_files = os.listdir(server_path) + [os.remove(server_path + txt) for txt in text_files if txt.endswith(".txt")] + + + + +if __name__ == '__main__': + sse = SSE() + + # sse.generate_and_save_keys() + # sse.update_IVs_and_doc_index() + # sse.create_switched_document_index() + # sse.create_inverted_keyword_index(get_longer_path('data')) + # sse.encrypt_index() + # sse.encrypt_documents(get_longer_path('data'), get_longer_path('server')) + + # sse.delete_user_directories() + + # token = sse.generate_search_token('kruh') + # sse.search(token) + # sse.decrypt_documents() + + + + # sse.delete_user_directories() + # sse.delete_server_text_files() + + + + + ''' + key = read_bin_file(get_longer_path('document_key')) + + res = read_json_file(get_longer_path('ivs')) + # print(res) + iv = string_2_bytes(res['2'], 'latin-1') + + # mess = read_file(get_longer_path('data') + 'text_SLO_ZGO.txt') + # print('mess', mess) + + a = sse.encrypt(key, iv, ['personal//name//Marko']) + b = sse.encrypt(key, iv, ['personal//name//Janez']) + + dec_A = sse.decrypt(key, get_random_bytes(16), a) + dec_B = sse.decrypt(key, iv, b) + + print(a) + print(b) + print(dec_A) + print(dec_B) + + + cipher = sse.encrypt(key, iv, mess) + print('cipher', cipher) + + print(len(mess), len(cipher)) + + ciphertext = [bytes_2_string(c) for c in cipher] + ciphertext = ' '.join(map(str, ciphertext)) + # print('ciphertext') + # print(ciphertext) + + write_to_file('../testing.txt', ciphertext) + + content = enc_read_file('../testing.txt') + # print('read content') + # print(content) + + content = [string_2_bytes(c, 'latin-1') for c in content] + # print('\ncontent') + # print(content) + # print('len content: ', len(content)) + + repaired = [] + i = 0 + while (i < len(content)): + val = content[i] + if (i < len(content)-1): + next_val = content[i+1] + if (len(val) == 15 and len(next_val) != 0): + if (b'\n' in val): + new_val = val.replace(b'\n', b'\r\n') + else: + new_val = b' ' + val + + # print('\nval', val) + # print('rep', new_val) + # print(new_val == val) + # print(len(next_val)) + + repaired.append(new_val) + i += 1 + continue + elif not (len(val) / 16).is_integer(): + # print(i) + # print(val, next_val) + repaired.append(val + string_2_bytes(' ', 'latin-1') + next_val) + i += 1 + elif (len(val) != 0): + repaired.append(val) + i += 1 + + print('\n\nrepaired', len(repaired), repaired) + + # repaired[87] = b' \xa9*\xf4U,\xa2V\xc2\x8a\xa8tf=E\xf3' + + + # for i in range(len(repaired)): + # if (len(repaired[i]) != len(cipher[i])): + # print(i) + # print(len(repaired[i]), len(cipher[i])) + # print(repaired[i], cipher[i]) + # print(mess[i]) + # print() + + # print(len(cipher)) + # print(len(repaired)) + # print(len(cipher) == len(repaired)) + + + sse_2 = SSE() + plain = sse_2.decrypt(key, iv, repaired) + print(plain) + ''' + + + + ''' + key = read_bin_file(get_longer_path('document_key')) + + res = read_json_file(get_longer_path('ivs')) + # print(res) + iv = string_2_bytes(res['2'], 'latin-1') + + mess = ['riž', 'bučka', 'češnja'] + + cipher = sse.encrypt(key, iv, mess) + print(cipher) + + + sse_2 = SSE() + + plain = sse_2.decrypt(key, iv, cipher) + print(plain) + ''' + diff --git a/DemoCodes/FreshCopy/SSE/src/__init__.py b/DemoCodes/FreshCopy/SSE/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/FreshCopy/SSE/src/help_functions.py b/DemoCodes/FreshCopy/SSE/src/help_functions.py new file mode 100644 index 0000000..9a777ce --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/help_functions.py @@ -0,0 +1,450 @@ +import os +import json +import re +import ast +import math +import bisect +from collections import OrderedDict +from dateutil.parser import parse + + +def remove_double_backslashes(b): + return ast.literal_eval(str(b).replace('\\\\', '\\')) + + +def write_to_file(filepath, content): + f = open(filepath, 'w', encoding="utf-8") + f.write(content) + f.close() + + +def write_to_bin_file(filepath, content): + f = open(filepath, 'wb') + f.write(content) + f.close() + + +def read_file(filepath): + regex_remove = re.compile('[,\.\[\]!?():;]') + with open(filepath) as f: + content = f.read().split() + content = [regex_remove.sub('', c) for c in content] + return content + + +def enc_read_file(filepath): + with open(filepath) as f: + content = f.read().split(' ') + # content = [c.replace('\n', '\r') for c in content] + return content + + +def read_bin_file(filepath): + with open(filepath, "rb") as f: + B = [] + byte = f.read(1) + B.append(byte) + while byte != b"": + byte = f.read(1) + B.append(byte) + return b''.join(B) + + +def read_file_string(filepath): + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read().strip("'\"") + return content + + +def pad(data): + length = 16 - (len(data) % 16) + data += bytes([length]) * length + return data + + +def unpad(data): + return data[:-data[-1]] + + +def get_random_bytes(length): + return os.urandom(length) + + +def bytes_2_string(B): + return B.decode("latin-1") + + +def string_2_bytes(s, encoding): + return bytes(s, encoding) + + +def write_keys_to_file(keys, filepaths, bin=False): + if (len(keys) != len(filepaths)): + raise SyntaxError('Keys and filepaths lists must have same length.') + for i in range(len(keys)): + if (bin): + write_to_bin_file(filepaths[i], keys[i]) + else: + write_to_file(filepaths[i], keys[i]) + + +def write_obj_to_json_file(obj, filepath): + with open(filepath, 'w') as outfile: + json.dump(obj, outfile, indent=4) + + +def read_json_file(filepath): + try: + with open(filepath) as data_file: + return json.load(data_file) + except IOError: + print('Cannot open file at: ', filepath) + return False + + +def read_ordered_json_file(filepath): + try: + with open(filepath) as data_file: + return json.load(data_file, object_pairs_hook=OrderedDict) + except IOError: + print('Cannot open file at: ', filepath) + return False + + +def repair_data(content): + repaired = [] + i = 0 + while (i < len(content)): + val = content[i] + if (i < len(content) - 1): + next_val = content[i + 1] + + if (len(val) == 15 and len(next_val) != 0): + if (b'\n' in val): + new_val = val.replace(b'\n', b'\r\n') + else: + new_val = b' ' + val + repaired.append(new_val) + elif not (len(val) / 16).is_integer(): + repaired.append(val + string_2_bytes(' ', 'latin-1') + next_val) + i += 1 + elif (len(val) != 0): + repaired.append(val) + i += 1 + + return repaired + + +def get_path(short_path): + paths = { + 'doc_index': os.path.join('..', 'Private', 'document_index.json'), + 'doc_index_switched': os.path.join('..', 'Server', 'document_index_switched.json'), + + 'inverted_index': os.path.join('..', 'Private', 'inverted_index.json'), + 'values_index': os.path.join('..', 'Private', 'values_index.json'), + + 'index_key': os.path.join('..', 'Private', 'keys', 'index_key'), + 'index_key_txt': os.path.join('..', 'Private', 'keys', 'index_key.txt'), + 'document_key': os.path.join('..', 'Private', 'keys', 'document_key'), + 'document_key_txt': os.path.join('..', 'Private', 'keys', 'document_key.txt'), + + 'ivs': os.path.join('..', 'Private', 'IVs', 'ivs.json'), + + 'encrypted_index': os.path.join('..', 'Server', 'encrypted_index.json'), + + 'data': os.path.join('..', 'Data') + os.sep, + 'server': os.path.join('..', 'Server') + os.sep, + 'user_enc': os.path.join('..', 'User', 'encrypted') + os.sep, + 'user_dec': os.path.join('..', 'User', 'decrypted') + os.sep, + } + return paths[short_path] + + +def get_longer_path(short_path): + return os.path.join('..', get_path(short_path)) + + +def make_json_from_decrypted_file(ordered_dict): + json = OrderedDict() + for key, value in ordered_dict.items(): + if (len(key) == 1): + json[value] = OrderedDict() + else: + last_char = key[-1] + if (last_char != '*'): + key_chars_without_last = key[:-1] + keys = [ordered_dict[key_chars_without_last[:(i + 1)]] for i in range(len(key_chars_without_last))] + curr_json = json + for k in keys: + curr_json = curr_json[k] + + curr_json[value] = OrderedDict() + + else: # final data + key_chars_without_last_2 = key[:-2] + keys = [ordered_dict[key_chars_without_last[:(i + 1)]] for i in range(len(key_chars_without_last_2))] + curr_json = json + for k in keys: + curr_json = curr_json[k] + + curr_json[ordered_dict[key[:-1]]] = value + + return json + + +def path_strings(string): + paths = { + 'birthDate': 'personal//birthDate//', + 'firstName': 'personal//firstName//', + 'lastName': 'personal//lastName//', + + 'houseNum': 'personal//address//houseNumber//', + 'street': 'personal//address//street//', + 'country': 'personal//address//country//', + 'city': 'personal//address//city//', + 'postCode': 'personal//address//postCode//', + + 'type': 'type//', + + 'temperature': 'health//temperature//', + 'heartRate': 'health//heartRate//', + 'diastolic': 'health//diastolic//', + 'systolic': 'health//systolic//', + 'spO2': 'health//spO2//', + } + return paths[string] + + +def get_docs2return(encrypted_index, str_search_token, operator, position): + if (operator == 'eq'): + doc_ids2return = [encrypted_index[token] for token in str_search_token if token in encrypted_index] + else: + num_of_files = encrypted_index['number_of_files'] + indices = [] + for token in str_search_token: + if (token in encrypted_index): + index = list(encrypted_index.keys()).index(token) + indices.append(index) + + indices.sort() + + if (operator == 'lt' or operator == 'gte' or operator == 'ne'): # for 'ne' it doesn't matter + index = indices[0] + elif (operator == 'gt' or operator == 'lte'): + index = indices[-1] + + if (index % num_of_files != 0): + start_index = math.floor(index / num_of_files) * num_of_files + 1 + end_index = math.ceil(index / num_of_files) * num_of_files + else: + start_index = index - num_of_files + 1 + end_index = index + + ordered_list = list(encrypted_index.items()) + + doc_ids2return = [] + if (position == 'exact'): + if (operator == 'ne'): + from_start_to_end = set([i for i in range(start_index, end_index + 1)]) + indices_set = set(indices) + not_equals = from_start_to_end - indices_set + for ind in not_equals: + doc_ids2return.append(ordered_list[ind][1]) + elif (operator == 'gt'): + for i in range(index + 1, end_index + 1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'gte'): + for i in range(index, end_index + 1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'lt'): + for i in range(index - 1, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'lte'): + for i in range(index, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + elif (position == 'left'): + if (operator == 'ne'): + from_start_to_end = set([i for i in range(start_index, end_index + 1)]) + for ind in from_start_to_end: + doc_ids2return.append(ordered_list[ind][1]) + elif (operator == 'gt' or operator == 'gte'): + for i in range(index, end_index + 1): + doc_ids2return.append(ordered_list[i][1]) + elif (operator == 'lt' or operator == 'lte'): + for i in range(index - 1, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + elif (position == 'right'): + if (operator == 'ne'): + from_start_to_end = set([i for i in range(start_index, end_index + 1)]) + for ind in from_start_to_end: + doc_ids2return.append(ordered_list[ind][1]) + elif (operator == 'gt' or operator == 'gte'): + doc_ids2return = [] + elif (operator == 'lt' or operator == 'lte'): + for i in range(index, start_index - 1, -1): + doc_ids2return.append(ordered_list[i][1]) + + return doc_ids2return + + +def matched_brackets(string): + count = 0 + for i in string: + if i == "(": + count += 1 + elif i == ")": + count -= 1 + if count < 0: + return False + return count == 0 + + +def is_number(s): + try: + float(s) + return True + except ValueError: + return False + + +def is_date_format(string): + date_reg_exp = re.compile('\d{4}[-]\d{2}[-]\d{2}') + match = re.match(date_reg_exp, string) + if (match is not None): + try: + parse(string) + return True + except ValueError: + return False + return False + + +def operator_string(operator): + op = { + '=': 'eq', + '≠': 'ne', + '<': 'lt', + '≤': 'lte', + '>': 'gt', + '≥': 'gte', + } + return op[operator] + + +def combine_sets(set_1, set_2, operator): + if (operator == 'OR'): + return set_1.union(set_2) + elif (operator == 'AND'): + return set_1.intersection(set_2) + + +def solve_expression(exp): + ok = True + while (ok and len(exp) != 1): + # find ANDs + while ('AND' in exp): + and_index = exp.index('AND') + if (and_index-1 >= 0 and and_index+1 < len(exp)): + combination = combine_sets(exp[and_index - 1], exp[and_index + 1], 'AND') + del exp[and_index - 1:and_index + 2] + exp.insert(and_index - 1, combination) + else: + ok = False + break + + # find ORs + while ('OR' in exp): + or_index = exp.index('OR') + if (or_index-1 >= 0 and or_index+1 < len(exp)): + combination = combine_sets(exp[or_index - 1], exp[or_index + 1], 'OR') + del exp[or_index - 1:or_index + 2] + exp.insert(or_index - 1, combination) + else: + ok = False + break + + return exp + + +def find_closest_value(path_string, operator, value, value_type): + # 'left', 'right' or 'exact' tells position of chosen value to curr_val + pos = None + if (operator != '='): + values_index = read_json_file(get_longer_path('values_index')) + all_values = values_index[path_string] + all_values_1d = [float(v[0]) if is_number(v[0]) else v[0] for v in all_values] + value = float(value) if is_number(value) and value_type != 'string' else value + + index = bisect.bisect_left(all_values_1d, value) + if (index == len(all_values_1d)): + curr_val = all_values_1d[-1] + pos = 'right' + elif (all_values_1d[index] == value): + curr_val = value + pos = 'exact' + else: + curr_val = all_values_1d[index] + pos = 'left' + else: + curr_val = value + + if (value_type == 'int'): + curr_val = int(curr_val) + + return [str(curr_val), pos] + + +def sort_values(a, b): + a_path = a[0].split('//')[:-1] + a_val = a[0].split('//')[-1] + b_path = b[0].split('//')[:-1] + b_val = b[0].split('//')[-1] + + if (a_path == b_path): + if (is_number(a_val) and is_number(b_val)): + return float(a_val) - float(b_val) + else: + if (a_val < b_val): + return -1 + else: + return 1 + elif (a_path < b_path): + return -1 + else: + return 1 + + +def property_type(property): + t = { + 'birthDate': 'date', + 'firstName': 'string', + 'lastName': 'string', + + 'houseNum': 'int', + 'street': 'string', + 'country': 'string', + 'city': 'string', + 'postCode': 'int', + + 'type': 'string', + + 'temperature': 'float', + 'heartRate': 'int', + 'diastolic': 'int', + 'systolic': 'int', + 'spO2': 'int', + } + return t[property] + + +def type_of_var(string): + dates = ['birthDate'] + numbers = ['houseNum', 'postCode', 'temperature', 'heartRate', 'diastolic', 'systolic', 'spO2'] + strings = ['firstName', 'lastName', 'street', 'country', 'city', 'type'] + + if (string in dates): + return ['d', 'YYYY-MM-DD'] + elif (string in numbers): + return ['n', '36.2'] + elif (string in strings): + return ['s', 'some string'] diff --git a/DemoCodes/FreshCopy/SSE/src/main00-create_patient_json.py b/DemoCodes/FreshCopy/SSE/src/main00-create_patient_json.py new file mode 100644 index 0000000..c0b67ef --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/main00-create_patient_json.py @@ -0,0 +1,90 @@ + +from help_functions import write_obj_to_json_file, get_longer_path + + +patient_1 = { + "type": "patient", + + "personal": { + "birthDate": "25. 08. 1995", + "firstName": "Marko", + "lastName": "Zeman", + "address": { + "street": "TKS", + "houseNumber": "15", + "city": "Ljubljana", + "country": "Slovenia", + "postCode": "1000" + } + }, + + "health": { + "temperature": "35.9", + "heartRate": "50", + "systolic": "115", + "diastolic": "75", + "spO2": "99", + } +} + + +patient_2 = { + "type": "patient", + + "personal": { + "birthDate": "19. 09. 1950", + "firstName": "Janez", + "lastName": "Novak", + "address": { + "street": "Čopova ulica", + "houseNumber": "25", + "city": "Ljubljana", + "country": "Slovenia", + "postCode": "1000" + } + }, + + "health": { + "temperature": "36.4", + "heartRate": "72", + "systolic": "140", + "diastolic": "88", + "spO2": "98", + } +} + + +patient_3 = { + "type": "patient", + + "personal": { + "birthDate": "17. 11. 1975", + "firstName": "Ana", + "lastName": "Horvat", + "address": { + "street": "Mariborska cesta", + "houseNumber": "300", + "city": "Maribor", + "country": "Slovenia", + "postCode": "2000" + } + }, + + "health": { + "temperature": "36.1", + "heartRate": "66", + "systolic": "133", + "diastolic": "77", + "spO2": "97", + } +} + + +if __name__ == '__main__': + path = get_longer_path('data') + + write_obj_to_json_file(patient_1, path + 'MarkoZeman.json') + write_obj_to_json_file(patient_2, path + 'JanezNovak.json') + write_obj_to_json_file(patient_3, path + 'AnaHorvat.json') + + diff --git a/DemoCodes/FreshCopy/SSE/src/main01.py b/DemoCodes/FreshCopy/SSE/src/main01.py new file mode 100644 index 0000000..7638231 --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/main01.py @@ -0,0 +1,5 @@ +from SSE import SSE +sse = SSE() + +sse.generate_and_save_keys() +sse.update_IVs_and_doc_index() \ No newline at end of file diff --git a/DemoCodes/FreshCopy/SSE/src/main02.py b/DemoCodes/FreshCopy/SSE/src/main02.py new file mode 100644 index 0000000..cecc454 --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/main02.py @@ -0,0 +1,7 @@ +from SSE import SSE +from OPE import OPE +sse = SSE() +ope = OPE() + +sse.create_switched_document_index() +ope.create_inverted_keyword_index() \ No newline at end of file diff --git a/DemoCodes/FreshCopy/SSE/src/main03.py b/DemoCodes/FreshCopy/SSE/src/main03.py new file mode 100644 index 0000000..1ffd175 --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/main03.py @@ -0,0 +1,6 @@ +from OPE import OPE +ope = OPE() + +ope.create_index_of_values() +ope.encrypt_index() +ope.encrypt_documents() \ No newline at end of file diff --git a/DemoCodes/FreshCopy/SSE/src/main04-GUI.py b/DemoCodes/FreshCopy/SSE/src/main04-GUI.py new file mode 100644 index 0000000..6dc84f1 --- /dev/null +++ b/DemoCodes/FreshCopy/SSE/src/main04-GUI.py @@ -0,0 +1,325 @@ +import time + +from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit +from help_functions import * + +from OPE import OPE + + +class SearchGUI(QWidget): + + def __init__(self): + super().__init__() + + self.ope = OPE() + + self.height = 480 + self.width = 720 + + self.setGeometry(700, 150, self.width, self.height) + self.setWindowTitle('SSE') + + self.last_clicked_property = None + + self.init_UI() + + def init_UI(self): + ver_spacing = 100 + hor_spacing = 20 + + left_bracket = QPushButton('(', self) + left_bracket.move(hor_spacing, hor_spacing) + self.connect_button(left_bracket) + right_bracket = QPushButton(')', self) + right_bracket.move(ver_spacing+hor_spacing, hor_spacing) + self.connect_button(right_bracket) + + self.brackets_buttons = [left_bracket, right_bracket] + + self.horizontal_line(3*hor_spacing) + + properties_widget = QWidget(self) + properties_widget.move(hor_spacing, 4*hor_spacing) + birth_date = QPushButton('birthDate', properties_widget) + self.connect_button(birth_date) + first_name = QPushButton('firstName', properties_widget) + first_name.move(ver_spacing, 0) + self.connect_button(first_name) + last_name = QPushButton('lastName', properties_widget) + last_name.move(2*ver_spacing, 0) + self.connect_button(last_name) + street = QPushButton('street', properties_widget) + street.move(3*ver_spacing, 0) + self.connect_button(street) + house_number = QPushButton('houseNum', properties_widget) + house_number.move(4*ver_spacing, 0) + self.connect_button(house_number) + city = QPushButton('city', properties_widget) + city.move(5*ver_spacing, 0) + self.connect_button(city) + post_code = QPushButton('postCode', properties_widget) + post_code.move(6*ver_spacing, 0) + self.connect_button(post_code) + country = QPushButton('country', properties_widget) + country.move(0, 2*hor_spacing) + self.connect_button(country) + type_of = QPushButton('type', properties_widget) + type_of.move(ver_spacing, 2 * hor_spacing) + self.connect_button(type_of) + temperature = QPushButton('temperature', properties_widget) + temperature.move(2*ver_spacing, 2*hor_spacing) + self.connect_button(temperature) + heart_rate = QPushButton('heartRate', properties_widget) + heart_rate.move(3*ver_spacing, 2*hor_spacing) + self.connect_button(heart_rate) + diastolic = QPushButton('diastolic', properties_widget) + diastolic.move(4*ver_spacing, 2*hor_spacing) + self.connect_button(diastolic) + systolic = QPushButton('systolic', properties_widget) + systolic.move(5*ver_spacing, 2*hor_spacing) + self.connect_button(systolic) + spO2 = QPushButton('spO2', properties_widget) + spO2.move(6*ver_spacing, 2*hor_spacing) + self.connect_button(spO2) + + self.properties_buttons = [birth_date, first_name, last_name, street, house_number, city, post_code, + country, type_of, temperature, heart_rate, diastolic, systolic, spO2] + + self.horizontal_line(8*hor_spacing) + + operators_widget = QWidget(self) + operators_widget.move(hor_spacing, 9*hor_spacing) + eq = QPushButton('=', operators_widget) + self.connect_button(eq) + ne = QPushButton('≠', operators_widget) + ne.move(ver_spacing, 0) + self.connect_button(ne) + lt = QPushButton('<', operators_widget) + lt.move(2*ver_spacing, 0) + self.connect_button(lt) + lte = QPushButton('≤', operators_widget) + lte.move(3*ver_spacing, 0) + self.connect_button(lte) + gt = QPushButton('>', operators_widget) + gt.move(4*ver_spacing, 0) + self.connect_button(gt) + gte = QPushButton('≥', operators_widget) + gte.move(5*ver_spacing, 0) + self.connect_button(gte) + + self.operators_buttons = [eq, ne, lt, lte, gt, gte] + self.buttons_state(self.operators_buttons, 'disable') + + self.horizontal_line(11*hor_spacing) + + value_widget = QWidget(self) + value_widget.move(hor_spacing, 12*hor_spacing) + val_label = QLabel('Value: ', value_widget) + val_label.move(0, 4) + self.val_line_edit = QLineEdit(value_widget) + self.val_line_edit.setFixedWidth(2 * ver_spacing) + self.val_line_edit.move(ver_spacing-hor_spacing, 0) + self.val_line_edit.setDisabled(True) + self.val_line_edit.returnPressed.connect(self.ok_clicked) + self.ok_button = QPushButton('OK', value_widget) + self.ok_button.move(3*ver_spacing, 0) + self.ok_button.clicked.connect(self.ok_clicked) + self.ok_button.setDisabled(True) + + self.horizontal_line(14*hor_spacing) + + and_or_widget = QWidget(self) + and_or_widget.move(hor_spacing, 15*hor_spacing) + and_button = QPushButton('AND', and_or_widget) + self.connect_button(and_button) + or_button = QPushButton('OR', and_or_widget) + or_button.move(ver_spacing, 0) + self.connect_button(or_button) + + self.and_or_buttons = [and_button, or_button] + self.buttons_state(self.and_or_buttons, 'disable') + + self.horizontal_line(17*hor_spacing) + + info_widget = QWidget(self) + info_widget.move(hor_spacing, 18*hor_spacing) + query_label = QLabel('Selected query: ', info_widget) + self.query = QLabel('', info_widget) + self.query.move(0, int(1.5*hor_spacing)) + self.query.setStyleSheet("QLabel {color : blue;}") + self.query.setFixedWidth(self.width) + + self.horizontal_line(21*hor_spacing) + + commit_widget = QWidget(self) + commit_widget.move(hor_spacing, 22*hor_spacing) + search_button = QPushButton('Search!', commit_widget) + search_button.clicked.connect(self.search_clicked) + clear_button = QPushButton('Clear', commit_widget) + clear_button.move(ver_spacing, 0) + clear_button.clicked.connect(self.clear_clicked) + self.info_label = QLabel('', commit_widget) + self.info_label.move(2*ver_spacing, 4) + self.info_label.setFixedWidth(self.width-2*ver_spacing) + + def horizontal_line(self, y): + horizontal_line = QWidget(self) + horizontal_line.setFixedSize(self.width, 1) + horizontal_line.setStyleSheet("background-color: black;") + horizontal_line.move(0, y) + + def button_clicked(self, button): + btn_txt = button.text() + if (button in self.properties_buttons): + self.last_clicked_property = btn_txt + self.buttons_state(self.properties_buttons, 'disable') + self.buttons_state(self.brackets_buttons, 'disable') + self.buttons_state(self.operators_buttons, 'enable') + elif (button in self.operators_buttons): + self.buttons_state(self.operators_buttons, 'disable') + self.val_line_edit.setEnabled(True) + self.val_line_edit.clear() + self.val_line_edit.setPlaceholderText(type_of_var(self.last_clicked_property)[1]) + self.val_line_edit.setFocus(True) + self.ok_button.setEnabled(True) + elif (btn_txt == 'AND' or btn_txt == 'OR'): + self.buttons_state(self.and_or_buttons, 'disable') + self.buttons_state(self.properties_buttons, 'enable') + self.info_label.clear() + + new_text = self.query.text() + btn_txt + ' ' + self.query.setText(new_text) + + def clear_clicked(self): + self.query.clear() + self.info_label.clear() + self.val_line_edit.clear() + self.val_line_edit.setDisabled(True) + self.buttons_state(self.and_or_buttons, 'disable') + self.buttons_state(self.operators_buttons, 'disable') + self.buttons_state(self.brackets_buttons, 'enable') + self.buttons_state(self.properties_buttons, 'enable') + + def ok_clicked(self): + val = self.val_line_edit.text() + + type_of_variable = type_of_var(self.last_clicked_property)[0] + if (type_of_variable == 'd'): + if (not is_date_format(val)): + self.info_label.setText('Value should be date.') + return + elif (type_of_variable == 'n'): + if (not is_number(val)): + self.info_label.setText('Value should be number with dot separator.') + return + + self.query.setText(self.query.text() + val + ' ') + self.val_line_edit.setDisabled(True) + self.ok_button.setDisabled(True) + self.buttons_state(self.and_or_buttons, 'enable') + self.buttons_state(self.brackets_buttons, 'enable') + + def search_clicked(self): + start_time = time.time() + + query = self.query.text() + query_split = [x.strip() for x in re.split(r'[()]', query) if x.strip()] + + brackets_ok = matched_brackets(query) + if (brackets_ok): + if (len(query_split) == 0): + self.info_label.setText('Query cannot be empty.') + return + + res = [] + for q in query_split: + parameters = q.split(' ') + + if (len(parameters) == 1 and (parameters[0] == 'AND' or parameters[0] == 'OR')): + res.append(parameters[0]) + elif ('AND' not in parameters and 'OR' not in parameters): # only one condition + if (len(parameters) < 3): + self.info_label.setText('Query is not correct.') + return + + property = parameters[0] + path_string = path_strings(property) + operator = parameters[1] + value = ' '.join(parameters[2:]) + + curr_value, position = find_closest_value(path_string, operator, value, property_type(property)) + + token = self.ope.generate_search_token(path_string + curr_value) + doc_ids = self.ope.search(token, operator_string(operator), position) + res.append(doc_ids) + else: + and_operators = [i for i, j in enumerate(parameters) if j == 'AND'] + or_operators = [i for i, j in enumerate(parameters) if j == 'OR'] + and_or_operators = and_operators + or_operators + and_or_operators.sort() + + i = 0 + part_res = [] + while (i < len(parameters)): + if (i in and_or_operators): + part_res.append(parameters[i]) + elif (i == 0 or i-1 in and_or_operators): # property + property = parameters[i] + path_string = path_strings(property) + elif (i == 1 or i-2 in and_or_operators): # operator + operator = parameters[i] + elif (i == 2 or i-3 in and_or_operators): # start of value + val = [] + while (i < len(parameters) and i not in and_or_operators): + val.append(parameters[i]) + i += 1 + i -= 1 + value = ' '.join(val) + + curr_value, position = find_closest_value(path_string, operator, value, property_type(property)) + + token = self.ope.generate_search_token(path_string + curr_value) + doc_ids = self.ope.search(token, operator_string(operator), position) + part_res.append(doc_ids) + i += 1 + + # solve part_res in save into res + part_res = solve_expression(part_res) + res += part_res + + # solve res + res = solve_expression(res) + + # clear directory, copy encrypted files and decrypt them + self.ope.delete_user_directories() + if (len(res) == 1): + num_of_files = self.ope.copy_encrypted_files_to_user(res[0]) + self.ope.decrypt_documents() + end_time = time.time() + self.info_label.setText('Documents matching query: ' + str(num_of_files) + '.' + 4*' ' + + 'Query took ' + "{0:.1f}".format(1000*(end_time - start_time)) + ' ms.') + + else: + print('res does not have one element.') + else: + self.info_label.setText('Brackets are not set correctly.') + + def connect_button(self, btn): + btn.clicked.connect(lambda: self.button_clicked(btn)) + + def buttons_state(self, buttons, state): + if (state == 'enable'): + for btn in buttons: + btn.setEnabled(True) + elif (state == 'disable'): + for btn in buttons: + btn.setDisabled(True) + + + + +if __name__ == '__main__': + app = QApplication([]) + w = SearchGUI() + w.show() + app.exec_() diff --git a/DemoCodes/FreshCopy/Server/.gitkeep b/DemoCodes/FreshCopy/Server/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/FreshCopy/User/decrypted/.gitkeep b/DemoCodes/FreshCopy/User/decrypted/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/DemoCodes/FreshCopy/User/encrypted/.gitkeep b/DemoCodes/FreshCopy/User/encrypted/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md index 5a4490e..a8a993b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # Searchable Symmetric Encryption with order preservation +### N.B. +* `pip install -r requirements.txt` +* `DemoCodes/FreshCopy` has been created by following the steps already. +* main00, main01, ... etc has been created by following the codes given in the steps. +* `DemoCodes/ExampleTestSSE` is an example of simulation + ### _About_ Searchable Symmetric Encryption enables search over encrypted data. Order preservation gives us possibility to search over encrypted data not only by exact match but also with operators like < and >. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..04710ca Binary files /dev/null and b/requirements.txt differ diff --git a/src/README.txt b/src/README.txt new file mode 100644 index 0000000..a4e1b56 --- /dev/null +++ b/src/README.txt @@ -0,0 +1,6 @@ +Run serially +1. main00-create_patient_json.py +2. main01.py +3. main02.py +4. main03.py +5. main04-GUI.py \ No newline at end of file diff --git a/src/help_functions.py b/src/help_functions.py index 33b220f..9a777ce 100644 --- a/src/help_functions.py +++ b/src/help_functions.py @@ -13,7 +13,7 @@ def remove_double_backslashes(b): def write_to_file(filepath, content): - f = open(filepath, 'w') + f = open(filepath, 'w', encoding="utf-8") f.write(content) f.close() @@ -51,7 +51,7 @@ def read_bin_file(filepath): def read_file_string(filepath): - with open(filepath) as f: + with open(filepath, 'r', encoding='utf-8') as f: content = f.read().strip("'\"") return content diff --git a/src/main00-create_patient_json.py b/src/main00-create_patient_json.py new file mode 100644 index 0000000..c0b67ef --- /dev/null +++ b/src/main00-create_patient_json.py @@ -0,0 +1,90 @@ + +from help_functions import write_obj_to_json_file, get_longer_path + + +patient_1 = { + "type": "patient", + + "personal": { + "birthDate": "25. 08. 1995", + "firstName": "Marko", + "lastName": "Zeman", + "address": { + "street": "TKS", + "houseNumber": "15", + "city": "Ljubljana", + "country": "Slovenia", + "postCode": "1000" + } + }, + + "health": { + "temperature": "35.9", + "heartRate": "50", + "systolic": "115", + "diastolic": "75", + "spO2": "99", + } +} + + +patient_2 = { + "type": "patient", + + "personal": { + "birthDate": "19. 09. 1950", + "firstName": "Janez", + "lastName": "Novak", + "address": { + "street": "Čopova ulica", + "houseNumber": "25", + "city": "Ljubljana", + "country": "Slovenia", + "postCode": "1000" + } + }, + + "health": { + "temperature": "36.4", + "heartRate": "72", + "systolic": "140", + "diastolic": "88", + "spO2": "98", + } +} + + +patient_3 = { + "type": "patient", + + "personal": { + "birthDate": "17. 11. 1975", + "firstName": "Ana", + "lastName": "Horvat", + "address": { + "street": "Mariborska cesta", + "houseNumber": "300", + "city": "Maribor", + "country": "Slovenia", + "postCode": "2000" + } + }, + + "health": { + "temperature": "36.1", + "heartRate": "66", + "systolic": "133", + "diastolic": "77", + "spO2": "97", + } +} + + +if __name__ == '__main__': + path = get_longer_path('data') + + write_obj_to_json_file(patient_1, path + 'MarkoZeman.json') + write_obj_to_json_file(patient_2, path + 'JanezNovak.json') + write_obj_to_json_file(patient_3, path + 'AnaHorvat.json') + + diff --git a/src/main01.py b/src/main01.py new file mode 100644 index 0000000..7638231 --- /dev/null +++ b/src/main01.py @@ -0,0 +1,5 @@ +from SSE import SSE +sse = SSE() + +sse.generate_and_save_keys() +sse.update_IVs_and_doc_index() \ No newline at end of file diff --git a/src/main02.py b/src/main02.py new file mode 100644 index 0000000..cecc454 --- /dev/null +++ b/src/main02.py @@ -0,0 +1,7 @@ +from SSE import SSE +from OPE import OPE +sse = SSE() +ope = OPE() + +sse.create_switched_document_index() +ope.create_inverted_keyword_index() \ No newline at end of file diff --git a/src/main03.py b/src/main03.py new file mode 100644 index 0000000..1ffd175 --- /dev/null +++ b/src/main03.py @@ -0,0 +1,6 @@ +from OPE import OPE +ope = OPE() + +ope.create_index_of_values() +ope.encrypt_index() +ope.encrypt_documents() \ No newline at end of file diff --git a/src/main04-GUI.py b/src/main04-GUI.py new file mode 100644 index 0000000..6dc84f1 --- /dev/null +++ b/src/main04-GUI.py @@ -0,0 +1,325 @@ +import time + +from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit +from help_functions import * + +from OPE import OPE + + +class SearchGUI(QWidget): + + def __init__(self): + super().__init__() + + self.ope = OPE() + + self.height = 480 + self.width = 720 + + self.setGeometry(700, 150, self.width, self.height) + self.setWindowTitle('SSE') + + self.last_clicked_property = None + + self.init_UI() + + def init_UI(self): + ver_spacing = 100 + hor_spacing = 20 + + left_bracket = QPushButton('(', self) + left_bracket.move(hor_spacing, hor_spacing) + self.connect_button(left_bracket) + right_bracket = QPushButton(')', self) + right_bracket.move(ver_spacing+hor_spacing, hor_spacing) + self.connect_button(right_bracket) + + self.brackets_buttons = [left_bracket, right_bracket] + + self.horizontal_line(3*hor_spacing) + + properties_widget = QWidget(self) + properties_widget.move(hor_spacing, 4*hor_spacing) + birth_date = QPushButton('birthDate', properties_widget) + self.connect_button(birth_date) + first_name = QPushButton('firstName', properties_widget) + first_name.move(ver_spacing, 0) + self.connect_button(first_name) + last_name = QPushButton('lastName', properties_widget) + last_name.move(2*ver_spacing, 0) + self.connect_button(last_name) + street = QPushButton('street', properties_widget) + street.move(3*ver_spacing, 0) + self.connect_button(street) + house_number = QPushButton('houseNum', properties_widget) + house_number.move(4*ver_spacing, 0) + self.connect_button(house_number) + city = QPushButton('city', properties_widget) + city.move(5*ver_spacing, 0) + self.connect_button(city) + post_code = QPushButton('postCode', properties_widget) + post_code.move(6*ver_spacing, 0) + self.connect_button(post_code) + country = QPushButton('country', properties_widget) + country.move(0, 2*hor_spacing) + self.connect_button(country) + type_of = QPushButton('type', properties_widget) + type_of.move(ver_spacing, 2 * hor_spacing) + self.connect_button(type_of) + temperature = QPushButton('temperature', properties_widget) + temperature.move(2*ver_spacing, 2*hor_spacing) + self.connect_button(temperature) + heart_rate = QPushButton('heartRate', properties_widget) + heart_rate.move(3*ver_spacing, 2*hor_spacing) + self.connect_button(heart_rate) + diastolic = QPushButton('diastolic', properties_widget) + diastolic.move(4*ver_spacing, 2*hor_spacing) + self.connect_button(diastolic) + systolic = QPushButton('systolic', properties_widget) + systolic.move(5*ver_spacing, 2*hor_spacing) + self.connect_button(systolic) + spO2 = QPushButton('spO2', properties_widget) + spO2.move(6*ver_spacing, 2*hor_spacing) + self.connect_button(spO2) + + self.properties_buttons = [birth_date, first_name, last_name, street, house_number, city, post_code, + country, type_of, temperature, heart_rate, diastolic, systolic, spO2] + + self.horizontal_line(8*hor_spacing) + + operators_widget = QWidget(self) + operators_widget.move(hor_spacing, 9*hor_spacing) + eq = QPushButton('=', operators_widget) + self.connect_button(eq) + ne = QPushButton('≠', operators_widget) + ne.move(ver_spacing, 0) + self.connect_button(ne) + lt = QPushButton('<', operators_widget) + lt.move(2*ver_spacing, 0) + self.connect_button(lt) + lte = QPushButton('≤', operators_widget) + lte.move(3*ver_spacing, 0) + self.connect_button(lte) + gt = QPushButton('>', operators_widget) + gt.move(4*ver_spacing, 0) + self.connect_button(gt) + gte = QPushButton('≥', operators_widget) + gte.move(5*ver_spacing, 0) + self.connect_button(gte) + + self.operators_buttons = [eq, ne, lt, lte, gt, gte] + self.buttons_state(self.operators_buttons, 'disable') + + self.horizontal_line(11*hor_spacing) + + value_widget = QWidget(self) + value_widget.move(hor_spacing, 12*hor_spacing) + val_label = QLabel('Value: ', value_widget) + val_label.move(0, 4) + self.val_line_edit = QLineEdit(value_widget) + self.val_line_edit.setFixedWidth(2 * ver_spacing) + self.val_line_edit.move(ver_spacing-hor_spacing, 0) + self.val_line_edit.setDisabled(True) + self.val_line_edit.returnPressed.connect(self.ok_clicked) + self.ok_button = QPushButton('OK', value_widget) + self.ok_button.move(3*ver_spacing, 0) + self.ok_button.clicked.connect(self.ok_clicked) + self.ok_button.setDisabled(True) + + self.horizontal_line(14*hor_spacing) + + and_or_widget = QWidget(self) + and_or_widget.move(hor_spacing, 15*hor_spacing) + and_button = QPushButton('AND', and_or_widget) + self.connect_button(and_button) + or_button = QPushButton('OR', and_or_widget) + or_button.move(ver_spacing, 0) + self.connect_button(or_button) + + self.and_or_buttons = [and_button, or_button] + self.buttons_state(self.and_or_buttons, 'disable') + + self.horizontal_line(17*hor_spacing) + + info_widget = QWidget(self) + info_widget.move(hor_spacing, 18*hor_spacing) + query_label = QLabel('Selected query: ', info_widget) + self.query = QLabel('', info_widget) + self.query.move(0, int(1.5*hor_spacing)) + self.query.setStyleSheet("QLabel {color : blue;}") + self.query.setFixedWidth(self.width) + + self.horizontal_line(21*hor_spacing) + + commit_widget = QWidget(self) + commit_widget.move(hor_spacing, 22*hor_spacing) + search_button = QPushButton('Search!', commit_widget) + search_button.clicked.connect(self.search_clicked) + clear_button = QPushButton('Clear', commit_widget) + clear_button.move(ver_spacing, 0) + clear_button.clicked.connect(self.clear_clicked) + self.info_label = QLabel('', commit_widget) + self.info_label.move(2*ver_spacing, 4) + self.info_label.setFixedWidth(self.width-2*ver_spacing) + + def horizontal_line(self, y): + horizontal_line = QWidget(self) + horizontal_line.setFixedSize(self.width, 1) + horizontal_line.setStyleSheet("background-color: black;") + horizontal_line.move(0, y) + + def button_clicked(self, button): + btn_txt = button.text() + if (button in self.properties_buttons): + self.last_clicked_property = btn_txt + self.buttons_state(self.properties_buttons, 'disable') + self.buttons_state(self.brackets_buttons, 'disable') + self.buttons_state(self.operators_buttons, 'enable') + elif (button in self.operators_buttons): + self.buttons_state(self.operators_buttons, 'disable') + self.val_line_edit.setEnabled(True) + self.val_line_edit.clear() + self.val_line_edit.setPlaceholderText(type_of_var(self.last_clicked_property)[1]) + self.val_line_edit.setFocus(True) + self.ok_button.setEnabled(True) + elif (btn_txt == 'AND' or btn_txt == 'OR'): + self.buttons_state(self.and_or_buttons, 'disable') + self.buttons_state(self.properties_buttons, 'enable') + self.info_label.clear() + + new_text = self.query.text() + btn_txt + ' ' + self.query.setText(new_text) + + def clear_clicked(self): + self.query.clear() + self.info_label.clear() + self.val_line_edit.clear() + self.val_line_edit.setDisabled(True) + self.buttons_state(self.and_or_buttons, 'disable') + self.buttons_state(self.operators_buttons, 'disable') + self.buttons_state(self.brackets_buttons, 'enable') + self.buttons_state(self.properties_buttons, 'enable') + + def ok_clicked(self): + val = self.val_line_edit.text() + + type_of_variable = type_of_var(self.last_clicked_property)[0] + if (type_of_variable == 'd'): + if (not is_date_format(val)): + self.info_label.setText('Value should be date.') + return + elif (type_of_variable == 'n'): + if (not is_number(val)): + self.info_label.setText('Value should be number with dot separator.') + return + + self.query.setText(self.query.text() + val + ' ') + self.val_line_edit.setDisabled(True) + self.ok_button.setDisabled(True) + self.buttons_state(self.and_or_buttons, 'enable') + self.buttons_state(self.brackets_buttons, 'enable') + + def search_clicked(self): + start_time = time.time() + + query = self.query.text() + query_split = [x.strip() for x in re.split(r'[()]', query) if x.strip()] + + brackets_ok = matched_brackets(query) + if (brackets_ok): + if (len(query_split) == 0): + self.info_label.setText('Query cannot be empty.') + return + + res = [] + for q in query_split: + parameters = q.split(' ') + + if (len(parameters) == 1 and (parameters[0] == 'AND' or parameters[0] == 'OR')): + res.append(parameters[0]) + elif ('AND' not in parameters and 'OR' not in parameters): # only one condition + if (len(parameters) < 3): + self.info_label.setText('Query is not correct.') + return + + property = parameters[0] + path_string = path_strings(property) + operator = parameters[1] + value = ' '.join(parameters[2:]) + + curr_value, position = find_closest_value(path_string, operator, value, property_type(property)) + + token = self.ope.generate_search_token(path_string + curr_value) + doc_ids = self.ope.search(token, operator_string(operator), position) + res.append(doc_ids) + else: + and_operators = [i for i, j in enumerate(parameters) if j == 'AND'] + or_operators = [i for i, j in enumerate(parameters) if j == 'OR'] + and_or_operators = and_operators + or_operators + and_or_operators.sort() + + i = 0 + part_res = [] + while (i < len(parameters)): + if (i in and_or_operators): + part_res.append(parameters[i]) + elif (i == 0 or i-1 in and_or_operators): # property + property = parameters[i] + path_string = path_strings(property) + elif (i == 1 or i-2 in and_or_operators): # operator + operator = parameters[i] + elif (i == 2 or i-3 in and_or_operators): # start of value + val = [] + while (i < len(parameters) and i not in and_or_operators): + val.append(parameters[i]) + i += 1 + i -= 1 + value = ' '.join(val) + + curr_value, position = find_closest_value(path_string, operator, value, property_type(property)) + + token = self.ope.generate_search_token(path_string + curr_value) + doc_ids = self.ope.search(token, operator_string(operator), position) + part_res.append(doc_ids) + i += 1 + + # solve part_res in save into res + part_res = solve_expression(part_res) + res += part_res + + # solve res + res = solve_expression(res) + + # clear directory, copy encrypted files and decrypt them + self.ope.delete_user_directories() + if (len(res) == 1): + num_of_files = self.ope.copy_encrypted_files_to_user(res[0]) + self.ope.decrypt_documents() + end_time = time.time() + self.info_label.setText('Documents matching query: ' + str(num_of_files) + '.' + 4*' ' + + 'Query took ' + "{0:.1f}".format(1000*(end_time - start_time)) + ' ms.') + + else: + print('res does not have one element.') + else: + self.info_label.setText('Brackets are not set correctly.') + + def connect_button(self, btn): + btn.clicked.connect(lambda: self.button_clicked(btn)) + + def buttons_state(self, buttons, state): + if (state == 'enable'): + for btn in buttons: + btn.setEnabled(True) + elif (state == 'disable'): + for btn in buttons: + btn.setDisabled(True) + + + + +if __name__ == '__main__': + app = QApplication([]) + w = SearchGUI() + w.show() + app.exec_()