Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
carlospuenteg authored Oct 1, 2022
1 parent 4103e9f commit dab4d55
Show file tree
Hide file tree
Showing 17 changed files with 94 additions and 175 deletions.
145 changes: 2 additions & 143 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,150 +1,9 @@
# Encroval

## Index
* [Description](#description)
* [Get Started](#get-started)
* [Install requirements](#install-requirements)
* [Enter the input](#enter-the-input)
* [Run](#run)
* [Configuration](#configuration)
* [Examples](#examples)
* [Encryption](#encryption-example)
* [Text Decryption](#text-decryption-example)
* [Image Decryption](#image-decryption-example)

## Description

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

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).




## Get Started

#### Install Requirements

```bash
pip3 install -r requirements.txt
```


#### Enter the input

##### Select the text to encrypt
Place the text you want to encrypt (a `.txt` file) in the `input` folder.

##### Select the text/image to decrypt
Place the text you want to encrypt (a `.txt` file) or the image (a `.png` file) in the `input` folder.


#### Run

```bash
python3 main.py
```




## Configuration

You can change the `POS_LEN` in the `config.py` file, which changes the input text's maximum length.

Default is `POS_LEN = 4`, so the maximum length the text can have by default is **32,446**

The more the `POS_LEN` is, the longer the text can be, but the longer it will take to encrypt and decrypt it.

| POS_LEN | Max text lenght |
|-|-|
| 1 | 2 |
| 2 | 107 |
| 3 | 2,026 |
| 4 | 32,446 |
| 5 | 524,169 |
| 6 | 8,386,749 |
| 7 | 134,188,028 |
| 8 | 2,147,329,544 |
| ... | ... |




## Examples

### Encryption example

<img width=400 src=readme-assets/example_encryption.png>

##### `config.py`

```python
POS_LEN = 2
```

##### Input text (`input/test.txt`)
```txt
This is a test 123!!!
It can have every UTF-8 character! ✔️ ❤️ ☆
```

##### Password
```txt
test#@–{}password123¿?
```

##### Encrypted text (`output/encrypted_text.txt`)
```txt
3e8b00b4bcbb4ff246fdb3bc9afd63cf080e1cbefdc4b4bb2b5f1400f3fd4e6cb10d40825d0ab41e080e4e751a1ebbb8e7c4448fc14434d5c84d7fb3cc68e2c66033d5cfeece84bd256888b5e3dbb5bdc7fd47845be373e44bc8defbabb92e544f5eb0b4c43403084344d663
```

##### Encrypted image (`output/encrypted_image.png`)
<img width=70 src=readme-assets/encrypted_image.png>



### Text Decryption example

<img width=400 src=readme-assets/example_text_decryption.png>

##### Input text (`input/encrypted_text.txt`)
```txt
This is a test 123!!!
It can have every UTF-8 character! ✔️ ❤️ ☆
```

##### Password
```txt
test#@–{}password123¿?
```

##### Decrypted text (`output/decrypted_text.txt`)
```txt
This is a test 123!!!
The text can have every UTF-8 character! ✔️ ❤️ ☆
```



### Image Decryption example

<img width=400 src=readme-assets/example_image_decryption.png>

##### Input image (`input/encrypted_image.png`)
<img width=70 src=readme-assets/encrypted_image.png>

##### Password
```txt
test#@–{}password123¿?
```

##### Decrypted image (`output/decrypted_image.txt`)
```txt
This is a test 123!!!
It can encrypt a text of any length with a password and the only way to decrypt it is by knowing it.

The text can have every UTF-8 character! ✔️ ❤️ ☆
```
The password can be up to 2<sup>128</sup> bits long and accepts any UTF-8 character (1,112,064 different characters).
11 changes: 5 additions & 6 deletions classes/Decryptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ def __init__(self, ciphertext:str, pwd:str):


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:
encrypted_len = len(self.ciphertext) # Same as ENC_LEN
if not all(c in HEX_SYMB for c in self.ciphertext) or len(self.ciphertext) != encrypted_len:
raise Exception("Encrypted text is invalid")

# Seed the random generator with the hash
Expand All @@ -25,13 +24,13 @@ def decrypt(self) -> str:
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_len_digits = len(format(encrypted_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, encrypted_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_idxs = get_textE_idxs(seed, encrypted_len, textE_len, textE_len_idxs)
textE = "".join([self.ciphertext[idx] for idx in textE_idxs])
text = h2t(dec(textE, encryption_code))

Expand Down
18 changes: 8 additions & 10 deletions classes/Encryptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def __init__(self, text:str, pwd:str):
self.hash = get_pwd_hash(self.pwd)


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

Expand All @@ -22,21 +22,19 @@ def encrypt(self) -> str: # Returns a ciphertext
# 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)]
# Fill the encrypted text with random characters
ciphertext = [random.choice(HEX_SYMB) for _ in range(ENC_LEN)]

# INFO: Encrypt the text
# INFO: Encrypt the text and text length
textE = enc(t2h(self.text), encryption_code)

# INFO: Encrypt the text length
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
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_len_fixed_len = format(textE_len,"x").rjust(textE_len_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)
textE_len_idxs = get_textE_len_idxs(seed, ENC_LEN, textE_len_digits)
textE_idxs = get_textE_idxs(seed, ENC_LEN, len(textE), textE_len_idxs)

# Save the text and text length in toret
for i,idx in enumerate(textE_len_idxs):
Expand Down
1 change: 0 additions & 1 deletion classes/Menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
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:
Expand Down
Binary file added classes/__pycache__/Cryptography.cpython-310.pyc
Binary file not shown.
Binary file added classes/__pycache__/Decryptor.cpython-310.pyc
Binary file not shown.
Binary file added classes/__pycache__/Encryptor.cpython-310.pyc
Binary file not shown.
Binary file added classes/__pycache__/ImageCreator.cpython-310.pyc
Binary file not shown.
Binary file added classes/__pycache__/Menu.cpython-310.pyc
Binary file not shown.
Binary file added classes/__pycache__/Options.cpython-310.pyc
Binary file not shown.
Binary file added classes/__pycache__/Text.cpython-310.pyc
Binary file not shown.
22 changes: 12 additions & 10 deletions config.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
POS_LEN = 2
POS_LEN = 4
"""
The length that the positions in the encryption will have (in hex)
1 = 0x0 - 0xF Max text length: 2
2 = 0x00 - 0xFF Max text length: 107
3 = 0x000 - 0xFFF Max text length: 2,026
4 = 0x0000 - 0xFFFF Max text length: 32,446
5 = 0x00000 - 0xFFFFF Max text length: 524,169
6 = 0x000000 - 0xFFFFFF Max text length: 8,386,749
7 = 0x0000000 - 0xFFFFFFF Max text length: 134,188,028
8 = 0x00000000 - 0xFFFFFFFF Max text length: 2,147,329,544
1 = 0x0 - 0xF Max text length: 1
2 = 0x00 - 0xFF Max text length: 105
3 = 0x000 - 0xFFF Max text length: 2,024
4 = 0x0000 - 0xFFFF Max text length: 32,443
5 = 0x00000 - 0xFFFFF Max text length: 524,166
6 = 0x000000 - 0xFFFFFF Max text length: 8,386,745
7 = 0x0000000 - 0xFFFFFFF Max text length: 134,188,024
8 = 0x00000000 - 0xFFFFFFFF Max text length: 2,147,329,539
...
This affects the maximum length of the message that can be encrypted
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
"""
Binary file modified constants/__pycache__/constants.cpython-310.pyc
Binary file not shown.
7 changes: 3 additions & 4 deletions constants/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

HEX_SYMB = "0123456789abcdef"

IMG_SIZE = int(np.sqrt((16**POS_LEN)//6)) # Size that the image will have (the image is a square)
CIPHER_LEN = (IMG_SIZE**2)*6 # Length that the ciphertext will have
TXT_E_MAX_LEN = CIPHER_LEN - len(format(CIPHER_LEN,"x")) # Max length text (encrypted) can have
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)
IMG_SIZE = int(np.sqrt((16**POS_LEN)//6)) # Size that the image will have (the image is a square)
ENC_LEN = (IMG_SIZE**2)*6 # Length that the encrypted text will have
TXT_MAX_LEN = ENC_LEN//2-1-POS_LEN # Max length text (not encrypted) can have
2 changes: 1 addition & 1 deletion input/test.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
This is a test 123!!!

The text can have every UTF-8 character! ✔️ ❤️ ☆
It can have every UTF-8 character! ✔️ ❤️ ☆
Binary file added utils/__pycache__/cryptography.cpython-310.pyc
Binary file not shown.
63 changes: 63 additions & 0 deletions utils/cryptography.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import hashlib
import sys; sys.path.append("..")
from constants.constants import *
import random

def enc(txt, encryption_code:str) -> str:
encTxt = ""
for x in txt:
encTxt += encryption_code[int(x,16)] # The symbol in the equivalent position
return encTxt


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


# Encode the password to sha512 and get a hash (length = 128)
def get_pwd_hash(pwd:str) -> str:
return hashlib.sha512(pwd.encode()).hexdigest()


def get_encryption_code(hash:str) -> str:
encryption_code = ""
while len(encryption_code) < 16: # While the encryption string doesn't have 16 symbols
if (hash[0] not in encryption_code):
encryption_code += hash[0]
hash = h2h(hash)
return encryption_code


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
random.seed(seed)
return random.sample(range(encryption_len), textE_len_digits)

def get_textE_idxs(seed:int, encryption_len:int, textE_len:int, textE_len_idxs:tuple) -> tuple:
random.seed(seed)
textE_idxs = []
for _ in range(textE_len):
while True:
idx = random.randint(0, encryption_len-1)
if idx not in textE_idxs and idx not in textE_len_idxs:
textE_idxs.append(idx)
break
return textE_idxs


# Create a new hash from a hash
def h2h(hash:str, iters:int=1) -> str:
for _ in range(iters):
hash = hashlib.sha512(hash.encode()).hexdigest()
return hash

# Convert from text to hexadecimal
def t2h(txt:str) -> str:
return txt.encode('utf-8').hex()

# Convert hexadecimal to utf-8
def h2t(hex:str)-> str:
return bytes.fromhex(hex).decode('utf-8')

0 comments on commit dab4d55

Please sign in to comment.