Skip to content

Commit 22f71bd

Browse files
Add files via upload
1 parent a9de2d9 commit 22f71bd

File tree

6 files changed

+220
-8
lines changed

6 files changed

+220
-8
lines changed

classes/Options.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from colorama import Fore, init; init()
2-
from .Text import Text
2+
3+
from utils.colortext import colortext
34

45
class Options:
5-
def __init__(self, options:list, first_idx=0):
6+
def __init__(self, options:list, first_idx:int=0):
67
self.options = options
78
self.first_idx = first_idx
89

@@ -13,7 +14,7 @@ def get_choice(self) -> int:
1314

1415
if self.check_input(choice): return int(choice)
1516

16-
print(Text("\nInvalid choice.\n", Fore.RED))
17+
print(colortext("\nInvalid choice.\n", Fore.RED))
1718
return self.get_choice()
1819

1920

main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from classes.Menu import Menu
1+
from utils.menu import menu
22

3-
Menu()
3+
menu()

utils/colortext.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from colorama import Fore, init; init()
2+
3+
def colortext(text:str, color:str=Fore.WHITE) -> str:
4+
return f"{color}{text}{Fore.RESET}"

utils/cryptography.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import hashlib
2-
import sys; sys.path.append("..")
3-
from constants.constants import *
42
import random
53

4+
from constants.constants import *
5+
66
def enc(txt, encryption_code:str) -> str:
77
encTxt = ""
88
for x in txt:
@@ -11,7 +11,7 @@ def enc(txt, encryption_code:str) -> str:
1111

1212

1313
# Decrypt an encrypted text with an encryption code
14-
def dec(txt, encryption:str) -> str:
14+
def dec(txt:str, encryption:str) -> str:
1515
decTxt = ""
1616
for x in txt:
1717
decTxt += HEX_SYMB[encryption.index(x)] # The symbol in the equivalent position

utils/encryption.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import random
2+
from colorama import Fore, init; init()
3+
4+
from utils.colortext import colortext
5+
from utils.cryptography import *
6+
from constants.constants import *
7+
8+
def encrypt_txt(text:str, pwd:str) -> str:
9+
hash = get_pwd_hash(pwd)
10+
print(colortext("Preparing...", Fore.MAGENTA))
11+
if len(text)>TXT_MAX_LEN or len(text)==0:
12+
raise Exception(f"text length ({len(text)}) is invalid. It must be between 1 and {TXT_MAX_LEN} characters.")
13+
14+
# Seed the random generator with the hash
15+
seed = int(hash,16)
16+
random.seed(seed)
17+
18+
# Get the encryption code
19+
encryption_code = get_encryption_code(hash)
20+
21+
print(colortext("Creating the random base...", Fore.MAGENTA))
22+
# Fill the ciphertext with random characters
23+
ciphertext = [random.choice(HEX_SYMB) for _ in range(CIPHER_LEN)]
24+
25+
print(colortext("Encrypting the input and its length...", Fore.MAGENTA))
26+
# INFO: Encrypt the text and the text length
27+
textE = enc(t2h(text), encryption_code)
28+
textE_len = len(textE)
29+
textE_len_max_digits = len(format(CIPHER_LEN,"x")) # If the position length is 5, the length of the encrypted text will fit in 5 digits
30+
textE_len_fixed_len = format(textE_len,"x").rjust(textE_len_max_digits,"0")
31+
textE_lenE = enc(textE_len_fixed_len, encryption_code) # Encrypted text length (fixed length)
32+
33+
print(colortext("Generating the length indexes...", Fore.MAGENTA))
34+
# Get the indexes where the INFO will be stored
35+
textE_len_idxs = get_textE_len_idxs(seed, CIPHER_LEN, textE_len_max_digits)
36+
37+
print(colortext("Generating the encrypted text indexes...", Fore.MAGENTA))
38+
textE_idxs = get_textE_idxs(seed, CIPHER_LEN, len(textE), textE_len_idxs)
39+
40+
print(colortext("Creating the ciphertext...", Fore.MAGENTA))
41+
# Save the text and text length in toret
42+
for i,idx in enumerate(textE_len_idxs):
43+
ciphertext[idx] = textE_lenE[i]
44+
for i,idx in enumerate(textE_idxs):
45+
ciphertext[idx] = textE[i]
46+
47+
return "".join(ciphertext)
48+
49+
50+
def decrypt_cipher(ciphertext:str, pwd:str) -> str:
51+
hash = get_pwd_hash(pwd)
52+
print(colortext("Preparing...", Fore.MAGENTA))
53+
cipher_len = len(ciphertext) # Same as CIPHER_LEN
54+
55+
if not all(c in HEX_SYMB for c in ciphertext) or len(ciphertext) != cipher_len:
56+
raise Exception("Encrypted text is invalid")
57+
58+
# Seed the random generator with the hash
59+
seed = int(hash,16)
60+
random.seed(seed)
61+
62+
# Get the encryption code
63+
encryption_code = get_encryption_code(hash)
64+
65+
print(colortext("Decrypting the text length...", Fore.MAGENTA))
66+
# Decrypt the text length
67+
textE_len_digits = len(format(cipher_len,"x")) # If the position length is 5, the length of the encrypted text will fit in 5 digits
68+
textE_len_idxs = get_textE_len_idxs(seed, cipher_len, textE_len_digits)
69+
textE_lenE = "".join([ciphertext[idx] for idx in textE_len_idxs])
70+
textE_len = int(dec(textE_lenE, encryption_code), 16)
71+
72+
print(colortext("Decrypting the text...", Fore.MAGENTA))
73+
# Decrypt the text
74+
textE_idxs = get_textE_idxs(seed, cipher_len, textE_len, textE_len_idxs)
75+
textE = "".join([ciphertext[idx] for idx in textE_idxs])
76+
text = h2t(dec(textE, encryption_code))
77+
78+
return text
79+
80+
81+
def get_img_arr(text:str) -> np.ndarray:
82+
img_arr = np.array(
83+
[[int(text[i:i+2], 16),
84+
int(text[i+2:i+4], 16),
85+
int(text[i+4:i+6], 16)]
86+
for i in range(0, len(text), 6)],
87+
dtype=np.uint8)
88+
return img_arr.reshape(IMG_SIZE, IMG_SIZE, 3)

utils/menu.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import os
2+
from colorama import Fore, init; init()
3+
from PIL import Image
4+
import time
5+
6+
from classes.Options import Options
7+
from utils.colortext import colortext
8+
from utils.encryption import encrypt_txt, decrypt_cipher, get_img_arr
9+
from constants.constants import *
10+
11+
def menu():
12+
create_folders()
13+
print(f'\n{colortext("Max length of the text",Fore.CYAN)}: {colortext(f"{TXT_MAX_LEN:,}",Fore.GREEN)} (you can change it in {colortext("config.py",Fore.LIGHTYELLOW_EX)}')
14+
option = Options(["EXIT", "Encrypt", "Decrypt text", "Decrypt image"]).get_choice()
15+
16+
if option == 0:
17+
exit()
18+
19+
elif option == 1:
20+
encrypt()
21+
22+
elif option == 2:
23+
decrypt_colortext()
24+
25+
elif option == 3:
26+
decrypt_image()
27+
28+
29+
def create_folders():
30+
if not os.path.exists(INPUT_DIR): os.mkdir(INPUT_DIR)
31+
if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR)
32+
33+
34+
def encrypt():
35+
text = open(get_input_file("text filename: ", "txt")).read()
36+
pwd = input("Password: ")
37+
enc_text_file = get_file("New text filename: ", "txt")
38+
enc_img_file = get_file("New image filename: ", "png") if yes_no("Save image? [y/n]: ") else None
39+
40+
t1 = time.time()
41+
try:
42+
encrypted_text = encrypt_txt(text, pwd)
43+
save_colortext(encrypted_text, enc_text_file)
44+
print(colortext("\ntext encrypted succesfully\n", Fore.GREEN))
45+
if enc_img_file:
46+
save_img(encrypted_text, enc_img_file)
47+
print(colortext("Image saved", Fore.GREEN))
48+
except Exception as e:
49+
print(colortext(f"Error: {e}", Fore.RED))
50+
return
51+
52+
print(colortext(f"\nDone in {time.time() - t1:.2f} seconds", Fore.LIGHTYELLOW_EX))
53+
54+
55+
def decrypt(ciphertext:str) -> str:
56+
pwd = input("Password: ")
57+
dec_text_file = get_file("New filename: ", "txt")
58+
59+
t1 = time.time()
60+
try:
61+
decypted_text = decrypt_cipher(ciphertext, pwd)
62+
save_colortext(decypted_text, dec_text_file)
63+
print(colortext("\ntext decrypted succesfully\n", Fore.GREEN))
64+
except Exception as e:
65+
print(colortext(f"Error: {e}", Fore.RED))
66+
return
67+
68+
print(colortext(f"\nDone in {time.time() - t1:.2f} seconds", Fore.LIGHTYELLOW_EX))
69+
70+
71+
def decrypt_colortext():
72+
ciphertext = open(get_input_file("Ciphertext filename: ", "txt")).read()
73+
decrypt(ciphertext)
74+
75+
76+
def decrypt_image():
77+
ciphertext = img_to_colortext(get_input_file("Cipher image filename: ", "png"))
78+
decrypt(ciphertext)
79+
80+
81+
def img_to_colortext(img_file:str) -> str:
82+
img_arr = np.array(Image.open(img_file)).flatten()
83+
img_str = "".join([f'{n:02x}' for n in img_arr])
84+
return img_str
85+
86+
87+
def yes_no(msg:str="Save? [y/n]: ") -> bool:
88+
inp = input(msg)
89+
if inp.lower() == "y": return True
90+
elif inp.lower() == "n": return False
91+
else: return yes_no(msg)
92+
93+
94+
def save_colortext(text:str, file:str):
95+
with open(f"{OUTPUT_DIR}/{file}", "w") as f:
96+
f.write(text)
97+
98+
99+
def save_img(text:str, file:str):
100+
img_arr = get_img_arr(text)
101+
Image.fromarray(img_arr).save(f"{OUTPUT_DIR}/{file}")
102+
103+
104+
def get_file(msg:str, ext:str) -> str:
105+
while True:
106+
filename = input(msg)
107+
if "." in filename:
108+
if filename.endswith(f".{ext}"): return filename
109+
print(colortext("Invalid extension", Fore.RED))
110+
continue
111+
return f"{filename}.{ext}"
112+
113+
114+
def get_input_file(msg:str, ext:str) -> str:
115+
while True:
116+
file = get_file(msg, ext)
117+
path = f"{INPUT_DIR}/{file}"
118+
if os.path.exists(path): return path
119+
print(colortext("File not found",Fore.RED))

0 commit comments

Comments
 (0)