From 3bdab827ff6a62b54931b887d9dbed55a93ac61b Mon Sep 17 00:00:00 2001 From: Naumkin Vladimir Date: Fri, 18 Oct 2024 01:45:41 +0300 Subject: [PATCH] Version 2 --- app_local/McEliece_console.py | 22 ++- app_local/McEliece_console_v2.py | 290 ++++++++++++++++++++++++++++++ app_local/cryptosystem_core.py | 25 +++ app_local/cryptosystem_core_v2.py | 267 +++++++++++++++++++++++++++ app_local/readme.txt | 4 +- 5 files changed, 605 insertions(+), 3 deletions(-) create mode 100644 app_local/McEliece_console_v2.py create mode 100644 app_local/cryptosystem_core_v2.py diff --git a/app_local/McEliece_console.py b/app_local/McEliece_console.py index e211fd4..cdc302a 100644 --- a/app_local/McEliece_console.py +++ b/app_local/McEliece_console.py @@ -1,5 +1,6 @@ #pyinstaller -F -i "icon.ico" McEliece_console.py +import hashlib import getpass import random import base64 @@ -33,10 +34,10 @@ def menu(): import cryptosystem_core as core print("\nMcEliece cryptosystem implementation by vovuas2003.\n") print("All necessary txt files must be in utf-8 and located in the directory with this exe program.\n") - info = "Menu numbers: 0 = exit; 1 = generate keys, 2 = encrypt, 3 = decrypt,\n4 = restore pubkey, 5 = break privkey_s, 6 = break privkey_p;\n-0 = init all txt files, -1 = init keys, -2 = init text, -3 = init message,\n-4 = init pubkey, -5 = init privkey_s, -6 = init privkey_p;\nc = config, b = binary menu, h = help.\n" + info = "Menu numbers: 0 = exit; 1 = generate keys, 2 = encrypt, 3 = decrypt,\n4 = restore pubkey, 5 = break privkey_s, 6 = break privkey_p;\n-0 = init all txt files, -1 = init keys, -2 = init text, -3 = init message,\n-4 = init pubkey, -5 = init privkey_s, -6 = init privkey_p;\nc = config, b = binary menu, u = unsafe keygen by password, h = help.\n" err = "Error! Check command info and try again!\n" ok = "Operation successful.\n" - inp = [str(i) for i in range(7)] + ['-' + str(i) for i in range(7)] + ['c', 'b', 'h'] + ['1337', '-1337'] + inp = [str(i) for i in range(7)] + ['-' + str(i) for i in range(7)] + ['c', 'b', 'h', 'u'] + ['1337', '-1337'] print(info) while True: s = input("Menu number: ") @@ -66,6 +67,20 @@ def menu(): elif s == '0': print("\nGood luck!") break + elif s == 'u': + print("WARNING: setting sha256hash(password) mod 2^32 as random seed is VERY unsafe practice!\nIt is better to use menu number 1 for independent random.") + print("This operation will rewrite pubkey.txt, privkey_s.txt and privkey_p.txt; are you sure?") + if(not get_yes_no()): + continue + try: + seed = normalhash(getpass.getpass("Any password for hashing: ")) + G, S, P = core.unsafe_generate(seed) + write_txt("pubkey", G) + write_txt("privkey_s", S) + write_txt("privkey_p", P) + print(ok) + except: + print(err) elif s == '1': print("This operation will rewrite pubkey.txt, privkey_s.txt and privkey_p.txt; are you sure?") if(not get_yes_no()): @@ -386,6 +401,9 @@ def myhash(s, m = 2**61 - 1, p = 257): a = ((a * p) % m + ord(s[i])) % m return a +def normalhash(s): + return int(hashlib.sha256(bytearray(s, 'utf-8')).hexdigest(), 16) + def PT(m, M = 3): if m == 0: print("Iron: 'OK, I will choose the number by myself.'") diff --git a/app_local/McEliece_console_v2.py b/app_local/McEliece_console_v2.py new file mode 100644 index 0000000..cb8b33a --- /dev/null +++ b/app_local/McEliece_console_v2.py @@ -0,0 +1,290 @@ +#pyinstaller -F -i "icon.ico" McEliece_console_v2.py + +import hashlib +import getpass + +def main(): + safe_start() + +def safe_start(): + try: + start_menu() + except: + print("\nUnknown error (maybe ctrl+c), emergency exit!") + +def start_menu(): + f = True + print("\nWhat is the author's nickname?") + #vovuas 2003 + if myhash(getpass.getpass("Letters: ")) != 132782522349988: + f = False + if myhash(getpass.getpass("Digits: ")) != 851912389: + f = False + if f: + print("Authorization successful, wait a bit.") + menu() + else: + print("Permission denied.") + print("\nPress ENTER to exit.", end = '') + input() + +def menu(): + import cryptosystem_core_v2 as ME_core + core = ME_core.McEliece_core() + global_info = "All files are interpreted as raw bytes and must be located in the directory with this executable file.\nDefault filenames with .bin extension: pubkey, privkey_S, privkey_p, plaintext, ciphertext, ciphered_string.\nDon't forget to import (or generate) keys before encryption/decryption and after changing config!\nYou can restore any one key from two another (don't forget to import before).\n" + info = "Menu numbers: 0 = exit, s = print short info, h = print this info, g = print global info, c = change config;\n1 = generate keys, 10 = unsafe generate keys (seed = hash(password)),\n11 = export pubkey, 12 = export privkey_S, 13 = export privkey_p,\n14 = import pubkey, 15 = import privkey_S, 16 = import privkey_p,\n17 = restore pubkey, 18 = restore privkey_S, 19 = restore privkey_p;\n2 = encrypt,\n21 = encrypt non-default filename, 22 = encrypt string from keyboard, 23 = encrypt hided string.\n3 = decrypt,\n31 = decrypt non-default filename, 32 = decrypt string and show on screen\n" + short_info = "0 = exit, s/h/g = print short/extended/global info, c = change config;\n1 = generate keys, 2 = encrypt, 3 = decrypt\n" + err = "Error! Check global info (menu number g) and try again!\n" + ok = "Operation successful.\n" + inp = ['0', 's', 'h', 'g', 'c'] + [str(i) for i in range(1, 4)] + [str(i) for i in range(10, 20)] + [str(i) for i in range(21, 24)] + [str(i) for i in range(31, 33)] + ['1337'] + print("\nMcEliece cryptosystem implementation by vovuas2003. Version 2.\n") + print(global_info) + print(info) + while True: + s = input("Menu number: ") + while s not in inp: + s = input("Wrong menu number; h = help, s = short help: ") + if s == '0': + print("\nGood luck!") + break + elif s == 's': + print(short_info) + elif s == 'h': + print(info) + elif s == 'g': + print(global_info) + elif s == 'c': + n, k = core.get_config() + print("Default config is 255 210, current is " + str(n) + " " + str(k) + ". Change config? Also reset all keys!") + if(not get_yes_no()): + continue + try: + print("Config is two numbers n >= k >= 2; (3 * 5 * 17) mod n = 0. Larger values = larger keys.\nRandomly change (n - k) div 2 bytes during encryption, but add (n - k + 1) bytes to each chunk with len (k - 1).") + n, k = map(int, input("Write n and k separated by a space: ").split()) + core.change_config(n, k) + print(ok) + except: + print(err) + elif s == '1': + print("Reset all keys!") + if(not get_yes_no()): + continue + try: + core.generate_keys() + print(ok) + except: + print(err) + elif s == '10': + print("It is better to use menu number 1 for true random!") + if(not get_yes_no()): + continue + try: + core.generate_keys(normalhash(getpass.getpass("Password for hash: "))) + print(ok) + except: + print(err) + elif s == '11': + print("Rewrite pubkey.bin!") + if(not get_yes_no()): + continue + try: + write_file("pubkey.bin", bytes(core.get_pubkey())) + print(ok) + except: + print(err) + elif s == '12': + print("Rewrite privkey_S.bin!") + if(not get_yes_no()): + continue + try: + write_file("privkey_S.bin", bytes(core.get_privkey_S())) + print(ok) + except: + print(err) + elif s == '13': + print("Rewrite privkey_p.bin!") + if(not get_yes_no()): + continue + try: + write_file("privkey_p.bin", bytes(core.get_privkey_p())) + print(ok) + except: + print(err) + elif s == '14': + print("Load pubkey.bin into cryptosystem.") + if(not get_yes_no()): + continue + try: + core.set_pubkey([int(i) for i in read_file("pubkey.bin")]) + print(ok) + except: + print(err) + elif s == '15': + print("Load privkey_S.bin into cryptosystem.") + if(not get_yes_no()): + continue + try: + core.set_privkey_S([int(i) for i in read_file("privkey_S.bin")]) + print(ok) + except: + print(err) + elif s == '16': + print("Load privkey_p.bin into cryptosystem.") + if(not get_yes_no()): + continue + try: + core.set_privkey_p([int(i) for i in read_file("privkey_p.bin")]) + print(ok) + except: + print(err) + elif s == '17': + print("Rewrite pubkey inside cryptosystem.") + if(not get_yes_no()): + continue + try: + core.restore_pubkey() + print(ok) + except: + print(err) + elif s == '18': + print("Rewrite privkey_S inside cryptosystem.") + if(not get_yes_no()): + continue + try: + core.restore_privkey_S() + print(ok) + except: + print(err) + elif s == '19': + print("Rewrite privkey_p inside cryptosystem.") + if(not get_yes_no()): + continue + try: + core.restore_privkey_p() + print(ok) + except: + print(err) + elif s == '2': + print("Need plaintext.bin, rewrite ciphertext.bin!") + if(not get_yes_no()): + continue + try: + write_file("ciphertext.bin", bytes(core.encrypt([int(i) for i in read_file("plaintext.bin")]))) + print(ok) + except: + print(err) + elif s == '21': + print("Type names with extensions!") + if(not get_yes_no()): + continue + try: + inp_name = input("File to encrypt: ") + out_name = input("Name for save: ") + write_file(out_name, bytes(core.encrypt([int(i) for i in read_file(inp_name)]))) + print(ok) + except: + print(err) + elif s == '22': + print("Rewrite ciphered_string.bin!") + if(not get_yes_no()): + continue + try: + inp_str = input("String to encrypt: ") + write_file("ciphered_string.bin", bytes(core.encrypt([int(i) for i in inp_str.encode('utf-8')]))) + print(ok) + except: + print(err) + elif s == '23': + print("Rewrite ciphered_string.bin!") + if(not get_yes_no()): + continue + try: + inp_str = getpass.getpass("Hided string to encrypt: ") + write_file("ciphered_string.bin", bytes(core.encrypt([int(i) for i in inp_str.encode('utf-8')]))) + print(ok) + except: + print(err) + elif s == '3': + print("Need ciphertext.bin, rewrite plaintext.bin!") + if(not get_yes_no()): + continue + try: + write_file("plaintext.bin", bytes(core.decrypt([int(i) for i in read_file("ciphertext.bin")]))) + print(ok) + except: + print(err) + elif s == '31': + print("Type names with extensions!") + if(not get_yes_no()): + continue + try: + inp_name = input("File to decrypt: ") + out_name = input("Name for save: ") + write_file(out_name, bytes(core.decrypt([int(i) for i in read_file(inp_name)]))) + print(ok) + except: + print(err) + elif s == '32': + print("Need ciphered_string.bin!") + if(not get_yes_no()): + continue + try: + print("Deciphered string: " + bytes(core.decrypt([int(i) for i in read_file("ciphered_string.bin")])).decode('utf-8')) + print(ok) + except: + print(err) + elif s == '1337': + PT() + else: + print("Impossible behaviour, mistake in source code, emergency exit!\nThe string allowed in the inp array is not bound to the call of any function!") + break + +def write_file(name, data): + with open(name, "wb") as f: + f.write(data) + +def read_file(name): + with open(name, "rb") as f: + data = f.read() + return data + +def get_yes_no(): + s = input("Confirm (0 = go back, 1 = continue): ") + while s not in ['0', '1']: + s = input("Try again, 0 or 1: ") + return int(s) + +def myhash(s, m = 2**61 - 1, p = 257): + a = 0 + for i in range(len(s)): + a = ((a * p) % m + ord(s[i])) % m + return a + +def normalhash(s): + return int(hashlib.sha256(bytearray(s, 'utf-8')).hexdigest(), 16) + +def PT(m = -3, M = 3): + if m == 0 or abs(m) > M: + print("PT!") + return + s = "PT!" + p = " " + f = False + if m < 0: + s, p = p, s + m *= -1 + f = True + print() + if f: + print(p * (10 * m + 1)) + print(p + (s * 3 + p + s * 3 + p + s + p) * m) + print(p + (s + p + s + p * 2 + s + p * 2 + s + p) * m) + print(p + (s * 3 + p * 2 + s + p * 2 + s + p) * m) + print(p + (s + p * 4 + s + p * 4) * m) + print(p + (s + p * 4 + s + p * 2 + s + p) * m) + if f: + print(p * (10 * m + 1)) + print() + +if __name__ == "__main__": + main() diff --git a/app_local/cryptosystem_core.py b/app_local/cryptosystem_core.py index 1831431..4d49b63 100644 --- a/app_local/cryptosystem_core.py +++ b/app_local/cryptosystem_core.py @@ -10,6 +10,7 @@ ''' import cryptosystem_core as core G, S, P = core.generate() +G, S, P = core.unsafe_generate(seed) #e.g. seed = hash(password) msg = core.encrypt(G, text) text = core.decrypt(S, P, msg) G = core.restore_G(S, P) @@ -78,6 +79,30 @@ def generate_P(): P[i, p[i]] = 1 return P, p +def unsafe_generate(h): + seed = h % (2**32) + S = unsafe_generate_S(seed) + G = rs.G + P, p = unsafe_generate_P(seed) + G_ = S @ G @ P + return write_pubkey(G_), write_privkey_s(S), write_privkey_p(p) + +def unsafe_generate_S(seed): + pseudo = np.random.RandomState(seed) + S = GF(pseudo.randint(0, order, (k, k))) + while np.linalg.det(S) == 0: + S = GF(pseudo.randint(0, order, (k, k))) + return S + +def unsafe_generate_P(seed): + pseudo = np.random.RandomState(seed) + p = [i for i in range(n)] + pseudo.shuffle(p) + P = GF.Zeros((n, n)) + for i in range(n): + P[i, p[i]] = 1 + return P, p + def write_pubkey(G_): rows = [bytes(row) for row in G_] row = bytes([i for j in rows for i in j]) diff --git a/app_local/cryptosystem_core_v2.py b/app_local/cryptosystem_core_v2.py new file mode 100644 index 0000000..eaf3070 --- /dev/null +++ b/app_local/cryptosystem_core_v2.py @@ -0,0 +1,267 @@ +#McEliece cryptosystem implementation by vovuas2003 +#v2. Power of Python OOP. + +#Usage (check main function or class implementation): +#G = pubkey; S and p = privkeys; text = plaintext; msg = encrypted text. +#All these variables must be lists of integers from 0 to 255. Easy to use binary files (check console_v2). +#It is possible to rewrite my class McEliece_core and add changing the order of Galois Field (check galois lib docs) to M, so all these variables will be lists of integers from 0 to M-1. +#But it will be impossible to save keys and encrypted texts as binary files. Also you will need to add polynomial database for pyinstaller. + +import numpy as np +import galois +import random + +#import cryptosystem_core_v2 as ME_core +#core = ME_core.McEliece_core() + +def main(): #comment "return" for testing + return + core = McEliece_core() + n, k = core.get_config() + print(n, k) + core.change_config(5, 3) #check comments in class implementation to understand possible values + n, k = core.get_config() + print(n, k) + print() + core.generate_keys() #true random + print(core.get_pubkey()) + print(core.get_privkey_S()) + print(core.get_privkey_p()) + print() + core.generate_keys(sum([ord(i) for i in list("password")])) #very simple seed + G = core.get_pubkey() + S = core.get_privkey_S() + p = core.get_privkey_p() + print(G) + print(S) + print(p) + print() + core.change_config(5, 3) #unset all keys inside core + core.set_privkey_S(S) + core.set_privkey_p(p) + core.restore_pubkey() + print(core.get_pubkey() == G) + core.change_config(5, 3) #unset all keys inside core + core.set_pubkey(G) + core.set_privkey_p(p) + core.restore_privkey_S() + print(core.get_privkey_S() == S) + core.change_config(5, 3) #unset all keys inside core + core.set_pubkey(G) + core.set_privkey_S(S) + core.restore_privkey_p() + print(core.get_privkey_p() == p) + print() + for j in range(2): + text = [i + 1 for i in range(5)] + msg = core.encrypt(text) + print(msg) + text = core.decrypt(msg) + print(text) + print() + print("All tests are finished!") + +class McEliece_core: + def __init__(self): + self._order = 256 #p^m = 2**8; encryption of each byte + self._n = 255 #(order - 1) mod n = 0 for Reed Solomon code; 255 = 3 * 5 * 17 = (order - 1) + self._k = 210 #2 <= k <= n; randomly change (n - k) div 2 bytes during encryption, but add (n - k + 1) bytes to each chunk with len (k - 1); k != 1 for padding function; k almost equal to n is very bad because of small amount of randomly changed bytes (k == n -> privkey for decryption == numpy.linalg.inv(pubkey)) + self._GF = galois.GF(2, 8, irreducible_poly = "x^8 + x^4 + x^3 + x^2 + 1", primitive_element = "x", verify = False) #hardcoded galois.GF(2**8).properties for pyinstaller + self._rs = galois.ReedSolomon(self._n, self._k, field = self._GF) + self._G = self._GF.Zeros((self._k, self._n)) #pubkey + self._S = self._GF.Zeros((self._k, self._k)) #1st part of privkey + self._S_inv = self._GF.Zeros((self._k, self._k)) #for decryption + self._P = self._GF.Zeros((self._n, self._n)) #2nd part of privkey + self._P_inv = self._GF.Zeros((self._n, self._n)) #for decryption + self._p = [0 for i in range(self._n)] #compact format of P as a permutation array + def change_config(self, n, k): + try: + if k < 2: + raise Exception() + rs = galois.ReedSolomon(n, k, field = self._GF) + except: + raise Exception() + else: + self._n = n + self._k = k + self._rs = rs + #Also unset all keys! + self._G = self._GF.Zeros((self._k, self._n)) + self._S = self._GF.Zeros((self._k, self._k)) + self._S_inv = self._GF.Zeros((self._k, self._k)) + self._P = self._GF.Zeros((self._n, self._n)) + self._P_inv = self._GF.Zeros((self._n, self._n)) + self._p = [0 for i in range(self._n)] + def get_config(self): + return self._n, self._k + def generate_keys(self, seed = None): + if seed == None: + self._generate_S() + self._generate_P() + elif type(seed) != int: + raise Exception() + else: + self._unsafe_generate_S(seed % (2**32)) + self._unsafe_generate_P(seed % (2**32)) + self._G = self._S @ self._rs.G @ self._P + def get_pubkey(self): + return [int(i) for j in self._G for i in j] + def get_privkey_S(self): + return [int(i) for j in self._S for i in j] + def get_privkey_p(self): + return self._p + def set_pubkey(self, G): + try: + G = [G[i - self._n : i] for i in range(self._n, self._n * self._k + self._n, self._n)] + G = self._GF(G) + except: + raise Exception() + else: + self._G = G + def set_privkey_S(self, S): + try: + S = [S[i - self._k : i] for i in range(self._k, self._k * self._k + self._k, self._k)] + S = self._GF(S) + S_inv = np.linalg.inv(S) + except: + raise Exception() + else: + self._S = S + self._S_inv = S_inv + def set_privkey_p(self, p): + if sorted(p) != [i for i in range(self._n)]: + raise Exception() + else: + self._p = p + self._P = self._GF.Zeros((self._n, self._n)) + self._P_inv = self._GF.Zeros((self._n, self._n)) + for i in range(self._n): + self._P[i, p[i]] = 1 + self._P_inv[p[i], i] = 1 + def restore_pubkey(self): + self._G = self._S @ self._rs.G @ self._P + def restore_privkey_S(self): + S = self._G @ self._P_inv + S = self._GF(S[:, : self._k]) + try: + S_inv = np.linalg.inv(S) + except: + raise Exception() + self._S = S + self._S_inv = S_inv + def restore_privkey_p(self): + G = self._rs.G + G = G.T + G = [[int(i) for i in j] for j in G] + GP = self._S_inv @ self._G + GP = GP.T + GP = [[int(i) for i in j] for j in GP] + p = [0 for i in range(self._n)] + f = False + for i in range(self._n): + f = False + for j in range(self._n): + if G[i] == GP[j]: + p[i] = j + f = True + break + if f: + continue + raise Exception() + self._p = p + self._P = self._GF.Zeros((self._n, self._n)) + self._P_inv = self._GF.Zeros((self._n, self._n)) + for i in range(self._n): + self._P[i, p[i]] = 1 + self._P_inv[p[i], i] = 1 + def encrypt(self, text): + try: + out = [] + while len(text) > self._k - 1: + tmp = text[: self._k - 1] + text = text[self._k - 1 :] + out += self._encrypt_one(tmp) + out += self._encrypt_one(text) + return out + except: + raise Exception() + def decrypt(self, msg): + try: + msg = [msg[i - self._n : i] for i in range(self._n, len(msg) + self._n, self._n)] + msg = [self._decrypt_one(self._GF(i)) for i in msg] + return [i for j in msg for i in j] + except: + raise Exception() +#End of top-level functions, please do NOT use functions below without understanding! + def _generate_S(self): + S = self._GF.Random((self._k, self._k)) + while np.linalg.det(S) == 0: + S = self._GF.Random((self._k, self._k)) + self._S = S + self._S_inv = np.linalg.inv(S) + def _generate_P(self): + r = [i for i in range(self._n)] + p = [] + for i in range(self._n): + p.append(r.pop(random.randint(0, self._n - 1 - i))) + self._p = p + self._P = self._GF.Zeros((self._n, self._n)) + self._P_inv = self._GF.Zeros((self._n, self._n)) + for i in range(self._n): + self._P[i, p[i]] = 1 + self._P_inv[p[i], i] = 1 + def _unsafe_generate_S(self, seed): + pseudo = np.random.RandomState(seed) + S = self._GF(pseudo.randint(0, self._order, (self._k, self._k))) + while np.linalg.det(S) == 0: + S = self._GF(pseudo.randint(0, self._order, (self._k, self._k))) + self._S = S + self._S_inv = np.linalg.inv(S) + def _unsafe_generate_P(self, seed): + pseudo = np.random.RandomState(seed) + p = [i for i in range(self._n)] + pseudo.shuffle(p) + self._p = p + self._P = self._GF.Zeros((self._n, self._n)) + self._P_inv = self._GF.Zeros((self._n, self._n)) + for i in range(self._n): + self._P[i, p[i]] = 1 + self._P_inv[p[i], i] = 1 + def _encrypt_one(self, text): + msg = self._pad_message(text) + m = self._GF(msg) + c = m.T @ self._G + t = (self._n - self._k) // 2 + z = np.zeros(self._n, dtype = int) + p = [i for i in range(self._n)] + for i in range(t): + ind = p.pop(random.randint(0, self._n - 1 - i)) + z[ind] = random.randint(1, self._order - 1) + c = c + self._GF(z) + return [int(i) for i in c] + def _decrypt_one(self, msg): + msg = msg @ self._P_inv + msg, e = self._rs.decode(msg, errors = True) + if e == -1: + raise Exception() + msg = msg @ self._S_inv + msg = [int(i) for i in msg] + try: + msg = self._unpad_message(msg) + except: + raise Exception() + return msg + def _pad_message(self, msg): + last_value = self._k - (len(msg) % self._k) + return msg + [last_value] * last_value + def _unpad_message(self, msg): + last_value = msg[-1] + if last_value >= self._k or last_value <= 0: + raise Exception() + for i in range(1, last_value + 1): + if msg[-i] != last_value: + raise Exception() + return msg[: -last_value] + +if __name__ == "__main__": + main() diff --git a/app_local/readme.txt b/app_local/readme.txt index 5985c7a..03272d3 100644 --- a/app_local/readme.txt +++ b/app_local/readme.txt @@ -2,6 +2,8 @@ McEliece cryptosystem implementation by vovuas2003 Required Python libraries: numpy, galois. +UPDATE: version 2 is available. Check cryptosystem_core_v2 and McEliece_console_v2. + All cryptosystem functions are implemented in cryptosystem_core.py, just import it into your project and enjoy! For example, I coded a console menu (that works with text in txt files and also with any files in binary mode) and a GUI app (for text encryption). @@ -10,4 +12,4 @@ But it is NOT compilation, so exe file will be quite large. The pdf presentation in Russian contains a bit of theory about the Mceliece cryptosystem. icon.ico is an optional file for pyinstaller -old_portable.py can support another order of galois field, but saves raw integers and does not specify utf-8 encoding for strings and txt files \ No newline at end of file +old_portable.py can support another order of galois field, but saves raw integers and does not specify utf-8 encoding for strings and txt files