commit adfe36062124f747d7bf7e85e6332fe2291be4b1 Author: vovuas2003 <89464038+vovuas2003@users.noreply.github.com> Date: Mon Apr 1 17:03:02 2024 +0300 Add files via upload diff --git a/break.py b/break.py new file mode 100644 index 0000000..48ad36f --- /dev/null +++ b/break.py @@ -0,0 +1,60 @@ +#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_): + #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) + S = G_ @ np.linalg.inv(G) + return S + +if __name__ == "__main__": + main() diff --git a/decode.py b/decode.py new file mode 100644 index 0000000..814b3ec --- /dev/null +++ b/decode.py @@ -0,0 +1,37 @@ +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() diff --git a/encode.py b/encode.py new file mode 100644 index 0000000..5af35dd --- /dev/null +++ b/encode.py @@ -0,0 +1,40 @@ +import numpy as np +import galois + +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_ + return c + +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() diff --git a/generate.py b/generate.py new file mode 100644 index 0000000..1732478 --- /dev/null +++ b/generate.py @@ -0,0 +1,58 @@ +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() diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..c36e812 --- /dev/null +++ b/readme.txt @@ -0,0 +1,13 @@ +McEliece cryptosystem implementation + +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 + +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. + +todo: check randomization during encode (add vector z, check https://en.wikipedia.org/wiki/McEliece_cryptosystem) \ No newline at end of file