import argparse, itertools, os from copy import deepcopy from os import path class aes_base: _SBox = [ [0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76], [0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0], [0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15], [0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75], [0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84], [0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF], [0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8], [0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2], [0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73], [0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB], [0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79], [0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08], [0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A], [0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E], [0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF], [0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16] ] _SBoxInv = [ [0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB], [0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB], [0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E], [0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25], [0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92], [0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84], [0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06], [0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B], [0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73], [0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E], [0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B], [0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4], [0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F], [0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF], [0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61], [0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D] ] ByteSize = 8 RoundNumber = 9 BlockSize = 128 def encrypt(PlainTextMatrix,KeyRoundMatrix): Inverse = False ARK = aes_base._add_round_key(PlainTextMatrix,aes_base._get_round_key(KeyRoundMatrix,0)) for i in range(aes_base.RoundNumber): BSB = aes_base._byte_sub(ARK,Inverse) SR = aes_base._shift_row(BSB,Inverse) MC = aes_base._mix_columns(SR,Inverse) ARK = aes_base._add_round_key(MC,aes_base._get_round_key(KeyRoundMatrix,i+1)) BSB = aes_base._byte_sub(ARK,Inverse) SR = aes_base._shift_row(BSB,Inverse) ARK = aes_base._add_round_key(SR,aes_base._get_round_key(KeyRoundMatrix,aes_base.RoundNumber+1)) return ARK def decrypt(CipherTextMatrix,KeyRoundMatrix): Inverse = True ARK = aes_base._add_round_key(CipherTextMatrix,aes_base._get_round_key(KeyRoundMatrix,0)) SR = aes_base._shift_row(ARK,Inverse) BSB = aes_base._byte_sub(SR,Inverse) for i in range(aes_base.RoundNumber): ARK = aes_base._add_round_key(BSB,aes_base._get_round_key(KeyRoundMatrix,i+1)) MC = aes_base._mix_columns(ARK,Inverse) SR = aes_base._shift_row(MC,Inverse) BSB = aes_base._byte_sub(SR,Inverse) ARK = aes_base._add_round_key(BSB,aes_base._get_round_key(KeyRoundMatrix,aes_base.RoundNumber+1)) return ARK def _byte_sub(Matrix,Inverse = False): if Inverse == False: return [[aes_base._SBox[int(bin(Value)[2:].zfill(8)[:aes_base.ByteSize//2],2)][int(bin(Value)[2:].zfill(8)[aes_base.ByteSize//2:],2)] for Value in Row] for Row in Matrix] else: return [[aes_base._SBoxInv[int(bin(Value)[2:].zfill(8)[:aes_base.ByteSize//2],2)][int(bin(Value)[2:].zfill(8)[aes_base.ByteSize//2:],2)] for Value in Row] for Row in Matrix] def _shift_row(Matrix,Inverse = False): if Inverse == False: return [aes_base._row_rotate(Row,Idx) for Idx,Row in enumerate(Matrix)] else: return [aes_base._row_rotate(Row,Idx,True) for Idx,Row in enumerate(Matrix)] def _mix_columns(Matrix,Inverse = False): MC = deepcopy(Matrix) if Inverse == False: for i in range(aes_base.ByteSize//2): MC[0][i] = aes_base._g_mul(Matrix[0][i],2)^aes_base._g_mul(Matrix[1][i],3)^aes_base._g_mul(Matrix[2][i],1)^aes_base._g_mul(Matrix[3][i],1) MC[1][i] = aes_base._g_mul(Matrix[0][i],1)^aes_base._g_mul(Matrix[1][i],2)^aes_base._g_mul(Matrix[2][i],3)^aes_base._g_mul(Matrix[3][i],1) MC[2][i] = aes_base._g_mul(Matrix[0][i],1)^aes_base._g_mul(Matrix[1][i],1)^aes_base._g_mul(Matrix[2][i],2)^aes_base._g_mul(Matrix[3][i],3) MC[3][i] = aes_base._g_mul(Matrix[0][i],3)^aes_base._g_mul(Matrix[1][i],1)^aes_base._g_mul(Matrix[2][i],1)^aes_base._g_mul(Matrix[3][i],2) else: for i in range(aes_base.ByteSize//2): MC[0][i] = aes_base._g_mul(Matrix[0][i],14)^aes_base._g_mul(Matrix[1][i],11)^aes_base._g_mul(Matrix[2][i],13)^aes_base._g_mul(Matrix[3][i],9) MC[1][i] = aes_base._g_mul(Matrix[0][i],9)^aes_base._g_mul(Matrix[1][i],14)^aes_base._g_mul(Matrix[2][i],11)^aes_base._g_mul(Matrix[3][i],13) MC[2][i] = aes_base._g_mul(Matrix[0][i],13)^aes_base._g_mul(Matrix[1][i],9)^aes_base._g_mul(Matrix[2][i],14)^aes_base._g_mul(Matrix[3][i],11) MC[3][i] = aes_base._g_mul(Matrix[0][i],11)^aes_base._g_mul(Matrix[1][i],13)^aes_base._g_mul(Matrix[2][i],9)^aes_base._g_mul(Matrix[3][i],14) return MC def _add_round_key(Matrix,KeyRoundMatrix): return [[Value^KeyRoundMatrix[Idx][Jdx] for Jdx,Value in enumerate(Row)] for Idx,Row in enumerate(Matrix)] def _row_rotate(OldRow,Rotation,Inverse = False): Row = deepcopy(OldRow) if Inverse == False: #to left for i in range(Rotation): Row.append(Row.pop(0)) else: for i in range(Rotation): Row.insert(0,Row.pop()) return Row def _g_mul(Value,Factor): if Factor == 1: return Value elif Factor == 2: if Value < 2**7: return 2*Value else: return (2*Value)^(0x11b) elif Factor == 3: return aes_base._g_mul(Value,2)^Value elif Factor == 9: return aes_base._g_mul(aes_base._g_mul(aes_base._g_mul(Value,2),2),2)^Value elif Factor == 11: return aes_base._g_mul(aes_base._g_mul(aes_base._g_mul(Value,2),2)^Value,2)^Value elif Factor == 13: return aes_base._g_mul(aes_base._g_mul(aes_base._g_mul(Value,2)^Value,2),2)^Value elif Factor == 14: return aes_base._g_mul(aes_base._g_mul(aes_base._g_mul(Value,2)^Value,2)^Value,2) def _get_round_key(KeyRoundMatrix,Round): return [[Col[i] for Col in KeyRoundMatrix[Round*(aes_base.ByteSize//2):(Round+1)*aes_base.ByteSize]] for i in range(aes_base.ByteSize//2)] class aes: MatDim = 4 Byte = 8 ByteSize = 16 BitSize = 128 RoundConst = [0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1B,0x36] def __init__(self,Key,OpMode = 'ECB'): self._OpMode = OpMode self._KeyRoundMatrix = aes._key_expansion(Key) self._InvKeyRoundMat = aes._key_inversion(self._KeyRoundMatrix) self._Format = list def encrypt(self,TextString): ByteMatrix = self._format_input(TextString) EncryptMatrix = aes_base.encrypt(ByteMatrix,self._KeyRoundMatrix) Encryption = self._format_output(EncryptMatrix) return Encryption def encrypt_file(self,FileName): File = open(FileName,'r+b') Block = 'EMPTY' TextLeng = 0 while len(Block) != 0: WritePos = File.tell() Block = File.read(aes.ByteSize) TextLeng += len(Block) if len(Block) != 0: File.seek(WritePos) File.write(self.encrypt(Block)) File.write(self.encrypt(aes._size_to_bytes(TextLeng))) File.close() def encrypt_folder(self,FolderName): Files = [] Folders = [] for Item in os.listdir(FolderName): PathName = path.join(FolderName,Item) if path.isfile(PathName): Files.append(PathName) elif path.isdir(PathName): Folders.append(PathName) for File in Files: self.encrypt_file(File) File = [] for Folder in Folders: self.encrypt_folder(Folder) def decrypt(self,TextString): ByteMatrix = self._format_input(TextString) DecryptMatrix = aes_base.decrypt(ByteMatrix,self._InvKeyRoundMat) Decryption = self._format_output(DecryptMatrix) return Decryption def decrypt_file(self,FileName): File = open(FileName,'r+b') Block = 'EMPTY' while len(Block) != 0: WritePos = File.tell() Block = File.read(aes.ByteSize) if len(Block) != 0: File.seek(WritePos) File.write(self.decrypt(Block)) File.seek(WritePos-aes.ByteSize) TextLeng = aes._bytes_to_size(File.read(aes.ByteSize)) if(TextLeng < (2**64-1)): File.seek(0) File.truncate(TextLeng) File.close() def decrypt_folder(self,FolderName): Files = [] Folders = [] for Item in os.listdir(FolderName): PathName = path.join(FolderName,Item) if path.isfile(PathName): Files.append(PathName) elif path.isdir(PathName): Folders.append(PathName) for File in Files: self.decrypt_file(File) File = [] for Folder in Folders: self.decrypt_folder(Folder) def _key_expansion(Key): KeyMatrix = list(bytes(Key.encode())) LeftBitsSide = 2**aes.Byte-2**(aes.Byte//2) RightBitsSide = 2**aes.Byte-1 Idx = 0 while(len(KeyMatrix) != aes.ByteSize): Row = ((KeyMatrix[Idx]&LeftBitsSide) >> aes.Byte//2)%aes.ByteSize Col = ((KeyMatrix[Idx]&RightBitsSide))%aes.ByteSize KeyMatrix.append(aes_base._SBox[Row][Col]) Idx = (Row+Col+KeyMatrix[-1])%aes.ByteSize KeyRoundMatrix = [KeyMatrix[i*aes.MatDim:(i+1)*aes.MatDim] for i in range(aes.MatDim)] for i in range(aes.MatDim*(aes_base.RoundNumber+1)): if i%(aes.MatDim) != 0: KeyRoundMatrix.append([KeyRoundMatrix[i][Idx]^Value for Idx,Value in enumerate(KeyRoundMatrix[-1])]) else: KeyTrafo = aes._key_col_trafo(KeyRoundMatrix[-1],i) KeyRoundMatrix.append([KeyRoundMatrix[i][Idx]^Value for Idx,Value in enumerate(KeyTrafo)]) return KeyRoundMatrix def _key_col_trafo(Col,Round): return [aes._key_sbox_trafo(Col[3]), aes._key_sbox_trafo(Col[0])^aes.RoundConst[Round//aes.MatDim], aes._key_sbox_trafo(Col[1]), aes._key_sbox_trafo(Col[2])] def _key_sbox_trafo(Value): return aes_base._SBox[((Value^(2**aes.Byte-2**(aes.Byte//2))) >> aes.Byte//2)%aes.ByteSize][((Value^(2**aes.Byte-1)))%aes.ByteSize] def _key_inversion(KeyMatrix): return list(itertools.chain.from_iterable(reversed([KeyMatrix[(i*aes.MatDim):(i+1)*aes.MatDim] for i in range(aes_base.RoundNumber+2)]))) def _8bit_to_byte(Bits): Byte = 0 for Idx,Bit in enumerate(reversed(Bits)): Byte |= (Bit << Idx) return Byte def _size_to_bytes(Size): BitList = list(map(int,list(bin(Size)[2:].zfill(aes.BitSize)))) return bytes([ aes._8bit_to_byte(BitList[i*aes.Byte:(i+1)*aes.Byte]) for i in range(aes.ByteSize)]) def _bytes_to_size(Bytes): return int(''.join([ bin(Block)[2:].zfill(aes.Byte) for Block in list(Bytes)]),2) def _format_input(self,TextString): ByteList = [] ByteMatrix = [] if type(TextString) is str: ByteList = list(TextString.encode()) ByteList.extend([255]*(aes.ByteSize-len(ByteList))) self._Format = bytes elif type(TextString) is bytes: ByteList = list(TextString) ByteList.extend([255]*(aes.ByteSize-len(ByteList))) self._Format = bytes elif type(TextString) is list: ByteList = [int(Sig) for Sig in TextString]+[255]*(aes.ByteSize-len(TextString)) self._Format = list else: raise TypeError('Input must be either String, Bytes or List') if len(ByteList) == aes.ByteSize: ByteMatrix = [ByteList[i*aes.MatDim:(i+1)*aes.MatDim] for i in range(aes.MatDim)] elif len(ByteList) == aes.BitSize: pass #TODO 128-BitList to 16Byte Matrix else: raise ValueError('Input must contain 16 Bytes or 128 Bits') return ByteMatrix def _format_output(self,ByteMatrix): TextString = list(itertools.chain.from_iterable(ByteMatrix)) if self._Format is bytes: TextString = bytes(TextString) return TextString Parser = argparse.ArgumentParser(description='En-/Decryption for Strings,Files and Folders') Parser.add_argument('--file', metavar='', type=str, nargs=1, help='File that should be en-/decrypted.', required = False) Parser.add_argument('--folder', metavar='', type=str, nargs=1, help='Folder with files that should be en-/decrypted; recursive.', required = False) Parser.add_argument('--text', metavar='', type=str, nargs=1, help='File that should be en-/decrypted.', required = False) Parser.add_argument('--key', metavar='', type=str, nargs=1, help='asdf', required = True) Parser.add_argument('--decrypt', help='Enable input file decryption', action='store_true', required = False) Args = Parser.parse_args() KeyWord = Args.key[0] Decryption = Args.decrypt CryptoSystem = aes(KeyWord) if not (Args.file or Args.folder or Args.text): Parser.error('one of the following arguments is requested: --file, --folder, --text') if Decryption == False: if Args.file: CryptoSystem.encrypt_file(Args.file[0]) elif Args.folder: CryptoSystem.encrypt_folder(Args.folder[0]) else: if Args.file: CryptoSystem.decrypt_file(Args.file[0]) elif Args.folder: CryptoSystem.decrypt_folder(Args.folder[0])