global refactoring

main
Naumkin Vladimir 7 months ago
parent f1d912fb5d
commit 1fcbcc98c5

@ -0,0 +1,130 @@
#pyinstaller -F -w -i "icon.ico" McEliece_GUI.py
import cryptosystem_core as core
import tkinter as tk
def mymessagebox(fontsize, butsize, mytitle, mytext):
toplevel = tk.Toplevel(window)
toplevel.title(mytitle)
toplevel.geometry(f"600x400+{window.winfo_x()+40}+{window.winfo_y()+15}")
toplevel.resizable(False, False)
l = tk.Label(toplevel, text = mytext, font = ("TkDefaultFont", fontsize))
l.pack()
b = tk.Button(toplevel, text = "Close", command = toplevel.destroy, width = 10, font = ("TkDefaultFont", butsize))
b.pack()
def show_error():
mymessagebox(30, 30, "Error!", "ERROR!\n\nPress help button\nto show common\nmistakes in usage.\n")
def button0_click():
mymessagebox(15, 15, "Source code: github.com/vovuas2003/McEliece", "Program can work slow and doesn't response, it is normal.\n1st line = current configuration of cryptosystem.\n2nd line = public key (send to anybody).\n3rd line = first half (s) of private key (keep in secret!).\n4th line = second half (p) of private key (keep in secret!).\nBig field = place for writing text or pasting message.\nGenerate keys = rewrite all 3 keys.\nEncrypt = pubkey is required.\nDecrypt = both privkeys are required.\nYou can restore any key from the other two.\nConfig = change cryptosystem parameters n and k.\n(two numbers separated by a space, default: 255 210)\n(255 mod n = 0; 255 = 3 * 5 * 17)\n(2 <= k <= n; (n - k) div 2 bytes are randomly changed)\nPT! = just easter egg :)")
def button1_click():
try:
new_text1, new_text2, new_text3 = core.generate()
entry1.delete(0, tk.END)
entry1.insert(0, new_text1)
entry2.delete(0, tk.END)
entry2.insert(0, new_text2)
entry3.delete(0, tk.END)
entry3.insert(0, new_text3)
except:
show_error()
def button2_click():
try:
G = entry1.get()
text = entry4.get("1.0", "end-1c")
new_text4 = core.encrypt(G, text)
entry4.delete("1.0", tk.END)
entry4.insert("1.0", new_text4)
except:
show_error()
def button3_click():
try:
S = entry2.get()
P = entry3.get()
msg = entry4.get("1.0", "end-1c")
new_text4 = core.decrypt(S, P, msg)
entry4.delete("1.0", tk.END)
entry4.insert("1.0", new_text4)
except:
show_error()
def button4_click():
try:
S = entry2.get()
P = entry3.get()
new_text1 = core.restore_G(S, P)
entry1.delete(0, tk.END)
entry1.insert(0, new_text1)
except:
show_error()
def button5_click():
try:
G = entry1.get()
P = entry3.get()
new_text2 = core.break_S(G, P)
entry2.delete(0, tk.END)
entry2.insert(0, new_text2)
except:
show_error()
def button6_click():
try:
G = entry1.get()
S = entry2.get()
new_text3 = core.break_P(G, S)
entry3.delete(0, tk.END)
entry3.insert(0, new_text3)
except:
show_error()
def button7_click():
try:
core.config(entry0.get())
except:
entry0.delete(0, tk.END)
entry0.insert(0, str(core.n) + " " + str(core.k))
show_error()
def button8_click():
mymessagebox(200, 15, "PT!" * 25, "PT!")
window = tk.Tk()
window.title("McEliece by vovuas2003")
frame_buttons = tk.Frame(window)
frame_buttons.pack(side = tk.TOP, fill = tk.X)
buttons = []
but_names = ["help", "generate keys", "encrypt text", "decrypt message", "pubkey = s + p", "s = pubkey + p", "p = pubkey + s", "change config", "PT!"]
but_com = [button0_click, button1_click, button2_click, button3_click, button4_click, button5_click, button6_click, button7_click, button8_click]
for i in range(9):
buttons.append(tk.Button(frame_buttons, text = but_names[i], command = but_com[i]))
for button in buttons:
button.pack(side = tk.LEFT)
entry0 = tk.Entry(window, width = 50)
entry1 = tk.Entry(window, width = 50)
entry2 = tk.Entry(window, width = 50)
entry3 = tk.Entry(window, width = 50)
entry4 = tk.Text(window, height = 20, width = 50)
entry0.insert(0, "255 210 (default config)")
entry1.insert(0, "pubkey")
entry2.insert(0, "privkey s")
entry3.insert(0, "privkey p")
entry4.insert("1.0", "Write a text or paste a message here!\nAll utf-8 symbols are supported, e.g. alt codes and Russian text.\n\n(use ctrl+a before crtl+v or ctrl+c)\n(switch keyboard layout to english to use these shortcuts)\n\nYou can't resize window, but can scroll down.\nProgram can work slow, don't kill it please :)")
entry0.pack()
entry1.pack()
entry2.pack()
entry3.pack()
entry4.pack(fill = tk.BOTH, expand = True)
window.resizable(False, False)
window.mainloop()

@ -0,0 +1,262 @@
#pyinstaller -F -i "icon.ico" McEliece_console.py
import getpass
import random
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("\nA soldering iron is into a black hole.")
#thermorectal cryptanalysis
if myhash(getpass.getpass("Login: ")) != 1314399736851798576:
f = False
if myhash(getpass.getpass("Password: ")) != 192441972608755898:
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 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, 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', 'h'] + ['1337']
print(info)
while True:
s = input("Menu number: ")
while s not in inp:
s = input("Wrong menu number, h = help: ")
if s == 'h':
print(info)
elif s == 'c':
print("Default config is 255 210, current is " + str(core.n) + " " + str(core.k) + ". Change config?")
if(not get_yes_no()):
continue
try:
print("Config is two numbers n >= k >= 2; (3 * 5 * 17) mod n = 0.")
core.config(input("Write n and k separated by a space: "))
print(ok)
except:
print(err)
elif s == '0':
print("\nGood luck!")
break
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()):
continue
try:
G, S, P = core.generate()
write_txt("pubkey", G)
write_txt("privkey_s", S)
write_txt("privkey_p", P)
print(ok)
except:
print(err)
elif s == '2':
print("Write your text into text.txt; pubkey.txt is required, message.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
G = read_txt("pubkey")
text = read_txt("text")
msg = core.encrypt(G, text)
write_txt("message", msg)
print(ok)
except:
print(err)
elif s == '3':
print("You need message.txt, privkey_s.txt and privkey_p.txt; text.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
S = read_txt("privkey_s")
P = read_txt("privkey_p")
msg = read_txt("message")
text = core.decrypt(S, P, msg)
write_txt("text", text)
print(ok)
except:
print(err)
elif s == '4':
print("You need privkey_s.txt and privkey_p.txt; pubkey.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
S = read_txt("privkey_s")
P = read_txt("privkey_p")
G = core.restore_G(S, P)
write_txt("pubkey", G)
print(ok)
except:
print(err)
elif s == '5':
print("You need pubkey.txt and privkey_p.txt; privkey_s.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
G = read_txt("pubkey")
P = read_txt("privkey_p")
S = core.break_S(G, P)
write_txt("privkey_s", S)
print(ok)
except:
print(err)
elif s == '6':
print("You need pubkey.txt and privkey_s.txt; privkey_p.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
G = read_txt("pubkey")
S = read_txt("privkey_s")
P = core.break_P(G, S)
write_txt("privkey_p", P)
print(ok)
except:
print(err)
elif s == '-0':
print("Create (or make empty) all 5 necessary txt files in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("pubkey", "")
write_txt("privkey_s", "")
write_txt("privkey_p", "")
write_txt("text", "")
write_txt("message", "")
print(ok)
except:
print(err)
elif s == '-1':
print("Create (or make empty) all 3 keys txt files in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("pubkey", "")
write_txt("privkey_s", "")
write_txt("privkey_p", "")
print(ok)
except:
print(err)
elif s == '-2':
print("Create (or make empty) text.txt in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("text", "")
print(ok)
except:
print(err)
elif s == '-3':
print("Create (or make empty) message.txt in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("message", "")
print(ok)
except:
print(err)
elif s == '-4':
print("Create (or make empty) pubkey.txt in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("pubkey", "")
print(ok)
except:
print(err)
elif s == '-5':
print("Create (or make empty) privkey_s.txt in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("privkey_s", "")
print(ok)
except:
print(err)
elif s == '-6':
print("Create (or make empty) privkey_p.txt in right utf-8 encoding.")
if(not get_yes_no()):
continue
try:
write_txt("privkey_p", "")
print(ok)
except:
print(err)
elif s == '1337':
c = input("Move the soldering iron into the black hole number: ")
try:
PT(int(c))
except:
print("Iron: 'I don't know this hole.'")
continue
else:
print("Impossible behaviour, mistake in source code!\nThe string allowed in the inp array is not bound to the call of any function!")
break
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 PT(m, M = 3):
if m == 0:
print("Iron: 'OK, I will choose the number by myself.'")
while m == 0:
m = random.randint(-M, M)
s = "PT!"
p = " "
f = False
if m < 0:
s, p = p, s
m *= -1
f = True
if m > M:
print("Iron: 'Are you sure to move me so far?'")
if(not get_yes_no()):
return
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()
def write_txt(name, string):
with open(name + ".txt", "w", encoding = "utf-8") as f:
f.write(string)
def read_txt(name):
with open(name + ".txt", "r", encoding = "utf-8") as f:
out = f.read()
return out
if __name__ == "__main__":
main()

@ -1,61 +0,0 @@
#break cryptosystem if know a half of private key
import numpy as np
import galois
import pubkey
import privkey
import message
n = 127
k = 32
order = 2 ** 7
GF = galois.GF(order)
def main():
G_ = GF(pubkey.get()) #we need to know a public matrix
P = GF(privkey.get_P()) #and P - only one of two private matrices
S = break_S(P, G_) #to calculate S - the second part of private key
c = GF(message.get())
print(decode(S, P, c)) #and decode the message
def unpad_message(msg):
padding_byte = msg[-1]
for i in range(1, padding_byte + 1):
if msg[-i] != padding_byte:
raise ValueError("Incorrect padding!")
return msg[:-padding_byte]
def my_fix(A):
#make square matrix by deleting right columns
l = len(A)
r = GF.Zeros((l, l))
for i in range(l):
for j in range(l):
r[i][j] = A[i][j]
return r
def decode(S, P, c):
rs = galois.ReedSolomon(n, k, field=GF)
c = c @ np.linalg.inv(P)
c = rs.decode(c)
c = c @ np.linalg.inv(S)
c = [int(i) for i in c]
c = unpad_message(c)
c = bytes(c)
c = c.decode()
return c
def break_S(P, G_):
return my_fix(G_ @ np.linalg.inv(P)) #works for Reed-Solomon
#G_ = S @ G @ P
rs = galois.ReedSolomon(n, k, field=GF)
G = rs.G
G_ = G_ @ np.linalg.inv(P)
G_ = my_fix(G_)
G = my_fix(G) #returns E because we use Reed-Solomon algo
S = G_ @ np.linalg.inv(G)
return S
if __name__ == "__main__":
main()

@ -1,24 +1,58 @@
#G = pubkey; S and P = privkeys; text = plaintext; msg = encrypted text #McEliece cryptosystem implementation by vovuas2003
#all these variables are strings
#Usage: #Usage:
#G, S, P = generate() #G = pubkey; S and P = privkeys; text = plaintext; msg = encrypted text
#msg = encrypt(G, text) #all these variables are strings, so it's easy to integrate this code into any project (check GUI and console examples)
#text = decrypt(S, P, msg) #text must be in utf-8 encoding (e.g. force encoding when open txt files, check console example)
#G = restore_G(S, P) #keys and messages are saved as base64 strings
#S = break_S(G, P)
#P = break_P(G, S) '''
import cryptosystem_core as core
G, S, P = core.generate()
msg = core.encrypt(G, text)
text = core.decrypt(S, P, msg)
G = core.restore_G(S, P)
S = core.break_S(G, P)
P = core.break_P(G, S)
core.config("255 210") #this is the default configuration, NO NEED TO WRITE THIS LINE FOR INITIALIZATION, it is just an example of using the function
#these parameters n and k affect the length of the keys and the number of erroneous bytes in the message
#see the comments below to understand the requirements for n and k
'''
#if you want to figure out how the code below works, keep in mind that
#G_ is a pubkey, G is a Reed Solomon code matrix and P is saved as permutation array
#to use the core of the cryptosystem, you don't need to think about it, just write the code as in the example above
import numpy as np import numpy as np
import galois import galois
import random import random
import base64 import base64
order = 256 order = 256 #p^m = 2**8; encrypt each byte and save in base64 format
n = 255 n = 255 #(order - 1) mod n = 0 for Reed Solomon code; 255 = 3 * 5 * 17 = (order - 1)
k = 210 k = 210 #2 <= k <= n; randomly change (n - k) div 2 bytes during encryption
GF = galois.GF(2, 8, irreducible_poly = "x^8 + x^4 + x^3 + x^2 + 1", primitive_element = "x", verify = False) 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
rs = galois.ReedSolomon(n, k, field = GF) rs = galois.ReedSolomon(n, k, field = GF)
def main(): #for testing
pass
def config(string):
global n
global k
global rs
try:
nn, kk = map(int, string.split()[:2])
if kk < 2:
raise Exception()
rrss = galois.ReedSolomon(nn, kk, field = GF)
except:
raise Exception()
else:
n = nn
k = kk
rs = rrss
def generate(): def generate():
S = generate_S() S = generate_S()
G = rs.G G = rs.G
@ -44,17 +78,16 @@ def generate_P():
def write_pubkey(G_): def write_pubkey(G_):
rows = [bytes(row) for row in G_] rows = [bytes(row) for row in G_]
output = "".join([base64.b64encode(row).decode() for row in rows]) row = bytes([i for j in rows for i in j])
return output return base64.b64encode(row).decode()
def write_privkey_s(S): def write_privkey_s(S):
rows = [bytes(row) for row in S] rows = [bytes(row) for row in S]
output = "".join([base64.b64encode(row).decode() for row in rows]) row = bytes([i for j in rows for i in j])
return output return base64.b64encode(row).decode()
def write_privkey_p(p): def write_privkey_p(p):
output = base64.b64encode(bytes(p)).decode() return base64.b64encode(bytes(p)).decode()
return output
def read_pubkey(out): def read_pubkey(out):
out = [int(i) for i in base64.b64decode(out)] out = [int(i) for i in base64.b64decode(out)]
@ -95,14 +128,14 @@ def unpad_message(msg):
def encrypt(key, text): def encrypt(key, text):
G_ = GF(read_pubkey(key)) G_ = GF(read_pubkey(key))
text = text.encode() text = text.encode("utf-8")
out = "" out = bytes()
while len(text) > k - 1: while len(text) > k - 1:
tmp = text[: k - 1] tmp = text[: k - 1]
text = text[k - 1 :] text = text[k - 1 :]
out += encrypt_one(G_, tmp) out += encrypt_one(G_, tmp)
out += encrypt_one(G_, text) out += encrypt_one(G_, text)
return out return base64.b64encode(out).decode()
def encrypt_one(G_, text): def encrypt_one(G_, text):
msg = pad_message(text, k) msg = pad_message(text, k)
@ -116,7 +149,7 @@ def encrypt_one(G_, text):
z[ind] += random.randint(1, order - 1) z[ind] += random.randint(1, order - 1)
z[ind] %= order z[ind] %= order
c = c + GF(z) c = c + GF(z)
return base64.b64encode(bytes(c)).decode() return bytes(c)
def decrypt(s, p, msg): def decrypt(s, p, msg):
S_inv = np.linalg.inv(GF(read_privkey_s(s))) S_inv = np.linalg.inv(GF(read_privkey_s(s)))
@ -125,7 +158,7 @@ def decrypt(s, p, msg):
msg = [msg[i - n : i] for i in range(n, len(msg) + n, n)] msg = [msg[i - n : i] for i in range(n, len(msg) + n, n)]
msg = [decrypt_one(S_inv, P_inv, GF(i)) for i in msg] msg = [decrypt_one(S_inv, P_inv, GF(i)) for i in msg]
msg = [i for j in msg for i in j] msg = [i for j in msg for i in j]
msg = bytes(msg).decode() msg = bytes(msg).decode("utf-8")
return msg return msg
def decrypt_one(S_inv, P_inv, msg): def decrypt_one(S_inv, P_inv, msg):
@ -176,3 +209,6 @@ def break_P(key, s):
#print("Wrong pubkey and privkey_s combination!") #print("Wrong pubkey and privkey_s combination!")
raise Exception() raise Exception()
return write_privkey_p(p) return write_privkey_p(p)
if __name__ == "__main__":
main()

@ -1,37 +0,0 @@
import numpy as np
import galois
import privkey
import message
n = 127
k = 32
order = 2 ** 7
GF = galois.GF(order)
def main():
S = GF(privkey.get_S())
P = GF(privkey.get_P())
c = GF(message.get())
print(decode(S, P, c))
def unpad_message(msg):
padding_byte = msg[-1]
for i in range(1, padding_byte + 1):
if msg[-i] != padding_byte:
raise ValueError("Incorrect padding!")
return msg[:-padding_byte]
def decode(S, P, c):
rs = galois.ReedSolomon(n, k, field=GF)
c = c @ np.linalg.inv(P)
c = rs.decode(c)
c = c @ np.linalg.inv(S)
c = [int(i) for i in c]
c = unpad_message(c)
c = bytes(c)
c = c.decode()
return c
if __name__ == "__main__":
main()

@ -1,46 +0,0 @@
import numpy as np
import galois
import random
import pubkey
n = 127
k = 32
order = 2 ** 7
GF = galois.GF(order)
def main():
G_ = GF(pubkey.get())
print("Message to encode (max len = k-1 = 31):")
message = input()
if len(message) > k-1:
print("Message is too long!")
return
ct = encrypt(G_, message)
ct = list(map(int, ct))
export(ct)
print("Done!")
def pad_message(msg: bytes, pad_size: int) -> list[int]:
padding = pad_size - (len(msg) % pad_size)
return list(msg + padding.to_bytes() * padding)
def encrypt(G_, text):
msg = pad_message(text.encode(), k)
m = GF(msg)
c = m.T @ G_
t = (n - k) // 2
z = np.zeros(n, dtype = int)
p = [i for i in range(n)]
for i in range(t):
z[p.pop(random.randint(0, n - 1 - i))] = random.randint(0, order - 1)
return c + GF(z)
def export(ct):
output = "ct = [ " + ", ".join([str(int(cell)) for cell in ct]) + " ]"
with open("message.py", "w") as f:
f.write(output)
f.write("\ndef get():\n\treturn ct")
if __name__ == "__main__":
main()

@ -1,58 +0,0 @@
import numpy as np
import galois
import random
n = 127
k = 32
order = 2 ** 7
GF = galois.GF(order)
def main():
S = generate_S()
G = generate_G()
P = generate_P()
G_ = S @ G @ P
export_pubkey(G_)
export_privkey(S, P)
print("Done!")
def generate_S():
S = GF.Random((k, k))
while np.linalg.det(S) == 0:
S = GF.Random((k, k))
return S
def generate_G():
rs = galois.ReedSolomon(n, k, field=GF)
G = rs.G
return G
def generate_P():
r = [i for i in range(n)]
p = []
for i in range(n):
p.append(r.pop(random.randint(0, n - 1 - i)))
P = GF.Zeros((n, n))
for i in range(n):
P[i, p[i]] = 1
return P
def export_pubkey(G_):
rows = [", ".join([str(int(cell)) for cell in row]) for row in G_]
output = "G_ = [ " + ",\n".join([f"[{row}]" for row in rows]) + " ]"
with open("pubkey.py", "w") as f:
f.write(output)
f.write("\ndef get():\n\treturn G_")
def export_privkey(S, P):
rows = [", ".join([str(int(cell)) for cell in row]) for row in S]
output = "S = [ " + ",\n".join([f"[{row}]" for row in rows]) + " ]\n"
rows = [", ".join([str(int(cell)) for cell in row]) for row in P]
output += "P = [ " + ",\n".join([f"[{row}]" for row in rows]) + " ]\n"
with open("privkey.py", "w") as f:
f.write(output)
f.write("\ndef get_S():\n\treturn S")
f.write("\ndef get_P():\n\treturn P")
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -1,358 +0,0 @@
#pip install pyinstaller
#pyinstaller -F -i "icon.ico" portable.py
#exe into dist folder
import numpy as np
import galois
import random
import getpass
import base64
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("\nA soldering iron is into a black hole.")
#thermorectal cryptanalysis
if myhash(getpass.getpass("Login: ")) != 1314399736851798576:
f = False
if myhash(getpass.getpass("Password: ")) != 192441972608755898:
f = False
if f:
print("Authorization successful, wait a bit.")
menu()
else:
print("Permission denied.")
print("\nPress ENTER to exit.", end = '')
input()
def menu():
order = 2 ** 8 # = p^m = 256 = byte size, we well encrypt each byte and save in base64 format
n = order - 1 # = 255, (order - 1) mod n = 0 for Reed Solomon code (below i will name it RScode), n mod 3 = 0 for base64
k = 210 #k mod 3 = 0 for base64, n >= k, RScode can correct (n - k) // 2 errors in message
#
#print(galois.GF(2 ** 8).properties) to know irreducible_poly and primitive_element (pyinstaller will not add database files)
#example of path to *.db files is C:\Python_3_12_2\Lib\site-packages\galois\_databases and _interface.py (which opens the database) is also here
#maybe it is possible to use --add-data pyinstaller option, but I didn't figure out which paths to write so that galois could find the database
#you can write a function that changes the configuration of the cryptosystem, it is easy to change n and k and rebuild RScode during execution
#and if you understand how to add database files using Pyinstaller, you can change the order during portable execution too
#you can even use order = 2**7 (or any from 128 to 255 which is p^m where p is prime, m is natural) for ascii coding or make your own alphabet table
#but if you want to change the order, you will have to give up the wonderful storage of keys and messages in base64 format...
#...and use the first version of this program for saving raw integers (or reduce the order and abandon universal work for any encoding)
#
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#_interface.py causes an error during portable execution if it cannot find the database, but there is a hint at the end of this file:
#Alternatively, you can find irreducible polynomials with galois.irreducible_poly(p, m) or primitive polynomials with galois.primitive_poly(p, m).
#maybe it the key to the solution of the database problem (as I understand, these functions don't use database), check it; and maybe verify = True
#!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#
#nothing from the big comment above is needed if you don't want to build portable exe, just GF = galois.GF(order)
#
GF = galois.GF(2, 8, irreducible_poly = "x^8 + x^4 + x^3 + x^2 + 1", primitive_element = "x", verify = False)
rs = galois.ReedSolomon(n, k, field = GF)
print("\nMcEliece cryptosystem implementation by vovuas2003.\n")
print("All necessary txt files must be 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; h = help.\n"
err = "Error! Check command info and try again!\n"
ok = "Operation successful.\n"
inp = [str(i) for i in range(7)] + ['h'] + ['1337']
print(info)
while True:
s = input("Menu number: ")
while s not in inp:
s = input("Wrong menu number, h = help: ")
if s == 'h':
print(info)
elif s == '0':
print("\nGood luck!")
break
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()):
continue
try:
generate(n, k, GF, rs)
print(ok)
except:
print(err)
elif s == '2':
print("Write your text into text.txt; pubkey.txt is required, message.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
encrypt(n, k, order, GF)
print(ok)
except:
print(err)
elif s == '3':
print("You need message.txt, privkey_s.txt and privkey_p.txt; text.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
decrypt(n, k, GF, rs)
print(ok)
except:
print(err)
elif s == '4':
print("You need privkey_s.txt and privkey_p.txt; pubkey.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
restore_G_(n, k, GF, rs)
print(ok)
except:
print(err)
elif s == '5':
print("You need pubkey.txt and privkey_p.txt; privkey_s.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
break_S(n, k, GF)
print(ok)
except:
print(err)
elif s == '6':
print("You need pubkey.txt and privkey_s.txt; privkey_p.txt will be rewritten.")
if(not get_yes_no()):
continue
try:
break_P(n, k, GF, rs)
print(ok)
except:
print(err)
elif s == '1337':
c = input("Move the soldering iron into the black hole number: ")
try:
PT(int(c))
except:
print("Iron: 'I don't know this hole.'")
continue
else:
print("Impossible behaviour, mistake in source code!\nThe string allowed in the inp array is not bound to the call of any function!")
break
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 PT(m):
M = 5
if m == 0:
print("Iron: 'OK, I will choose the number by myself.'")
while m == 0:
m = random.randint(-M, M)
s = "PT!"
p = " "
f = False
if m < 0:
s, p = p, s
m *= -1
f = True
if m > M:
print("Iron: 'Are you sure to move me so far?'")
if(not get_yes_no()):
return
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()
def generate(n, k, GF, rs):
S = generate_S(k, GF)
G = rs.G
P, p = generate_P(n, GF)
G_ = S @ G @ P
write_pubkey(G_)
write_privkey_s(S)
write_privkey_p(p)
def generate_S(k, GF):
S = GF.Random((k, k))
while np.linalg.det(S) == 0:
S = GF.Random((k, k))
return S
def generate_P(n, GF):
r = [i for i in range(n)]
p = []
for i in range(n):
p.append(r.pop(random.randint(0, n - 1 - i)))
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_]
output = "".join([base64.b64encode(row).decode() for row in rows])
with open("pubkey.txt", "w") as f:
f.write(output)
def write_privkey_s(S):
rows = [bytes(row) for row in S]
output = "".join([base64.b64encode(row).decode() for row in rows])
with open("privkey_s.txt", "w") as f:
f.write(output)
def write_privkey_p(p):
output = base64.b64encode(bytes(p)).decode()
with open("privkey_p.txt", "w") as f:
f.write(output)
def read_pubkey(n, k):
with open("pubkey.txt", "r") as f:
out = f.read()
out = [int(i) for i in base64.b64decode(out)]
out = [out[i - n : i] for i in range(n, n * k + n, n)]
return out
def read_privkey_s(k):
with open("privkey_s.txt", "r") as f:
out = f.read()
out = [int(i) for i in base64.b64decode(out)]
out = [out[i - k : i] for i in range(k, k * k + k, k)]
return out
def read_privkey_p():
with open("privkey_p.txt", "r") as f:
out = f.read()
return [int(i) for i in base64.b64decode(out)]
def build_P(n, GF, p):
P = GF.Zeros((n, n))
for i in range(n):
P[i, p[i]] = 1
return P
def build_P_inv(n, GF, p):
P = GF.Zeros((n, n))
for i in range(n):
P[p[i], i] = 1
return P
def pad_message(msg: bytes, pad_size: int) -> list[int]:
padding = pad_size - (len(msg) % pad_size)
return list(msg + padding.to_bytes() * padding)
def unpad_message(msg):
padding_byte = msg[-1]
for i in range(1, padding_byte + 1):
if msg[-i] != padding_byte:
print("Wrong privkey!")
raise Exception()
return msg[: -padding_byte]
def encrypt(n, k, order, GF):
G_ = GF(read_pubkey(n, k))
with open("text.txt", "r") as f:
text = f.read()
text = text.encode()
out = ""
while len(text) > k - 1:
tmp = text[: k - 1]
text = text[k - 1 :]
out += encrypt_one(n, k, order, GF, G_, tmp)
out += encrypt_one(n, k, order, GF, G_, text)
with open("message.txt", "w") as f:
f.write(out)
def encrypt_one(n, k, order, GF, G_, text):
msg = pad_message(text, k)
m = GF(msg)
c = m.T @ G_
t = (n - k) // 2
z = np.zeros(n, dtype = int)
p = [i for i in range(n)]
for i in range(t):
ind = p.pop(random.randint(0, n - 1 - i))
z[ind] += random.randint(1, order - 1)
z[ind] %= order
c = c + GF(z)
return base64.b64encode(bytes(c)).decode()
def decrypt(n, k, GF, rs):
S_inv = np.linalg.inv(GF(read_privkey_s(k)))
P_inv = GF(build_P_inv(n, GF, read_privkey_p()))
with open("message.txt", "r") as f:
msg = f.read()
msg = [int(i) for i in base64.b64decode(msg)]
msg = [msg[i - n : i] for i in range(n, len(msg) + n, n)]
msg = [decrypt_one(rs, S_inv, P_inv, GF(i)) for i in msg]
msg = [i for j in msg for i in j]
msg = bytes(msg).decode()
with open("text.txt", "w") as f:
f.write(msg)
def decrypt_one(rs, S_inv, P_inv, msg):
msg = msg @ P_inv
msg, e = rs.decode(msg, errors = True)
if e == -1:
print("Too many erroneous values in message!")
raise Exception()
msg = msg @ S_inv
msg = [int(i) for i in msg]
msg = unpad_message(msg)
return msg
def restore_G_(n, k, GF, rs):
S = GF(read_privkey_s(k))
G = rs.G
P = GF(build_P(n, GF, read_privkey_p()))
G_ = S @ G @ P
write_pubkey(G_)
def break_S(n, k, GF):
G_ = GF(read_pubkey(n, k))
P_inv = GF(build_P_inv(n, GF, read_privkey_p()))
S = G_ @ P_inv
S = S[:, : k]
write_privkey_s(S)
def break_P(n, k, GF, rs):
G_ = GF(read_pubkey(n, k))
S_inv = np.linalg.inv(GF(read_privkey_s(k)))
G = rs.G
G = G.T
G = [[int(i) for i in j] for j in G]
GP = S_inv @ G_
GP = GP.T
GP = [[int(i) for i in j] for j in GP]
p = [0 for i in range(n)]
f = False
for i in range(n):
f = False
for j in range(n):
if G[i] == GP[j]:
p[i] = j
f = True
break
if f:
continue
print("Wrong pubkey and privkey_s combination!")
raise Exception()
write_privkey_p(p)
if __name__ == "__main__":
main()

@ -1,110 +0,0 @@
#G = pubkey; S and P = privkeys; text = plaintext; msg = encrypted text
#all these variables are strings
#Usage:
#G, S, P = generate()
#msg = encrypt(G, text)
#text = decrypt(S, P, msg)
#G = restore_G(S, P)
#S = break_S(G, P)
#P = break_P(G, S)
import portable_v3_core as core
import tkinter as tk
def button1_click():
new_text1, new_text2, new_text3 = core.generate()
entry1.delete(0, tk.END)
entry1.insert(0, new_text1)
entry2.delete(0, tk.END)
entry2.insert(0, new_text2)
entry3.delete(0, tk.END)
entry3.insert(0, new_text3)
def button2_click():
G = entry1.get()
text = entry4.get("1.0", "end-1c")
# Выполнить какие-либо действия с текстом
new_text4 = core.encrypt(G, text)
# Записать текст обратно в поля ввода
entry4.delete("1.0", tk.END)
entry4.insert("1.0", new_text4)
def button3_click():
S = entry2.get()
P = entry3.get()
msg = entry4.get("1.0", "end-1c")
# Выполнить какие-либо действия с текстом
new_text4 = core.decrypt(S, P, msg)
# Записать текст обратно в поля ввода
entry4.delete("1.0", tk.END)
entry4.insert("1.0", new_text4)
def button4_click():
S = entry2.get()
P = entry3.get()
# Выполнить какие-либо действия с текстом
new_text1 = core.restore_G(S, P)
# Записать текст обратно в поля ввода
entry1.delete(0, tk.END)
entry1.insert(0, new_text1)
def button5_click():
G = entry1.get()
P = entry3.get()
# Выполнить какие-либо действия с текстом
new_text2 = core.break_S(G, P)
# Записать текст обратно в поля ввода
entry2.delete(0, tk.END)
entry2.insert(0, new_text2)
def button6_click():
G = entry1.get()
S = entry2.get()
# Выполнить какие-либо действия с текстом
new_text3 = core.break_P(G, S)
# Записать текст обратно в поля ввода
entry3.delete(0, tk.END)
entry3.insert(0, new_text3)
# Создать главное окно
window = tk.Tk()
window.title("McEliece by vovuas2003")
# Создать поля для ввода текста
entry1 = tk.Entry(window, width=50)
entry2 = tk.Entry(window, width=50)
entry3 = tk.Entry(window, width=50)
entry4 = tk.Text(window, height=10, width=50)
# Создать кнопки
button1 = tk.Button(window, text="generate", command=button1_click)
button2 = tk.Button(window, text="encrypt", command=button2_click)
button3 = tk.Button(window, text="decrypt", command=button3_click)
button4 = tk.Button(window, text="pubkey", command=button4_click)
button5 = tk.Button(window, text="privkey_s", command=button5_click)
button6 = tk.Button(window, text="privkey_p", command=button6_click)
# Разместить элементы в окне
entry1.pack()
entry2.pack()
entry3.pack()
entry4.pack(fill=tk.BOTH, expand=True)
button1.pack()
button2.pack()
button3.pack()
button4.pack()
button5.pack()
button6.pack()
# Запустить главное окно
window.mainloop()

@ -1,21 +1,13 @@
McEliece cryptosystem implementation by vovuas2003 McEliece cryptosystem implementation by vovuas2003
Update: portable version is available! All functions in one file. New features and some improvements! Required Python libraries: numpy, galois.
Update 2: portable_v2 is available! Nice keys and message saving in base64 string format, so it is easy to integrate with other projects by a small change of read and write functions.
Instruction for old version below All cryptosystem functions are implemented in cryptosystem_core.py, just import it into your project and enjoy!
Usage: For example, I coded a console menu (that works with txt files) and a GUI app.
0. pip install numpy and galois
1. generate.py - generate and save public and private keys
2. send pubkey.py and encode.py to your friend
3. your friend runs encode.py, write secret string and send message.py to you
4. decode.py - get secret string
Hacker can get your private key if he will know a half of it (and pubkey.py, decode.py and Reed-Solomon algo). It is possible to build portable exe with pyinstaller and run code on a computer that does not have Python installed.
Check break.py to understand how hacker can do this. But it is NOT compilation, so exe file will be quite large.
todo: The pdf presentation in Russian contains a bit of theory about the Mceliece cryptosystem.
0. DONE!(in portable versions) build portable exe with pyinstaller icon.ico is an optional file for pyinstaller
1. left part of G is E, because we use Reed-Solomon algo; so left part of S @ G is S and cutting right colomns works; my_fix(G) returns E and in break_S we needn't get inv(G), just S = my_fix(G_ @ inv(P)); try break_S with another (not Reed-Solomon) code (matrix G will be different; will my_fix(G) and my_fix(G_) return nonsingular matrices?; of course, rank(G) = rank(G_) = k and we can iterate through all possible combinations of column deletions and find one that does not lead to nonsingular matrices); another way to get S is calculating it row by row (solving k systems, each has n equations with k variables, k < n, but we need to do it in Galois Field) 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
2. DONE! check randomization during encode (add vector z, check https://en.wikipedia.org/wiki/McEliece_cryptosystem)
3. DONE!(Russian language) make presentation that explains McEliece cryptosystem
Loading…
Cancel
Save