Version 2

main
Naumkin Vladimir 1 month ago
parent 339cc2bed6
commit 3bdab827ff

@ -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.'")

@ -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()

@ -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])

@ -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()

@ -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
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…
Cancel
Save