Skip to content

Commit dab4d55

Browse files
Add files via upload
1 parent 4103e9f commit dab4d55

17 files changed

+94
-175
lines changed

README.md

Lines changed: 2 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,150 +1,9 @@
11
# Encroval
22

3-
## Index
4-
* [Description](#description)
5-
* [Get Started](#get-started)
6-
* [Install requirements](#install-requirements)
7-
* [Enter the input](#enter-the-input)
8-
* [Run](#run)
9-
* [Configuration](#configuration)
10-
* [Examples](#examples)
11-
* [Encryption](#encryption-example)
12-
* [Text Decryption](#text-decryption-example)
13-
* [Image Decryption](#image-decryption-example)
14-
153
## Description
164

175
Encroval is an encryption tool that allows you to encrypt a text into other text or image using a password.
186

19-
It can encrypt a text of any length and the password can be up to 2<sup>128</sup> bits long and accepts any UTF-8 character (1,112,064 different characters).
20-
21-
22-
23-
24-
## Get Started
25-
26-
#### Install Requirements
27-
28-
```bash
29-
pip3 install -r requirements.txt
30-
```
31-
32-
33-
#### Enter the input
34-
35-
##### Select the text to encrypt
36-
Place the text you want to encrypt (a `.txt` file) in the `input` folder.
37-
38-
##### Select the text/image to decrypt
39-
Place the text you want to encrypt (a `.txt` file) or the image (a `.png` file) in the `input` folder.
40-
41-
42-
#### Run
43-
44-
```bash
45-
python3 main.py
46-
```
47-
48-
49-
50-
51-
## Configuration
52-
53-
You can change the `POS_LEN` in the `config.py` file, which changes the input text's maximum length.
54-
55-
Default is `POS_LEN = 4`, so the maximum length the text can have by default is **32,446**
56-
57-
The more the `POS_LEN` is, the longer the text can be, but the longer it will take to encrypt and decrypt it.
58-
59-
| POS_LEN | Max text lenght |
60-
|-|-|
61-
| 1 | 2 |
62-
| 2 | 107 |
63-
| 3 | 2,026 |
64-
| 4 | 32,446 |
65-
| 5 | 524,169 |
66-
| 6 | 8,386,749 |
67-
| 7 | 134,188,028 |
68-
| 8 | 2,147,329,544 |
69-
| ... | ... |
70-
71-
72-
73-
74-
## Examples
75-
76-
### Encryption example
77-
78-
<img width=400 src=readme-assets/example_encryption.png>
79-
80-
##### `config.py`
81-
82-
```python
83-
POS_LEN = 2
84-
```
85-
86-
##### Input text (`input/test.txt`)
87-
```txt
88-
This is a test 123!!!
89-
90-
It can have every UTF-8 character! ✔️ ❤️ ☆
91-
```
92-
93-
##### Password
94-
```txt
95-
test#@–{}password123¿?
96-
```
97-
98-
##### Encrypted text (`output/encrypted_text.txt`)
99-
```txt
100-
3e8b00b4bcbb4ff246fdb3bc9afd63cf080e1cbefdc4b4bb2b5f1400f3fd4e6cb10d40825d0ab41e080e4e751a1ebbb8e7c4448fc14434d5c84d7fb3cc68e2c66033d5cfeece84bd256888b5e3dbb5bdc7fd47845be373e44bc8defbabb92e544f5eb0b4c43403084344d663
101-
```
102-
103-
##### Encrypted image (`output/encrypted_image.png`)
104-
<img width=70 src=readme-assets/encrypted_image.png>
105-
106-
107-
108-
### Text Decryption example
109-
110-
<img width=400 src=readme-assets/example_text_decryption.png>
111-
112-
##### Input text (`input/encrypted_text.txt`)
113-
```txt
114-
This is a test 123!!!
115-
116-
It can have every UTF-8 character! ✔️ ❤️ ☆
117-
```
118-
119-
##### Password
120-
```txt
121-
test#@–{}password123¿?
122-
```
123-
124-
##### Decrypted text (`output/decrypted_text.txt`)
125-
```txt
126-
This is a test 123!!!
127-
128-
The text can have every UTF-8 character! ✔️ ❤️ ☆
129-
```
130-
131-
132-
133-
### Image Decryption example
134-
135-
<img width=400 src=readme-assets/example_image_decryption.png>
136-
137-
##### Input image (`input/encrypted_image.png`)
138-
<img width=70 src=readme-assets/encrypted_image.png>
139-
140-
##### Password
141-
```txt
142-
test#@–{}password123¿?
143-
```
144-
145-
##### Decrypted image (`output/decrypted_image.txt`)
146-
```txt
147-
This is a test 123!!!
7+
It can encrypt a text of any length with a password and the only way to decrypt it is by knowing it.
1488

149-
The text can have every UTF-8 character! ✔️ ❤️ ☆
150-
```
9+
The password can be up to 2<sup>128</sup> bits long and accepts any UTF-8 character (1,112,064 different characters).

classes/Decryptor.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ def __init__(self, ciphertext:str, pwd:str):
1212

1313

1414
def decrypt(self) -> str:
15-
cipher_len = len(self.ciphertext) # Same as CIPHER_LEN
16-
17-
if not all(c in HEX_SYMB for c in self.ciphertext) or len(self.ciphertext) != cipher_len:
15+
encrypted_len = len(self.ciphertext) # Same as ENC_LEN
16+
if not all(c in HEX_SYMB for c in self.ciphertext) or len(self.ciphertext) != encrypted_len:
1817
raise Exception("Encrypted text is invalid")
1918

2019
# Seed the random generator with the hash
@@ -25,13 +24,13 @@ def decrypt(self) -> str:
2524
encryption_code = get_encryption_code(self.hash)
2625

2726
# Decrypt the text length
28-
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
29-
textE_len_idxs = get_textE_len_idxs(seed, cipher_len, textE_len_digits)
27+
textE_len_digits = len(format(encrypted_len,"x")) # If the position length is 5, the length of the encrypted text will fit in 5 digits
28+
textE_len_idxs = get_textE_len_idxs(seed, encrypted_len, textE_len_digits)
3029
textE_lenE = "".join([self.ciphertext[idx] for idx in textE_len_idxs])
3130
textE_len = int(dec(textE_lenE, encryption_code), 16)
3231

3332
# Decrypt the text
34-
textE_idxs = get_textE_idxs(seed, cipher_len, textE_len, textE_len_idxs)
33+
textE_idxs = get_textE_idxs(seed, encrypted_len, textE_len, textE_len_idxs)
3534
textE = "".join([self.ciphertext[idx] for idx in textE_idxs])
3635
text = h2t(dec(textE, encryption_code))
3736

classes/Encryptor.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def __init__(self, text:str, pwd:str):
1111
self.hash = get_pwd_hash(self.pwd)
1212

1313

14-
def encrypt(self) -> str: # Returns a ciphertext
14+
def encrypt(self) -> str:
1515
if len(self.text)>TXT_MAX_LEN or len(self.text)==0:
1616
raise Exception("Text length is invalid")
1717

@@ -22,21 +22,19 @@ def encrypt(self) -> str: # Returns a ciphertext
2222
# Get the encryption code
2323
encryption_code = get_encryption_code(self.hash)
2424

25-
# Fill the ciphertext with random characters
26-
ciphertext = [random.choice(HEX_SYMB) for _ in range(CIPHER_LEN)]
25+
# Fill the encrypted text with random characters
26+
ciphertext = [random.choice(HEX_SYMB) for _ in range(ENC_LEN)]
2727

28-
# INFO: Encrypt the text
28+
# INFO: Encrypt the text and text length
2929
textE = enc(t2h(self.text), encryption_code)
30-
31-
# INFO: Encrypt the text length
30+
textE_len_digits = len(format(ENC_LEN,"x")) # If the position length is 5, the length of the encrypted text will fit in 5 digits
3231
textE_len = len(textE)
33-
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
34-
textE_len_fixed_len = format(textE_len,"x").rjust(textE_len_max_digits,"0")
32+
textE_len_fixed_len = format(textE_len,"x").rjust(textE_len_digits,"0")
3533
textE_lenE = enc(textE_len_fixed_len, encryption_code) # Encrypted text length (fixed length)
3634

3735
# Get the indexes where the INFO will be stored
38-
textE_len_idxs = get_textE_len_idxs(seed, CIPHER_LEN, textE_len_max_digits)
39-
textE_idxs = get_textE_idxs(seed, CIPHER_LEN, len(textE), textE_len_idxs)
36+
textE_len_idxs = get_textE_len_idxs(seed, ENC_LEN, textE_len_digits)
37+
textE_idxs = get_textE_idxs(seed, ENC_LEN, len(textE), textE_len_idxs)
4038

4139
# Save the text and text length in toret
4240
for i,idx in enumerate(textE_len_idxs):

classes/Menu.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
class Menu():
1515
def __init__(self):
1616
self.create_folders()
17-
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)}')
1817
option = Options(["EXIT", "Encrypt", "Decrypt text", "Decrypt image"]).get_choice()
1918

2019
if option == 0:
1.98 KB
Binary file not shown.
1.7 KB
Binary file not shown.
1.6 KB
Binary file not shown.
1.15 KB
Binary file not shown.
4.35 KB
Binary file not shown.
1.48 KB
Binary file not shown.
735 Bytes
Binary file not shown.

config.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
POS_LEN = 2
1+
POS_LEN = 4
22
"""
33
The length that the positions in the encryption will have (in hex)
4-
5-
1 = 0x0 - 0xF Max text length: 2
6-
2 = 0x00 - 0xFF Max text length: 107
7-
3 = 0x000 - 0xFFF Max text length: 2,026
8-
4 = 0x0000 - 0xFFFF Max text length: 32,446
9-
5 = 0x00000 - 0xFFFFF Max text length: 524,169
10-
6 = 0x000000 - 0xFFFFFF Max text length: 8,386,749
11-
7 = 0x0000000 - 0xFFFFFFF Max text length: 134,188,028
12-
8 = 0x00000000 - 0xFFFFFFFF Max text length: 2,147,329,544
4+
1 = 0x0 - 0xF Max text length: 1
5+
2 = 0x00 - 0xFF Max text length: 105
6+
3 = 0x000 - 0xFFF Max text length: 2,024
7+
4 = 0x0000 - 0xFFFF Max text length: 32,443
8+
5 = 0x00000 - 0xFFFFF Max text length: 524,166
9+
6 = 0x000000 - 0xFFFFFF Max text length: 8,386,745
10+
7 = 0x0000000 - 0xFFFFFFF Max text length: 134,188,024
11+
8 = 0x00000000 - 0xFFFFFFFF Max text length: 2,147,329,539
1312
...
13+
14+
This affects the maximum length of the message that can be encrypted
15+
Max text length isn't equal to the number of bits that a position can have, because those bits are also used to store other things
1416
"""
-41 Bytes
Binary file not shown.

constants/constants.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
HEX_SYMB = "0123456789abcdef"
1010

11-
IMG_SIZE = int(np.sqrt((16**POS_LEN)//6)) # Size that the image will have (the image is a square)
12-
CIPHER_LEN = (IMG_SIZE**2)*6 # Length that the ciphertext will have
13-
TXT_E_MAX_LEN = CIPHER_LEN - len(format(CIPHER_LEN,"x")) # Max length text (encrypted) can have
14-
TXT_MAX_LEN = TXT_E_MAX_LEN//2 # Max length the input text can have (Because the encrypted text is twice the length of the input text)
11+
IMG_SIZE = int(np.sqrt((16**POS_LEN)//6)) # Size that the image will have (the image is a square)
12+
ENC_LEN = (IMG_SIZE**2)*6 # Length that the encrypted text will have
13+
TXT_MAX_LEN = ENC_LEN//2-1-POS_LEN # Max length text (not encrypted) can have

input/test.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
This is a test 123!!!
22

3-
The text can have every UTF-8 character! ✔️ ❤️ ☆
3+
It can have every UTF-8 character! ✔️ ❤️ ☆
2.11 KB
Binary file not shown.

utils/cryptography.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import hashlib
2+
import sys; sys.path.append("..")
3+
from constants.constants import *
4+
import random
5+
6+
def enc(txt, encryption_code:str) -> str:
7+
encTxt = ""
8+
for x in txt:
9+
encTxt += encryption_code[int(x,16)] # The symbol in the equivalent position
10+
return encTxt
11+
12+
13+
# Decrypt an encrypted text with an encryption code
14+
def dec(txt, encryption:str) -> str:
15+
decTxt = ""
16+
for x in txt:
17+
decTxt += HEX_SYMB[encryption.index(x)] # The symbol in the equivalent position
18+
return decTxt
19+
20+
21+
# Encode the password to sha512 and get a hash (length = 128)
22+
def get_pwd_hash(pwd:str) -> str:
23+
return hashlib.sha512(pwd.encode()).hexdigest()
24+
25+
26+
def get_encryption_code(hash:str) -> str:
27+
encryption_code = ""
28+
while len(encryption_code) < 16: # While the encryption string doesn't have 16 symbols
29+
if (hash[0] not in encryption_code):
30+
encryption_code += hash[0]
31+
hash = h2h(hash)
32+
return encryption_code
33+
34+
35+
def get_textE_len_idxs(seed:int, encryption_len:int, textE_len_digits:int) -> tuple: # Save txt_len_enc in the first POS_LEN indexes
36+
random.seed(seed)
37+
return random.sample(range(encryption_len), textE_len_digits)
38+
39+
def get_textE_idxs(seed:int, encryption_len:int, textE_len:int, textE_len_idxs:tuple) -> tuple:
40+
random.seed(seed)
41+
textE_idxs = []
42+
for _ in range(textE_len):
43+
while True:
44+
idx = random.randint(0, encryption_len-1)
45+
if idx not in textE_idxs and idx not in textE_len_idxs:
46+
textE_idxs.append(idx)
47+
break
48+
return textE_idxs
49+
50+
51+
# Create a new hash from a hash
52+
def h2h(hash:str, iters:int=1) -> str:
53+
for _ in range(iters):
54+
hash = hashlib.sha512(hash.encode()).hexdigest()
55+
return hash
56+
57+
# Convert from text to hexadecimal
58+
def t2h(txt:str) -> str:
59+
return txt.encode('utf-8').hex()
60+
61+
# Convert hexadecimal to utf-8
62+
def h2t(hex:str)-> str:
63+
return bytes.fromhex(hex).decode('utf-8')

0 commit comments

Comments
 (0)