Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/method_for_visualization.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://words.filippo.io/the-ecb-penguin/
152 changes: 118 additions & 34 deletions src/AES_Module/AES.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
# ---------------
from os.path import getsize
from os import remove
from re import sub, search

# ---------------
# Program information m.m
Expand Down Expand Up @@ -108,6 +107,15 @@ def add_round_key(data, round_key):
return data


# XOR function
def xor(data1, data2):
data1, data2 = list_to_matrix(data1), list_to_matrix(data2)
for i in range(4):
for j in range(4):
data1[i][j] ^= data2[i][j]
return matrix_to_list(data1)


# Performs the byte substitution layer
def sub_bytes(data, bytesTable):
for r in range(4):
Expand Down Expand Up @@ -370,72 +378,148 @@ def SubWord(word):
def ecb_enc(key, file_path):
file_size = getsize(file_path)

with open(f"{file_path}.enc", 'wb') as output:
with open(file_path, 'rb') as data:

for i in range(int(file_size/16)):
raw = [i for i in data.read(16)]
result = bytes(encryption_rounds(raw, key))

output.write(result)
with open(f"{file_path}.enc", 'wb') as output, open(file_path, 'rb') as data:
for i in range(int(file_size/16)):
raw = [i for i in data.read(16)]
result = bytes(encryption_rounds(raw, key))
output.write(result)

if file_size % 16 != 0:
raw = [i for i in data.read()]
raw, length = add_padding(raw)
if file_size % 16 != 0:
raw = [i for i in data.read()]
raw, length = add_padding(raw)

result = bytes(encryption_rounds(raw, key))
identifier = bytes(encryption_rounds([0 for i in range(15)] + [length], key))
result = bytes(encryption_rounds(raw, key))
identifier = bytes(encryption_rounds([0 for i in range(15)] + [length], key))

output.write(result + identifier)
else:
identifier = bytes(encryption_rounds([0 for i in range(16)], key))
output.write(identifier)
output.write(result + identifier)
else:
identifier = bytes(encryption_rounds([0 for i in range(16)], key))
output.write(identifier)
remove(file_path)


# ECB decryption function
def ecb_dec(key, file_path):
if search('.enc', file_path) is None:
raise Exception('File is not encrypted in known format')
file_size = getsize(file_path)
file_name = file_path[:-4]

with open(f"{file_name}", 'wb') as output, open(file_path, 'rb') as data:
for i in range(int(file_size/16) - 2):
raw = [i for i in data.read(16)]
result = bytes(decryption_rounds(raw, key))
output.write(result)

data_pice = [i for i in data.read(16)]
identifier = [i for i in data.read()]

result = decryption_rounds(data_pice, key)
identifier = decryption_rounds(identifier, key)

result = bytes(remove_padding(result, identifier))

output.write(result)
remove(file_path)


# CBC encryption function
def cbc_enc(key, file_path, iv):
file_size = getsize(file_path)
vector = [int(iv[i:i+2], 16) for i in range(0, len(iv), 2)]

with open(f"{file_path}.enc", 'wb') as output, open(file_path, 'rb') as data:
for i in range(int(file_size/16)):
raw = [i for i in data.read(16)]
raw = xor(raw, vector)
vector = encryption_rounds(raw, key)
output.write(bytes(vector))

if file_size % 16 != 0:
raw = [i for i in data.read()]
raw, length = add_padding(raw)

raw = xor(raw, vector)
vector = encryption_rounds(raw, key)

identifier = xor(([0 for i in range(15)] + [length]), vector)
identifier = encryption_rounds(identifier, key)

output.write(bytes(vector + identifier))
else:
identifier = xor([0 for i in range(16)], vector)
identifier = bytes(encryption_rounds(identifier, key))
output.write(identifier)
remove(file_path)


# CBC decryption function
def cbc_dec(key, file_path, iv):
iv = [int(iv[i:i+2], 16) for i in range(0, len(iv), 2)]
file_size = getsize(file_path)
file_name = sub('.enc', '', file_path)
file_name = file_path[:-4]

with open(f"{file_name}", 'wb') as output:
with open(file_path, 'rb') as data:
with open(f"{file_name}", 'wb') as output, open(file_path, 'rb') as data:
if int(file_size/16) - 3 >= 0:
vector = [i for i in data.read(16)]
raw = decryption_rounds(vector, key)
result = xor(raw, iv)
output.write(bytes(result))

for i in range(int(file_size/16) - 2):
for i in range(int(file_size/16) - 3):
raw = [i for i in data.read(16)]
result = bytes(decryption_rounds(raw, key))
result = decryption_rounds(raw, key)
result = xor(result, vector)
vector = raw
output.write(bytes(result))
else:
vector = iv

output.write(result)
data_pice = [i for i in data.read(16)]
vector_1, identifier = data_pice, [i for i in data.read()]

data_pice = [i for i in data.read(16)]
identifier = [i for i in data.read()]
result = decryption_rounds(data_pice, key)
identifier = decryption_rounds(identifier, key)

result = decryption_rounds(data_pice, key)
identifier = decryption_rounds(identifier, key)
identifier = xor(identifier, vector_1)
data_pice = xor(result, vector)

result = bytes(remove_padding(result, identifier))
result = bytes(remove_padding(data_pice, identifier))

output.write(result)
output.write(result)
remove(file_path)


# ---------------
# AES main setup
# ---------------
# Encryption function
def encrypt(key, file_path, running_mode):
def encrypt(key, file_path, running_mode, iv=None):

# Input validation
if (len(key) / 2) not in [16, 24, 32]:
raise Exception('Key length is not valid')

# Running mode selection
if running_mode == "ECB":
ecb_enc(key, file_path)
elif running_mode == "CBC" and iv is not None:
cbc_enc(key, file_path, iv)
else:
raise Exception("Running mode not supported")


# Decryption function
def decrypt(key, file_path, running_mode):
def decrypt(key, file_path, running_mode, iv=None):

# Input validation
if file_path[-4:] != ".enc":
raise Exception('File is not encrypted in known format')
if (len(key) / 2) not in [16, 24, 32]:
raise Exception('Key length is not valid')

# Running mode selection
if running_mode == "ECB":
ecb_dec(key, file_path)
elif running_mode == "CBC" and iv is not None:
cbc_dec(key, file_path, iv)
else:
raise Exception("Running mode not supported")
23 changes: 11 additions & 12 deletions tests/Analyze.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import AES_Module.AES as AES

def enc():
key = "2b7e151628aed2a6abf7158809cf4f3c"
running_mode = "ECB"
file_path = r"C:\Users\Gabriel\Documents\GitHub\AES-Python\tmp\test_files\data.txt"

AES.encrypt(key, file_path, running_mode)

def dec():
def main(i):
key = "2b7e151628aed2a6abf7158809cf4f3c"
running_mode = "ECB"
file_path = r"C:\Users\Gabriel\Documents\GitHub\AES-Python\tmp\test_files\data.txt.enc"
iv = "000102030405060708090a0b0c0d0e0f"
running_mode = "CBC"
file_path = r"/Users/gabriellindeblad/Documents/GitHub/AES-Python/tmp/test_files/data.txt"

AES.decrypt(key, file_path, running_mode)
if i == "enc":
AES.encrypt(key, file_path, running_mode, iv)
else:
file_path += ".enc"
AES.decrypt(key, file_path, running_mode, iv)

if __name__ == '__main__':
dec()

if __name__ == '__main__':
main("enc")
73 changes: 71 additions & 2 deletions tests/test_&_debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def test_aes_actions_matrix_to_list():
def test_aes_actions_add_round_key():
assert AES.add_round_key([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) == [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]

def test_aes_actions_xor():
assert AES.xor([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

def test_aes_actions_sub_bytes():
assert AES.sub_bytes([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]], subBytesTable) == [[0x63, 0x7c, 0x77, 0x7b], [0xf2, 0x6b, 0x6f, 0xc5], [0x30, 0x01, 0x67, 0x2b], [0xfe, 0xd7, 0xab, 0x76]]

Expand Down Expand Up @@ -232,20 +235,86 @@ def test_aes_decryption_ECB(data, key, file_name, expected):

assert result == expected

@pytest.mark.parametrize("data,key,file_name,iv,expected", [
# 128 bit
(b'1234567890', "2b7e151628aed2a6abf7158809cf4f3c", "tmp.txt", "000102030405060708090a0b0c0d0e0f", b'\xe4\xa7\x0e\xbd\x84\xfa\xf5\xd8`\xb8\xa1\x10\x0b~\xadh\x89Feso\xc5~_|\xe9\x1bG\xd9*\\\x81'),
(b'1234567890123456', "2b7e151628aed2a6abf7158809cf4f3c", "tmp1.txt", "000102030405060708090a0b0c0d0e0f", b'\x1b\x16\x86:\xb9*w\xc5)"\xe4\xe9D\\\xf1\xee\r\xd6F?\x82\xd5\x02\x9e\xf6\xc2vJ\xdc\x05\x92\xbc'),
# 192 bit
(b'1234567890', "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "tmp2.txt", "000102030405060708090a0b0c0d0e0f", b'\x89\x8fwWh\xaf\xfb@\xc9\xc3\xc0w\x81\xf7\x0e\xd3\xfd\x93\r\x15\x05\xc7\xb5%\xc2k\t\xe8s*\xa7\x9e'),
(b'1234567890123456', "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "tmp3.txt", "000102030405060708090a0b0c0d0e0f", b'5\xb9\x19\x1dd\xf3e\xd7EP\x01^8\xb0\xf6\xfb\xc1\x86\xafZ\x0c\x11\x13\x1d4P\x85\x1b"\xdf\x14\xc6'),
# 256 bit
(b'1234567890', "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "tmp4.txt", "000102030405060708090a0b0c0d0e0f", b'\x9dT\xb7B\x19e\xb8q\xc95\xfa\x80L\x88.9)`\xef\xc2\x10\x9a\x95\x90U\xe0\x0f N\x80\xba\xb3'),
(b'1234567890123456', "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "tmp5.txt", "000102030405060708090a0b0c0d0e0f", b'a\xfdIRQ\xf8\xf1D\xcc\xbf\x89\xc8\xd6\xec\x01;pNAT\xedT\xd9Tp-_\xbbr\xd3\xb5\x11')
])
def test_aes_encrypt_CBC(data, key, file_name, iv, expected):
with open(file_name, "wb") as file:
file.write(data)

AES.encrypt(key, file_name, "CBC", iv)

with open(f"{file_name}.enc", "rb") as file:
result = file.read()

os.remove(f"{file_name}.enc")

assert result == expected

@pytest.mark.parametrize("data,key,file_name,iv,expected", [
# 128 bit
(b'\xe4\xa7\x0e\xbd\x84\xfa\xf5\xd8`\xb8\xa1\x10\x0b~\xadh\x89Feso\xc5~_|\xe9\x1bG\xd9*\\\x81', "2b7e151628aed2a6abf7158809cf4f3c", "tmp1.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890'),
(b'\x1b\x16\x86:\xb9*w\xc5)"\xe4\xe9D\\\xf1\xee\r\xd6F?\x82\xd5\x02\x9e\xf6\xc2vJ\xdc\x05\x92\xbc', "2b7e151628aed2a6abf7158809cf4f3c", "tmp2.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890123456'),
(b'\x1b\x16\x86:\xb9*w\xc5)"\xe4\xe9D\\\xf1\xee^\x84=\xa1\x00<J\xfc\xdfC#\xf7\x9d\xee~\x7f,\x92ZVX \x1ck\xac\xf2\xd2\xe6\x17u\xa2\xc1', "2b7e151628aed2a6abf7158809cf4f3c", "tmp7.txt", "000102030405060708090a0b0c0d0e0f", b'12345678901234567890'),
(b'\x1b\x16\x86:\xb9*w\xc5)"\xe4\xe9D\\\xf1\xee\x8b\x03\xcc\xe7\x0c~\xba7\xcf\x0f\x9c\x16dM$\xe9\x91\xef\xc3\xa6\xd2\xf0\xcd\xc2\xee\x86\xf0\x90\x8a]\x87\xf5R\xe2.c\xd4\xc6T\xdc\xe0#\xa7X\x8b_\x81\x04', "2b7e151628aed2a6abf7158809cf4f3c", "tmp8.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890123456789012345678901234567890'),
# 192 bit
(b'\x89\x8fwWh\xaf\xfb@\xc9\xc3\xc0w\x81\xf7\x0e\xd3\xfd\x93\r\x15\x05\xc7\xb5%\xc2k\t\xe8s*\xa7\x9e', "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "tmp3.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890'),
(b'5\xb9\x19\x1dd\xf3e\xd7EP\x01^8\xb0\xf6\xfb\xc1\x86\xafZ\x0c\x11\x13\x1d4P\x85\x1b"\xdf\x14\xc6', "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "tmp4.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890123456'),
# 256 bit
(b'\x9dT\xb7B\x19e\xb8q\xc95\xfa\x80L\x88.9)`\xef\xc2\x10\x9a\x95\x90U\xe0\x0f N\x80\xba\xb3', "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "tmp5.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890'),
(b'a\xfdIRQ\xf8\xf1D\xcc\xbf\x89\xc8\xd6\xec\x01;pNAT\xedT\xd9Tp-_\xbbr\xd3\xb5\x11', "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "tmp6.txt", "000102030405060708090a0b0c0d0e0f", b'1234567890123456')
])
def test_aes_decryption_CBC(data, key, file_name, iv, expected):
with open(f"{file_name}.enc", "wb") as file:
file.write(data)

AES.decrypt(key, f"{file_name}.enc", "CBC", iv)

with open(file_name, "rb") as file:
result = file.read()

os.remove(file_name)

assert result == expected

def test_aes_decryption_exeption():
with pytest.raises(Exception) as e:
AES.decrypt("1234567890123456", "tmp.txt", "ECB")
assert str(e.value) == 'File is not encrypted in known format'
assert e.type == Exception

with pytest.raises(Exception) as e:
AES.decrypt("1234567890123456", "tmp.txt.enc", "CBC")
assert str(e.value) == 'Key length is not valid'
assert e.type == Exception

def test_aes_encryption_exeption():
with pytest.raises(Exception) as e:
AES.encrypt("123456789012345", "tmp.txt", "CBC")
assert str(e.value) == 'Key length is not valid'
assert e.type == Exception

with pytest.raises(Exception) as e:
AES.encrypt("1234567890123456", "tmp.txt", "ECB")
assert str(e.value) == 'Key length is not valid'
assert e.type == Exception

def test_aes_running_mode_exeption():
with pytest.raises(Exception) as e:
AES.encrypt("1234567890123456", "tmp.txt", "a<wertygraewtg")
AES.encrypt("12345678901234567890123456789012", "tmp.txt", "a<wertygraewtg")
assert str(e.value) == 'Running mode not supported'
assert e.type == Exception

with pytest.raises(Exception) as p:
AES.decrypt("1234567890123456", "tmp.txt", "wrseyhstehy")
AES.decrypt("12345678901234567890123456789012", "tmp.txt.enc", "wrseyhstehy")
assert str(p.value) == 'Running mode not supported'
assert p.type == Exception

Expand Down
16,253 changes: 16,253 additions & 0 deletions tests/test_data/File_enc_visualization_1/A.ppm

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1,247 changes: 1,247 additions & 0 deletions tests/test_data/File_enc_visualization_1/body.bin.enc

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tests/test_data/File_enc_visualization_1/header.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
P6
# Created by GIMP version 2.10.22 PNM plug-in
600 600
255
581 changes: 581 additions & 0 deletions tests/test_data/File_enc_visualization_2/body.bin.enc

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tests/test_data/File_enc_visualization_2/header.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
P6
# Created by GIMP version 2.10.22 PNM plug-in
1280 1238
255
5 changes: 5 additions & 0 deletions tests/test_data/File_enc_visualization_2/pi.ppm

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/test_data/File_enc_visualization_3/body.bin

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tests/test_data/File_enc_visualization_3/header.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
P6
# Created by GIMP version 2.10.22 PNM plug-in
1280 1238
255
Binary file not shown.
5 changes: 5 additions & 0 deletions tests/test_data/File_enc_visualization_3/pi.ppm

Large diffs are not rendered by default.

Loading