44be used in any other use case than educational and no security is guaranteed for data
55encrypted or decrypted using this tool.
66"""
7+ import os
78
89# Imported libraries
910import numpy as np # Used for arrays and mathematical operations.
1011import galois # Used for GF(2^8) multiplication in mix columns operation.
1112from numpy .typing import NDArray # Used for type hinting numpy arrays.
1213from typing import Any # Used for type hinting __getattr__ function.
1314from secrets import token_bytes # Used for generating random key if needed.
15+ from os .path import getsize # Used to acquire size of files.
16+ from os import remove # Used to remove files.
1417
1518
1619class AES :
@@ -138,7 +141,7 @@ def get(self, item: str) -> Any:
138141 :param item: Attribute to be retrieved. Valid attributes (running_mode, key, iv).
139142 :return: Attribute value.
140143 """
141- if item :
144+ if item in [ "running_mode" , "key" , "iv" ] :
142145 return self .__dict__ [f"_{ item } " ]
143146 else :
144147 raise AttributeError (f"No attribute <{ item } > exists!" )
@@ -176,8 +179,17 @@ def enc(self, *, data_string: str = "", file_path: str = "",
176179 return self .__cbc_enc (data_string = data_string , keys = self .key_expand (key ), iv = iv )
177180 else :
178181 raise NotImplementedError (f"{ running_mode } is not supported!" )
182+ elif file_path :
183+ if running_mode == "ECB" :
184+ self .__ecb_enc (file_path = file_path , keys = self .key_expand (key ))
185+ return ""
186+ elif running_mode == "CBC" :
187+ self .__cbc_enc (file_path = file_path , keys = self .key_expand (key ), iv = iv )
188+ return ""
189+ else :
190+ raise NotImplementedError (f"{ running_mode } is not supported!" )
179191 else :
180- raise NotImplementedError ( "File encryption is not implemented yet ..." )
192+ raise RuntimeWarning ( "No file or string was give ..." )
181193
182194 def dec (self , * , data_string : str = "" , file_path : str = "" ,
183195 running_mode : str = "" , key : str = "" , iv : str = "" ) -> str :
@@ -212,8 +224,17 @@ def dec(self, *, data_string: str = "", file_path: str = "",
212224 return self .__cbc_dec (data_string = data_string , keys = self .key_expand (key ), iv = iv )
213225 else :
214226 raise NotImplementedError (f"{ running_mode } is not supported!" )
227+ elif file_path :
228+ if running_mode == "ECB" :
229+ self .__ecb_dec (file_path = file_path , keys = self .key_expand (key ))
230+ return ""
231+ elif running_mode == "CBC" :
232+ self .__cbc_dec (file_path = file_path , keys = self .key_expand (key ), iv = iv )
233+ return ""
234+ else :
235+ raise NotImplementedError (f"{ running_mode } is not supported!" )
215236 else :
216- raise NotImplementedError ( "File encryption is not implemented yet ..." )
237+ raise RuntimeWarning ( "No file or string was give ..." )
217238
218239 @classmethod
219240 def __ecb_enc (cls , * , data_string : str = "" , file_path : str = "" , keys : NDArray [np .uint8 ]) -> str :
@@ -234,7 +255,7 @@ def __ecb_enc(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
234255 enc : str = "" .join (cls .vec_chr (cls .__enc_schedule (raw , keys ).flatten ().astype (np .uint8 )))
235256 output_string += enc
236257
237- extra = len (data_string ) % 16 # Calculates length of final data block
258+ extra = len (data_string ) % 16 # Calculates length of final data block
238259 result : str = ""
239260
240261 if extra != 0 : # If last data block not integer multiple of 16 adds extra padding
@@ -244,8 +265,33 @@ def __ecb_enc(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
244265 result = "" .join (cls .vec_chr (cls .__enc_schedule (raw .reshape (4 , 4 ), keys ).flatten ().astype (np .uint8 )))
245266
246267 return output_string + result
268+ elif file_path :
269+ file_size = getsize (file_path )
270+
271+ with open (f"{ file_path } .enc" , 'wb' ) as output , open (file_path , 'rb' ) as data :
272+ for i in range (int (file_size / 16 )):
273+ raw = np .array ([i for i in data .read (16 )], dtype = np .uint8 ).reshape (4 , 4 )
274+ out = bytes ((cls .__enc_schedule (raw , keys ).flatten ()).tolist ())
275+ output .write (out )
276+
277+ if file_size % 16 != 0 :
278+ raw_l = [i for i in data .read ()]
279+ raw_l , length = cls .__add_padding (raw_l )
280+
281+ out = bytes ((cls .__enc_schedule (np .array (raw_l ).reshape (4 , 4 ), keys ).flatten ()).tolist ())
282+ identifier = bytes ((cls .__enc_schedule (np .array ([0 for i in range (15 )] + [length ]).reshape (4 , 4 ),
283+ keys ).flatten ()).tolist ())
284+
285+ output .write (out + identifier )
286+ else :
287+ identifier = bytes ((cls .__enc_schedule (np .array ([0 for i in range (16 )]).reshape (4 , 4 ),
288+ keys ).flatten ()).tolist ())
289+ output .write (identifier )
290+
291+ remove (file_path )
292+ return ""
247293 else :
248- raise NotImplementedError
294+ raise RuntimeError ( "No string or file path received...?" )
249295
250296 @classmethod
251297 def __ecb_dec (cls , * , data_string : str = "" , file_path : str = "" , keys : NDArray [np .uint8 ]) -> str :
@@ -268,8 +314,30 @@ def __ecb_dec(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
268314 output_string += dec
269315
270316 return output_string
317+ elif file_path :
318+ file_size = getsize (file_path )
319+ file_name = file_path [:- 4 ]
320+
321+ with open (f"{ file_name } " , 'wb' ) as output , open (file_path , 'rb' ) as data :
322+ for i in range (int (file_size / 16 ) - 2 ):
323+ raw = np .array ([i for i in data .read (16 )]).reshape (4 , 4 )
324+ result = bytes ((cls .__dec_schedule (raw , keys ).flatten ()).tolist ())
325+ output .write (result )
326+
327+ data_final = np .array ([i for i in data .read (16 )]).reshape (4 , 4 )
328+ identifier = np .array ([i for i in data .read ()]).reshape (4 , 4 )
329+
330+ data_dec = (cls .__dec_schedule (data_final , keys ).flatten ()).tolist ()
331+ id_l = (cls .__dec_schedule (identifier , keys ).flatten ()).tolist ()
332+
333+ result = bytes (cls .__remove_padding (data_dec , id_l ))
334+
335+ output .write (result )
336+
337+ remove (file_path )
338+ return ""
271339 else :
272- raise NotImplementedError
340+ raise RuntimeError ( "No string or file path received...?" )
273341
274342 @classmethod
275343 def __cbc_enc (cls , * , data_string : str = "" , file_path : str = "" , keys : NDArray [np .uint8 ], iv : str ) -> str :
@@ -289,7 +357,7 @@ def __cbc_enc(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
289357 for i in range (len (data_string ) // 16 ): # Encryption cycle, skips last if not integer multiple of 16 bytes
290358 raw : NDArray [np .uint8 ] = np .array ([ord (i ) for i in data_string [(i * 16 ): ((i + 1 ) * 16 )]],
291359 dtype = np .uint8 ).reshape (4 , 4 )
292- enc = np .bitwise_xor (raw , enc_array ) # Xor operation with previous encrypted block or iv
360+ enc = np .bitwise_xor (raw , enc_array ) # Xor operation with previous encrypted block or iv
293361 enc_array = cls .__enc_schedule (enc , keys )
294362 output_string += "" .join (cls .vec_chr (enc_array .flatten ().astype (np .uint8 )))
295363
@@ -306,8 +374,35 @@ def __cbc_enc(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
306374 result = "" .join (cls .vec_chr (cls .__enc_schedule (temp_array , keys ).flatten ().astype (np .uint8 )))
307375
308376 return output_string + result
377+ elif file_path :
378+ file_size = getsize (file_path )
379+
380+ with open (f"{ file_path } .enc" , 'wb' ) as output , open (file_path , 'rb' ) as data :
381+ for i in range (int (file_size / 16 )):
382+ raw = np .array ([i for i in data .read (16 )]).reshape (4 , 4 )
383+ raw = np .bitwise_xor (raw , enc_array )
384+ enc_array = cls .__enc_schedule (raw , keys )
385+ output .write (bytes ((enc_array .flatten ()).tolist ()))
386+
387+ if file_size % 16 != 0 :
388+ final = [i for i in data .read ()]
389+ final , length = cls .__add_padding (final )
390+
391+ raw = np .bitwise_xor (np .array (final ).reshape (4 , 4 ), enc_array )
392+ enc_array = cls .__enc_schedule (raw , keys )
393+
394+ identifier = np .bitwise_xor (np .array ([0 for i in range (15 )] + [length ]).reshape (4 , 4 ), enc_array )
395+ identifier = cls .__enc_schedule (identifier , keys )
396+
397+ output .write (bytes ((enc_array .flatten ()).tolist () + (identifier .flatten ()).tolist ()))
398+ else :
399+ identifier = np .bitwise_xor (np .array ([0 for i in range (16 )]).reshape (4 , 4 ), enc_array )
400+ id_bytes = bytes (((cls .__enc_schedule (identifier , keys )).flatten ()).tolist ())
401+ output .write (id_bytes )
402+ remove (file_path )
403+ return ""
309404 else :
310- raise NotImplementedError
405+ raise RuntimeError ( "No string or file path received...?" )
311406
312407 @classmethod
313408 def __cbc_dec (cls , * , data_string : str = "" , file_path : str = "" , keys : NDArray [np .uint8 ], iv : str ) -> str :
@@ -325,7 +420,7 @@ def __cbc_dec(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
325420 output_string : str = ""
326421
327422 for i in range (len (data_string ) // 16 ): # Decryption cycle
328- raw : NDArray [np .uint8 ] = np .array ( # Reads in input string 16 bytes at a time
423+ raw : NDArray [np .uint8 ] = np .array ( # Reads in input string 16 bytes at a time
329424 [ord (i ) for i in data_string [(i * 16 ): ((i + 1 ) * 16 )]], dtype = np .uint8 ).reshape (4 , 4 )
330425
331426 temp_array = cls .__dec_schedule (raw , keys )
@@ -336,8 +431,44 @@ def __cbc_dec(cls, *, data_string: str = "", file_path: str = "", keys: NDArray[
336431 output_string += dec
337432
338433 return output_string
434+ elif file_path :
435+ file_size = getsize (file_path )
436+ file_name = file_path [:- 4 ]
437+
438+ with open (f"{ file_name } " , 'wb' ) as output , open (file_path , 'rb' ) as data :
439+ if int (file_size / 16 ) - 3 >= 0 :
440+ vector = np .array ([i for i in data .read (16 )]).reshape (4 , 4 )
441+ raw = cls .__dec_schedule (vector , keys )
442+ result = np .bitwise_xor (raw , dec_array )
443+ output .write (bytes ((result .flatten ()).tolist ()))
444+
445+ for i in range (int (file_size / 16 ) - 3 ):
446+ raw = np .array ([i for i in data .read (16 )]).reshape (4 , 4 )
447+ result = cls .__dec_schedule (raw , keys )
448+ result = np .bitwise_xor (result , vector )
449+ vector = raw
450+ output .write (bytes ((result .flatten ()).tolist ()))
451+ else :
452+ vector = dec_array
453+
454+ data_pice = np .array ([i for i in data .read (16 )]).reshape (4 , 4 )
455+ vector_1 , identifier = data_pice , np .array ([i for i in data .read ()]).reshape (4 , 4 )
456+
457+ result = cls .__dec_schedule (data_pice , keys )
458+ identifier = cls .__dec_schedule (identifier , keys )
459+
460+ identifier = np .bitwise_xor (identifier , vector_1 )
461+ data_pice = np .bitwise_xor (result , vector )
462+
463+ result_bytes = bytes (cls .__remove_padding ((data_pice .flatten ()).tolist (),
464+ (identifier .flatten ()).tolist ()))
465+
466+ output .write (result_bytes )
467+
468+ remove (file_path )
469+ return ""
339470 else :
340- raise NotImplementedError
471+ raise RuntimeError ( "No string or file path received...?" )
341472
342473 @staticmethod
343474 def key_gen (length : int = 16 ) -> str :
@@ -533,3 +664,21 @@ def __mix_columns(matrix: NDArray[np.uint8], shift: int) -> NDArray[np.uint8]:
533664 result [j , i ] = temp
534665
535666 return result .transpose ()
667+
668+ @staticmethod
669+ def __add_padding (data : list [int ]) -> tuple [list [int ], int ]:
670+ """Adds a padding to ensure a bloke size of 16 bytes."""
671+ length = 16 - len (data )
672+ for i in range (length ):
673+ data .append (0 )
674+ return data , length
675+
676+ @staticmethod
677+ def __remove_padding (data : list [int ], identifier : list [int ]) -> list [int ]:
678+ """Removes the padding from the data using information from identifier."""
679+ if identifier [- 1 ] == 0 :
680+ return data
681+ elif 0 < identifier [- 1 ] < 16 :
682+ return data [:- identifier [- 1 ]]
683+ else :
684+ raise ValueError ('Invalid padding' )
0 commit comments