-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1878538
commit 89d5ea4
Showing
15 changed files
with
436 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import random | ||
|
||
import sys; sys.path.append("..") | ||
from constants.constants import * | ||
from utils.cryptography import * | ||
|
||
class Decryptor: | ||
def __init__(self, ciphertext:str, pwd:str): | ||
self.ciphertext = ciphertext | ||
self.pwd = pwd | ||
self.hash = get_pwd_hash(pwd) | ||
|
||
|
||
def decrypt(self) -> str: | ||
cipher_len = len(self.ciphertext) # Same as CIPHER_LEN | ||
|
||
if not all(c in HEX_SYMB for c in self.ciphertext) or len(self.ciphertext) != cipher_len: | ||
raise Exception("Encrypted text is invalid") | ||
|
||
# Seed the random generator with the hash | ||
seed = int(self.hash,16) | ||
random.seed(seed) | ||
|
||
# Get the encryption code | ||
encryption_code = get_encryption_code(self.hash) | ||
|
||
# Decrypt the text length | ||
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 | ||
textE_len_idxs = get_textE_len_idxs(seed, cipher_len, textE_len_digits) | ||
textE_lenE = "".join([self.ciphertext[idx] for idx in textE_len_idxs]) | ||
textE_len = int(dec(textE_lenE, encryption_code), 16) | ||
|
||
# Decrypt the text | ||
textE_idxs = get_textE_idxs(seed, cipher_len, textE_len, textE_len_idxs) | ||
textE = "".join([self.ciphertext[idx] for idx in textE_idxs]) | ||
text = h2t(dec(textE, encryption_code)) | ||
|
||
return text |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import random | ||
|
||
import sys; sys.path.append("..") | ||
from constants.constants import * | ||
from utils.cryptography import * | ||
|
||
class Encryptor: | ||
def __init__(self, text:str, pwd:str): | ||
self.text = text | ||
self.pwd = pwd | ||
self.hash = get_pwd_hash(self.pwd) | ||
|
||
|
||
def encrypt(self) -> str: # Returns a ciphertext | ||
if len(self.text)>TXT_MAX_LEN or len(self.text)==0: | ||
raise Exception("Text length is invalid") | ||
|
||
# Seed the random generator with the hash | ||
seed = int(self.hash,16) | ||
random.seed(seed) | ||
|
||
# Get the encryption code | ||
encryption_code = get_encryption_code(self.hash) | ||
|
||
# Fill the ciphertext with random characters | ||
ciphertext = [random.choice(HEX_SYMB) for _ in range(CIPHER_LEN)] | ||
|
||
# INFO: Encrypt the text | ||
textE = enc(t2h(self.text), encryption_code) | ||
|
||
# INFO: Encrypt the text length | ||
textE_len = len(textE) | ||
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 | ||
textE_len_fixed_len = format(textE_len,"x").rjust(textE_len_max_digits,"0") | ||
textE_lenE = enc(textE_len_fixed_len, encryption_code) # Encrypted text length (fixed length) | ||
|
||
# Get the indexes where the INFO will be stored | ||
textE_len_idxs = get_textE_len_idxs(seed, CIPHER_LEN, textE_len_max_digits) | ||
textE_idxs = get_textE_idxs(seed, CIPHER_LEN, len(textE), textE_len_idxs) | ||
|
||
# Save the text and text length in toret | ||
for i,idx in enumerate(textE_len_idxs): | ||
ciphertext[idx] = textE_lenE[i] | ||
for i,idx in enumerate(textE_idxs): | ||
ciphertext[idx] = textE[i] | ||
|
||
return "".join(ciphertext) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import numpy as np | ||
|
||
import sys; sys.path.append("..") | ||
from constants.constants import * | ||
|
||
class ImageCreator: | ||
def __init__(self, text:str): | ||
self.text = text | ||
|
||
|
||
def get_img_arr(self) -> np.ndarray: | ||
img_arr = np.array( | ||
[[int(self.text[i:i+2], 16), | ||
int(self.text[i+2:i+4], 16), | ||
int(self.text[i+4:i+6], 16)] | ||
for i in range(0, len(self.text), 6)], | ||
dtype=np.uint8) | ||
return img_arr.reshape(IMG_SIZE, IMG_SIZE, 3) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import os | ||
from colorama import Fore, init; init() | ||
from PIL import Image | ||
|
||
from .Text import Text | ||
from .Options import Options | ||
from .Encryptor import Encryptor | ||
from .Decryptor import Decryptor | ||
from .ImageCreator import ImageCreator | ||
|
||
import sys; sys.path.append("..") | ||
from constants.constants import * | ||
|
||
class Menu(): | ||
def __init__(self): | ||
self.create_folders() | ||
print(f'\n{Text("Max length of the text",Fore.CYAN)}: {Text(f"{TXT_MAX_LEN:,}",Fore.GREEN)} (you can change it in {Text("config.py",Fore.LIGHTYELLOW_EX)}') | ||
option = Options(["EXIT", "Encrypt", "Decrypt text", "Decrypt image"]).get_choice() | ||
|
||
if option == 0: | ||
exit() | ||
|
||
elif option == 1: | ||
self.encrypt() | ||
|
||
elif option == 2: | ||
self.decrypt_text() | ||
|
||
elif option == 3: | ||
self.decrypt_image() | ||
|
||
|
||
def create_folders(self): | ||
if not os.path.exists(INPUT_DIR): os.mkdir(INPUT_DIR) | ||
if not os.path.exists(OUTPUT_DIR): os.mkdir(OUTPUT_DIR) | ||
|
||
|
||
def encrypt(self): | ||
text = open(self.get_input_file("Text filename: ", "txt")).read() | ||
pwd = input("Password: ") | ||
enc_text_file = self.get_file("New text filename: ", "txt") | ||
enc_img_file = self.get_file("New image filename: ", "png") if self.yes_no("Save image? [y/n]: ") else None | ||
|
||
try: | ||
encrypted_text = Encryptor(text, pwd).encrypt() | ||
self.save_text(encrypted_text, enc_text_file) | ||
print(Text("\nText encrypted succesfully\n", Fore.GREEN)) | ||
if enc_img_file: | ||
self.save_img(encrypted_text, enc_img_file) | ||
print(Text("Image saved", Fore.MAGENTA)) | ||
except Exception as e: | ||
print(Text(f"Error: {e}", Fore.RED)) | ||
return | ||
|
||
|
||
def decrypt(self, ciphertext:str) -> str: | ||
pwd = input("Password: ") | ||
dec_text_file = self.get_file("New filename: ", "txt") | ||
|
||
try: | ||
decypted_text = Decryptor(ciphertext, pwd).decrypt() | ||
self.save_text(decypted_text, dec_text_file) | ||
print(Text("\nText decrypted succesfully\n", Fore.GREEN)) | ||
except Exception as e: | ||
print(Text(f"Error: {e}", Fore.RED)) | ||
return | ||
|
||
|
||
def decrypt_text(self): | ||
ciphertext = open(self.get_input_file("Ciphertext filename: ", "txt")).read() | ||
self.decrypt(ciphertext) | ||
|
||
|
||
def decrypt_image(self): | ||
ciphertext = self.img_to_text(self.get_input_file("Cipher image filename: ", "png")) | ||
self.decrypt(ciphertext) | ||
|
||
|
||
def img_to_text(self, img_file:str) -> str: | ||
img_arr = np.array(Image.open(img_file)).flatten() | ||
img_str = "".join([f'{n:02x}' for n in img_arr]) | ||
return img_str | ||
|
||
|
||
def yes_no(self, msg:str="Save? [y/n]: ") -> bool: | ||
inp = input(msg) | ||
if inp.lower() == "y": return True | ||
elif inp.lower() == "n": return False | ||
else: return self.yes_no(msg) | ||
|
||
|
||
def save_text(self, text:str, file:str): | ||
with open(f"{OUTPUT_DIR}/{file}", "w") as f: | ||
f.write(text) | ||
|
||
|
||
def save_img(self, text:str, file:str): | ||
img_arr = ImageCreator(text).get_img_arr() | ||
Image.fromarray(img_arr).save(f"{OUTPUT_DIR}/{file}") | ||
|
||
|
||
def get_file(self, msg:str, ext:str) -> str: | ||
while True: | ||
filename = input(msg) | ||
if "." in filename: | ||
if filename.endswith(f".{ext}"): return filename | ||
print(Text("Invalid extension", Fore.RED)) | ||
continue | ||
return f"{filename}.{ext}" | ||
|
||
|
||
def get_input_file(self, msg:str, ext:str) -> str: | ||
while True: | ||
file = self.get_file(msg, ext) | ||
path = f"{INPUT_DIR}/{file}" | ||
if os.path.exists(path): return path | ||
print(Text("File not found",Fore.RED)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from colorama import Fore, init; init() | ||
from .Text import Text | ||
|
||
class Options: | ||
def __init__(self, options:list, first_idx=0): | ||
self.options = options | ||
self.first_idx = first_idx | ||
|
||
|
||
def get_choice(self) -> int: | ||
self.__str__() | ||
choice = input("Option: ") | ||
|
||
if self.check_input(choice): return int(choice) | ||
|
||
print(Text("\nInvalid choice.\n", Fore.RED)) | ||
return self.get_choice() | ||
|
||
|
||
def check_input(self, choice:str) -> bool: | ||
return choice.strip().isdigit() and \ | ||
int(choice) in range(self.first_idx, self.first_idx + len(self.options)) | ||
|
||
|
||
def __str__(self) -> str: | ||
print("".join( | ||
[f"[{self.first_idx+i}] {opt}\n" | ||
for i,opt in enumerate(self.options)])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from colorama import Fore, init; init() | ||
|
||
class Text: | ||
def __init__(self, text:str, color:str): | ||
self.text = text | ||
self.color = color | ||
|
||
def __str__(self) -> str: | ||
return f"{self.color}{self.text}{Fore.RESET}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
POS_LEN = 4 | ||
POS_LEN = 2 | ||
""" | ||
The length that the positions in the encryption will have (in hex) | ||
|
Oops, something went wrong.