global refactoring
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,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,21 +1,13 @@
|
||||
McEliece cryptosystem implementation by vovuas2003
|
||||
|
||||
Update: portable version is available! All functions in one file. New features and some improvements!
|
||||
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.
|
||||
Required Python libraries: numpy, galois.
|
||||
|
||||
Instruction for old version below
|
||||
Usage:
|
||||
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
|
||||
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 txt files) and a GUI app.
|
||||
|
||||
Hacker can get your private key if he will know a half of it (and pubkey.py, decode.py and Reed-Solomon algo).
|
||||
Check break.py to understand how hacker can do this.
|
||||
It is possible to build portable exe with pyinstaller and run code on a computer that does not have Python installed.
|
||||
But it is NOT compilation, so exe file will be quite large.
|
||||
|
||||
todo:
|
||||
0. DONE!(in portable versions) build portable exe with 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)
|
||||
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
|
||||
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
|
Loading…
Reference in New Issue