some improvements

main
Naumkin Vladimir 1 month ago
parent 3bdab827ff
commit f58c99f231

@ -2,6 +2,7 @@
import hashlib
import getpass
import pyperclip
def main():
safe_start()
@ -32,11 +33,11 @@ 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"
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 on screen, 33 = decrypt string to clipboard.\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']
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, 34)] + ['1337']
print("\nMcEliece cryptosystem implementation by vovuas2003. Version 2.\n")
print(global_info)
print(info)
@ -225,7 +226,7 @@ def menu():
except:
print(err)
elif s == '32':
print("Need ciphered_string.bin!")
print("Need ciphered_string.bin! Visible input!!!")
if(not get_yes_no()):
continue
try:
@ -233,6 +234,20 @@ def menu():
print(ok)
except:
print(err)
elif s == '33':
print("Need ciphered_string.bin! Copy to clipboard.")
if(not get_yes_no()):
continue
try:
tmp = bytes(core.decrypt([int(i) for i in read_file("ciphered_string.bin")])).decode('utf-8')
except:
print(err)
continue
try:
pyperclip.copy(tmp)
print(ok)
except:
print("Decryption was successful, but program can't use clipboard.\nOn Linux (especially Ubuntu) try to install xclip (e.g. via apt-get).")
elif s == '1337':
PT()
else:

@ -65,7 +65,8 @@ 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._k = 210 #2 <= k <= n; randomly change t = (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._t = (self._n - self._k) // 2
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
@ -80,10 +81,11 @@ class McEliece_core:
raise Exception()
rs = galois.ReedSolomon(n, k, field = self._GF)
except:
raise Exception()
raise
else:
self._n = n
self._k = k
self._t = (n - k) // 2
self._rs = rs
#Also unset all keys!
self._G = self._GF.Zeros((self._k, self._n))
@ -101,8 +103,9 @@ class McEliece_core:
elif type(seed) != int:
raise Exception()
else:
self._unsafe_generate_S(seed % (2**32))
self._unsafe_generate_P(seed % (2**32))
seed %= 2**32
self._unsafe_generate_S(seed)
self._unsafe_generate_P(seed)
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]
@ -115,7 +118,7 @@ class McEliece_core:
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()
raise
else:
self._G = G
def set_privkey_S(self, S):
@ -124,7 +127,7 @@ class McEliece_core:
S = self._GF(S)
S_inv = np.linalg.inv(S)
except:
raise Exception()
raise
else:
self._S = S
self._S_inv = S_inv
@ -146,7 +149,7 @@ class McEliece_core:
try:
S_inv = np.linalg.inv(S)
except:
raise Exception()
raise
self._S = S
self._S_inv = S_inv
def restore_privkey_p(self):
@ -168,6 +171,8 @@ class McEliece_core:
if f:
continue
raise Exception()
if sorted(p) != [i for i in range(self._n)]:
raise Exception()
self._p = p
self._P = self._GF.Zeros((self._n, self._n))
self._P_inv = self._GF.Zeros((self._n, self._n))
@ -184,14 +189,14 @@ class McEliece_core:
out += self._encrypt_one(text)
return out
except:
raise Exception()
raise
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()
raise
#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))
@ -231,10 +236,9 @@ class McEliece_core:
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):
for i in range(self._t):
ind = p.pop(random.randint(0, self._n - 1 - i))
z[ind] = random.randint(1, self._order - 1)
c = c + self._GF(z)
@ -249,7 +253,7 @@ class McEliece_core:
try:
msg = self._unpad_message(msg)
except:
raise Exception()
raise
return msg
def _pad_message(self, msg):
last_value = self._k - (len(msg) % self._k)

@ -0,0 +1,92 @@
import numpy as np
import galois
import random
import getpass
import hashlib
import pyperclip
def main():
try:
menu()
except:
print("Error!")
def menu():
core = McEliece_core()
core.generate_keys(normalhash(getpass.getpass("Password: ")))
pyperclip.copy(bytes(core.decrypt([int(i) for i in read_file("only_password")])).decode('utf-8'))
print("OK!")
class McEliece_core:
def __init__(self):
self._order = 256
self._n = 255
self._k = 210
self._GF = galois.GF(2, 8, irreducible_poly = "x^8 + x^4 + x^3 + x^2 + 1", primitive_element = "x", verify = False)
self._rs = galois.ReedSolomon(self._n, self._k, field = self._GF)
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 generate_keys(self, seed):
seed %= 2**32
self._unsafe_generate_S(seed)
self._unsafe_generate_P(seed)
self._G = self._S @ self._rs.G @ self._P
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
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 _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
return msg
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]
def read_file(name):
with open(name, "rb") as f:
data = f.read()
return data
def normalhash(s):
return int(hashlib.sha256(bytearray(s, 'utf-8')).hexdigest(), 16)
if __name__ == "__main__":
main()

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,249 @@
import numpy as np
import galois
import random
import getpass
import hashlib
import pyperclip
def main():
try:
menu()
except:
print("\nUnknown error (maybe ctrl+c), emergency exit!")
def menu():
info = "Menu numbers: h = info, t = test clipboard, c = config;\n0 = exit, 1 = generate, 2 = encrypt, 3 = decrypt\n"
err = "Error, try again!\n"
ok = "Operation successful.\n"
inp = ['h', 'c', 't'] + [str(i) for i in range (4)] + ['1337']
core = McEliece_core()
print("\nMcEliece password encryption by vovuas2003.\n")
print(info)
while True:
s = input("Menu number: ")
while s not in inp:
s = input("Wrong menu number; h = help: ")
if s == '0':
print("\nGood luck!")
break
elif s == 'h':
print(info)
elif s == 't':
print("Do you want to check Python ability to use clipboard in your OS?")
if(not get_yes_no()):
continue
try:
print('Trying to write "Hello, world!" to clipboard.')
mycopy("Hello, world!")
print("Success! You can check ctrl+v.")
except:
print("Python cannot use clipboard. On Linux maybe you need to run command: sudo apt-get install xclip.\nOr try code below to check python error log:\nimport pyperclip #don't forget to pip install pyperclip\npyperclip.copy(\"Hello, world!\")")
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(normalhash(getpass.getpass("Password for hash: ")))
print(ok)
except:
print(err)
elif s == '2':
print("Encrypt hided input (ONLY ONE LINE!!!) from clipboard!")
if(not get_yes_no()):
continue
try:
inp_str = getpass.getpass("Hided string to encrypt: ")
out_name = input("Filename to save: ")
if not out_name:
print("Using default name ciphered_string.bin")
out_name = "ciphered_string.bin"
write_file(out_name, bytes(core.encrypt([int(i) for i in inp_str.encode('utf-8')])))
print(ok)
except:
print(err)
elif s == '3':
print("Decrypt string from file and copy to clipboard!")
if(not get_yes_no()):
continue
try:
inp_name = input("Filename to decrypt: ")
if not inp_name:
print("Using default name ciphered_string.bin")
inp_name = "ciphered_string.bin"
mycopy(bytes(core.decrypt([int(i) for i in read_file(inp_name)])).decode('utf-8'))
print(ok)
except:
print(err)
elif s == '1337':
mycopy(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
class McEliece_core:
def __init__(self):
self._order = 256
self._n = 255
self._k = 210
self._t = (self._n - self._k) // 2
self._GF = galois.GF(2, 8, irreducible_poly = "x^8 + x^4 + x^3 + x^2 + 1", primitive_element = "x", verify = False)
self._rs = galois.ReedSolomon(self._n, self._k, field = self._GF)
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 change_config(self, n, k):
try:
if k < 2:
raise Exception()
rs = galois.ReedSolomon(n, k, field = self._GF)
except:
raise
else:
self._n = n
self._k = k
self._t = (n - k) // 2
self._rs = rs
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):
seed %= 2**32
self._unsafe_generate_S(seed)
self._unsafe_generate_P(seed)
self._G = self._S @ self._rs.G @ self._P
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
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
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
z = np.zeros(self._n, dtype = int)
p = [i for i in range(self._n)]
for i in range(self._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
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]
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 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:
return "PT!"
s = "PT!"
p = " "
f = False
if m < 0:
s, p = p, s
m *= -1
f = True
out = "\n"
if f:
out += p * (10 * m + 1) + "\n"
out += p + (s * 3 + p + s * 3 + p + s + p) * m + "\n"
out += p + (s + p + s + p * 2 + s + p * 2 + s + p) * m + "\n"
out += p + (s * 3 + p * 2 + s + p * 2 + s + p) * m + "\n"
out += p + (s + p * 4 + s + p * 4) * m + "\n"
out += p + (s + p * 4 + s + p * 2 + s + p) * m + "\n"
if f:
out += p * (10 * m + 1) + "\n"
out += "\n"
return out
def mycopy(s):
pyperclip.copy(s)
if __name__ == "__main__":
main()

@ -1,15 +1,5 @@
McEliece cryptosystem implementation by vovuas2003
Required Python libraries: numpy, galois.
Required additional Python libraries: numpy, galois (numpy extension).
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).
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.
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
Version 2 is the latest. Check cryptosystem_core_v2 and McEliece_console_v2.

Loading…
Cancel
Save