diff --git a/libcipher/rsa_cipher.py b/libcipher/rsa_cipher.py new file mode 100644 index 0000000..a9b2dcc --- /dev/null +++ b/libcipher/rsa_cipher.py @@ -0,0 +1,132 @@ +import sys, rsa_key_generator as rkg, os + +DEFAULT_BLOCK_SIZE = 128 +BYTE_SIZE = 256 + + +def main(): + filename = "encrypted_file.txt" + response = input(r"Encrypte\Decrypt [e\d]: ") + + if response.lower().startswith("e"): + mode = "encrypt" + elif response.lower().startswith("d"): + mode = "decrypt" + + if mode == "encrypt": + if not os.path.exists("rsa_pubkey.txt"): + rkg.makeKeyFiles("rsa", 1024) + + message = input("\nEnter message: ") + pubKeyFilename = "rsa_pubkey.txt" + print("Encrypting and writing to %s..." % (filename)) + encryptedText = encryptAndWriteToFile(filename, pubKeyFilename, message) + + print("\nEncrypted text:") + print(encryptedText) + + elif mode == "decrypt": + privKeyFilename = "rsa_privkey.txt" + print("Reading from %s and decrypting..." % (filename)) + decryptedText = readFromFileAndDecrypt(filename, privKeyFilename) + print("writing decryption to rsa_decryption.txt...") + with open("rsa_decryption.txt", "w") as dec: + dec.write(decryptedText) + + print("\nDecryption:") + print(decryptedText) + + +def getBlocksFromText(message, blockSize=DEFAULT_BLOCK_SIZE): + messageBytes = message.encode("ascii") + + blockInts = [] + for blockStart in range(0, len(messageBytes), blockSize): + blockInt = 0 + for i in range(blockStart, min(blockStart + blockSize, len(messageBytes))): + blockInt += messageBytes[i] * (BYTE_SIZE ** (i % blockSize)) + blockInts.append(blockInt) + return blockInts + + +def getTextFromBlocks(blockInts, messageLength, blockSize=DEFAULT_BLOCK_SIZE): + message = [] + for blockInt in blockInts: + blockMessage = [] + for i in range(blockSize - 1, -1, -1): + if len(message) + i < messageLength: + asciiNumber = blockInt // (BYTE_SIZE ** i) + blockInt = blockInt % (BYTE_SIZE ** i) + blockMessage.insert(0, chr(asciiNumber)) + message.extend(blockMessage) + return "".join(message) + + +def encryptMessage(message, key, blockSize=DEFAULT_BLOCK_SIZE): + encryptedBlocks = [] + n, e = key + + for block in getBlocksFromText(message, blockSize): + encryptedBlocks.append(pow(block, e, n)) + return encryptedBlocks + + +def decryptMessage(encryptedBlocks, messageLength, key, blockSize=DEFAULT_BLOCK_SIZE): + decryptedBlocks = [] + n, d = key + for block in encryptedBlocks: + decryptedBlocks.append(pow(block, d, n)) + return getTextFromBlocks(decryptedBlocks, messageLength, blockSize) + + +def readKeyFile(keyFilename): + with open(keyFilename) as fo: + content = fo.read() + keySize, n, EorD = content.split(",") + return (int(keySize), int(n), int(EorD)) + + +def encryptAndWriteToFile( + messageFilename, keyFilename, message, blockSize=DEFAULT_BLOCK_SIZE +): + keySize, n, e = readKeyFile(keyFilename) + if keySize < blockSize * 8: + sys.exit( + "ERROR: Block size is %s bits and key size is %s bits. The RSA cipher requires the block size to be equal to or greater than the key size. Either decrease the block size or use different keys." + % (blockSize * 8, keySize) + ) + + encryptedBlocks = encryptMessage(message, (n, e), blockSize) + + for i in range(len(encryptedBlocks)): + encryptedBlocks[i] = str(encryptedBlocks[i]) + encryptedContent = ",".join(encryptedBlocks) + encryptedContent = "%s_%s_%s" % (len(message), blockSize, encryptedContent) + with open(messageFilename, "w") as fo: + fo.write(encryptedContent) + return encryptedContent + + +def readFromFileAndDecrypt(messageFilename, keyFilename): + keySize, n, d = readKeyFile(keyFilename) + with open(messageFilename) as fo: + content = fo.read() + messageLength, blockSize, encryptedMessage = content.split("_") + messageLength = int(messageLength) + blockSize = int(blockSize) + + if keySize < blockSize * 8: + sys.exit( + "ERROR: Block size is %s bits and key size is %s bits. The RSA cipher requires the block size to be equal to or greater than the key size. Did you specify the correct key file and encrypted file?" + % (blockSize * 8, keySize) + ) + + encryptedBlocks = [] + for block in encryptedMessage.split(","): + encryptedBlocks.append(int(block)) + + return decryptMessage(encryptedBlocks, messageLength, (n, d), blockSize) + + +if __name__ == "__main__": + main() diff --git a/libcipher/rsa_key_generator.py b/libcipher/rsa_key_generator.py new file mode 100644 index 0000000..ce7c1f3 --- /dev/null +++ b/libcipher/rsa_key_generator.py @@ -0,0 +1,54 @@ +import random, sys, os +import rabin_miller as rabinMiller, cryptomath_module as cryptoMath + + +def main(): + print("Making key files...") + makeKeyFiles("rsa", 1024) + print("Key files generation successful.") + + +def generateKey(keySize): + print("Generating prime p...") + p = rabinMiller.generateLargePrime(keySize) + print("Generating prime q...") + q = rabinMiller.generateLargePrime(keySize) + n = p * q + + print("Generating e that is relatively prime to (p - 1) * (q - 1)...") + while True: + e = random.randrange(2 ** (keySize - 1), 2 ** (keySize)) + if cryptoMath.gcd(e, (p - 1) * (q - 1)) == 1: + break + + print("Calculating d that is mod inverse of e...") + d = cryptoMath.findModInverse(e, (p - 1) * (q - 1)) + + publicKey = (n, e) + privateKey = (n, d) + return (publicKey, privateKey) + + +def makeKeyFiles(name, keySize): + if os.path.exists("%s_pubkey.txt" % (name)) or os.path.exists( + "%s_privkey.txt" % (name) + ): + print("\nWARNING:") + print( + '"%s_pubkey.txt" or "%s_privkey.txt" already exists. \nUse a different name or delete these files and re-run this program.' + % (name, name) + ) + sys.exit() + + publicKey, privateKey = generateKey(keySize) + print("\nWriting public key to file %s_pubkey.txt..." % name) + with open("%s_pubkey.txt" % name, "w") as fo: + fo.write("%s,%s,%s" % (keySize, publicKey[0], publicKey[1])) + + print("Writing private key to file %s_privkey.txt..." % name) + with open("%s_privkey.txt" % name, "w") as fo: + fo.write("%s,%s,%s" % (keySize, privateKey[0], privateKey[1])) + + +if __name__ == "__main__": + main() diff --git a/libcipher/shuffled_shift_cipher.py b/libcipher/shuffled_shift_cipher.py new file mode 100644 index 0000000..bbefe33 --- /dev/null +++ b/libcipher/shuffled_shift_cipher.py @@ -0,0 +1,177 @@ +import random +import string + + +class ShuffledShiftCipher(object): + """ + This algorithm uses the Caesar Cipher algorithm but removes the option to + use brute force to decrypt the message. + + The passcode is a a random password from the selection buffer of + 1. uppercase letters of the English alphabet + 2. lowercase letters of the English alphabet + 3. digits from 0 to 9 + + Using unique characters from the passcode, the normal list of characters, + that can be allowed in the plaintext, is pivoted and shuffled. Refer to docstring + of __make_key_list() to learn more about the shuffling. + + Then, using the passcode, a number is calculated which is used to encrypt the + plaintext message with the normal shift cipher method, only in this case, the + reference, to look back at while decrypting, is shuffled. + + Each cipher object can possess an optional argument as passcode, without which a + new passcode is generated for that object automatically. + cip1 = ShuffledShiftCipher('d4usr9TWxw9wMD') + cip2 = ShuffledShiftCipher() + """ + + def __init__(self, passcode: str = None): + """ + Initializes a cipher object with a passcode as it's entity + Note: No new passcode is generated if user provides a passcode + while creating the object + """ + self.__passcode = passcode or self.__passcode_creator() + self.__key_list = self.__make_key_list() + self.__shift_key = self.__make_shift_key() + + def __str__(self): + """ + :return: passcode of the cipher object + """ + return "Passcode is: " + "".join(self.__passcode) + + def __neg_pos(self, iterlist: list) -> list: + """ + Mutates the list by changing the sign of each alternate element + + :param iterlist: takes a list iterable + :return: the mutated list + + """ + for i in range(1, len(iterlist), 2): + iterlist[i] *= -1 + return iterlist + + def __passcode_creator(self) -> list: + """ + Creates a random password from the selection buffer of + 1. uppercase letters of the English alphabet + 2. lowercase letters of the English alphabet + 3. digits from 0 to 9 + + :rtype: list + :return: a password of a random length between 10 to 20 + """ + choices = string.ascii_letters + string.digits + password = [random.choice(choices) for i in range(random.randint(10, 20))] + return password + + def __make_key_list(self) -> list: + """ + Shuffles the ordered character choices by pivoting at breakpoints + Breakpoints are the set of characters in the passcode + + eg: + if, ABCDEFGHIJKLMNOPQRSTUVWXYZ are the possible characters + and CAMERA is the passcode + then, breakpoints = [A,C,E,M,R] # sorted set of characters from passcode + shuffled parts: [A,CB,ED,MLKJIHGF,RQPON,ZYXWVUTS] + shuffled __key_list : ACBEDMLKJIHGFRQPONZYXWVUTS + + Shuffling only 26 letters of the english alphabet can generate 26! + combinations for the shuffled list. In the program we consider, a set of + 97 characters (including letters, digits, punctuation and whitespaces), + thereby creating a possibility of 97! combinations (which is a 152 digit number in itself), + thus diminishing the possibility of a brute force approach. Moreover, + shift keys even introduce a multiple of 26 for a brute force approach + for each of the already 97! combinations. + """ + # key_list_options contain nearly all printable except few elements from string.whitespace + key_list_options = ( + string.ascii_letters + string.digits + string.punctuation + " \t\n" + ) + + keys_l = [] + + # creates points known as breakpoints to break the key_list_options at those points and pivot each substring + breakpoints = sorted(set(self.__passcode)) + temp_list = [] + + # algorithm for creating a new shuffled list, keys_l, out of key_list_options + for i in key_list_options: + temp_list.extend(i) + + # checking breakpoints at which to pivot temporary sublist and add it into keys_l + if i in breakpoints or i == key_list_options[-1]: + keys_l.extend(temp_list[::-1]) + temp_list = [] + + # returning a shuffled keys_l to prevent brute force guessing of shift key + return keys_l + + def __make_shift_key(self) -> int: + """ + sum() of the mutated list of ascii values of all characters where the + mutated list is the one returned by __neg_pos() + """ + num = sum(self.__neg_pos([ord(x) for x in self.__passcode])) + return num if num > 0 else len(self.__passcode) + + def decrypt(self, encoded_message: str) -> str: + """ + Performs shifting of the encoded_message w.r.t. the shuffled __key_list + to create the decoded_message + + >>> ssc = ShuffledShiftCipher('4PYIXyqeQZr44') + >>> ssc.decrypt("d>**-1z6&'5z'5z:z+-='$'>=zp:>5:#z<'.&>#") + 'Hello, this is a modified Caesar cipher' + + """ + decoded_message = "" + + # decoding shift like Caesar cipher algorithm implementing negative shift or reverse shift or left shift + for i in encoded_message: + position = self.__key_list.index(i) + decoded_message += self.__key_list[ + (position - self.__shift_key) % -len(self.__key_list) + ] + + return decoded_message + + def encrypt(self, plaintext: str) -> str: + """ + Performs shifting of the plaintext w.r.t. the shuffled __key_list + to create the encoded_message + + >>> ssc = ShuffledShiftCipher('4PYIXyqeQZr44') + >>> ssc.encrypt('Hello, this is a modified Caesar cipher') + "d>**-1z6&'5z'5z:z+-='$'>=zp:>5:#z<'.&>#" + + """ + encoded_message = "" + + # encoding shift like Caesar cipher algorithm implementing positive shift or forward shift or right shift + for i in plaintext: + position = self.__key_list.index(i) + encoded_message += self.__key_list[ + (position + self.__shift_key) % len(self.__key_list) + ] + + return encoded_message + + +def test_end_to_end(msg: str = "Hello, this is a modified Caesar cipher"): + """ + >>> test_end_to_end() + 'Hello, this is a modified Caesar cipher' + """ + cip1 = ShuffledShiftCipher() + return cip1.decrypt(cip1.encrypt(msg)) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() diff --git a/libcipher/simple_substitution_cipher.py b/libcipher/simple_substitution_cipher.py new file mode 100644 index 0000000..12511cc --- /dev/null +++ b/libcipher/simple_substitution_cipher.py @@ -0,0 +1,77 @@ +import sys, random + +LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + +def main(): + message = input("Enter message: ") + key = "LFWOAYUISVKMNXPBDCRJTQEGHZ" + resp = input("Encrypt/Decrypt [e/d]: ") + + checkValidKey(key) + + if resp.lower().startswith("e"): + mode = "encrypt" + translated = encryptMessage(key, message) + elif resp.lower().startswith("d"): + mode = "decrypt" + translated = decryptMessage(key, message) + + print("\n%sion: \n%s" % (mode.title(), translated)) + + +def checkValidKey(key): + keyList = list(key) + lettersList = list(LETTERS) + keyList.sort() + lettersList.sort() + + if keyList != lettersList: + sys.exit("Error in the key or symbol set.") + + +def encryptMessage(key, message): + """ + >>> encryptMessage('LFWOAYUISVKMNXPBDCRJTQEGHZ', 'Harshil Darji') + 'Ilcrism Olcvs' + """ + return translateMessage(key, message, "encrypt") + + +def decryptMessage(key, message): + """ + >>> decryptMessage('LFWOAYUISVKMNXPBDCRJTQEGHZ', 'Ilcrism Olcvs') + 'Harshil Darji' + """ + return translateMessage(key, message, "decrypt") + + +def translateMessage(key, message, mode): + translated = "" + charsA = LETTERS + charsB = key + + if mode == "decrypt": + charsA, charsB = charsB, charsA + + for symbol in message: + if symbol.upper() in charsA: + symIndex = charsA.find(symbol.upper()) + if symbol.isupper(): + translated += charsB[symIndex].upper() + else: + translated += charsB[symIndex].lower() + else: + translated += symbol + + return translated + + +def getRandomKey(): + key = list(LETTERS) + random.shuffle(key) + return "".join(key) + + +if __name__ == "__main__": + main() diff --git a/libcipher/trafid_cipher.py b/libcipher/trafid_cipher.py new file mode 100644 index 0000000..0add9ee --- /dev/null +++ b/libcipher/trafid_cipher.py @@ -0,0 +1,120 @@ +# https://en.wikipedia.org/wiki/Trifid_cipher + + +def __encryptPart(messagePart, character2Number): + one, two, three = "", "", "" + tmp = [] + + for character in messagePart: + tmp.append(character2Number[character]) + + for each in tmp: + one += each[0] + two += each[1] + three += each[2] + + return one + two + three + + +def __decryptPart(messagePart, character2Number): + tmp, thisPart = "", "" + result = [] + + for character in messagePart: + thisPart += character2Number[character] + + for digit in thisPart: + tmp += digit + if len(tmp) == len(messagePart): + result.append(tmp) + tmp = "" + + return result[0], result[1], result[2] + + +def __prepare(message, alphabet): + # Validate message and alphabet, set to upper and remove spaces + alphabet = alphabet.replace(" ", "").upper() + message = message.replace(" ", "").upper() + + # Check length and characters + if len(alphabet) != 27: + raise KeyError("Length of alphabet has to be 27.") + for each in message: + if each not in alphabet: + raise ValueError("Each message character has to be included in alphabet!") + + # Generate dictionares + numbers = ( + "111", + "112", + "113", + "121", + "122", + "123", + "131", + "132", + "133", + "211", + "212", + "213", + "221", + "222", + "223", + "231", + "232", + "233", + "311", + "312", + "313", + "321", + "322", + "323", + "331", + "332", + "333", + ) + character2Number = {} + number2Character = {} + for letter, number in zip(alphabet, numbers): + character2Number[letter] = number + number2Character[number] = letter + + return message, alphabet, character2Number, number2Character + + +def encryptMessage(message, alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period=5): + message, alphabet, character2Number, number2Character = __prepare(message, alphabet) + encrypted, encrypted_numeric = "", "" + + for i in range(0, len(message) + 1, period): + encrypted_numeric += __encryptPart(message[i : i + period], character2Number) + + for i in range(0, len(encrypted_numeric), 3): + encrypted += number2Character[encrypted_numeric[i : i + 3]] + + return encrypted + + +def decryptMessage(message, alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ.", period=5): + message, alphabet, character2Number, number2Character = __prepare(message, alphabet) + decrypted_numeric = [] + decrypted = "" + + for i in range(0, len(message) + 1, period): + a, b, c = __decryptPart(message[i : i + period], character2Number) + + for j in range(0, len(a)): + decrypted_numeric.append(a[j] + b[j] + c[j]) + + for each in decrypted_numeric: + decrypted += number2Character[each] + + return decrypted + + +if __name__ == "__main__": + msg = "DEFEND THE EAST WALL OF THE CASTLE." + encrypted = encryptMessage(msg, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ") + decrypted = decryptMessage(encrypted, "EPSDUCVWYM.ZLKXNBTFGORIJHAQ") + print("Encrypted: {}\nDecrypted: {}".format(encrypted, decrypted)) diff --git a/libcipher/transposition_cipher.py b/libcipher/transposition_cipher.py new file mode 100644 index 0000000..b6c9195 --- /dev/null +++ b/libcipher/transposition_cipher.py @@ -0,0 +1,63 @@ +import math + + +def main(): + message = input("Enter message: ") + key = int(input("Enter key [2-%s]: " % (len(message) - 1))) + mode = input("Encryption/Decryption [e/d]: ") + + if mode.lower().startswith("e"): + text = encryptMessage(key, message) + elif mode.lower().startswith("d"): + text = decryptMessage(key, message) + + # Append pipe symbol (vertical bar) to identify spaces at the end. + print("Output:\n%s" % (text + "|")) + + +def encryptMessage(key, message): + """ + >>> encryptMessage(6, 'Harshil Darji') + 'Hlia rDsahrij' + """ + cipherText = [""] * key + for col in range(key): + pointer = col + while pointer < len(message): + cipherText[col] += message[pointer] + pointer += key + return "".join(cipherText) + + +def decryptMessage(key, message): + """ + >>> decryptMessage(6, 'Hlia rDsahrij') + 'Harshil Darji' + """ + numCols = math.ceil(len(message) / key) + numRows = key + numShadedBoxes = (numCols * numRows) - len(message) + plainText = [""] * numCols + col = 0 + row = 0 + + for symbol in message: + plainText[col] += symbol + col += 1 + + if ( + (col == numCols) + or (col == numCols - 1) + and (row >= numRows - numShadedBoxes) + ): + col = 0 + row += 1 + + return "".join(plainText) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + main() diff --git a/libcipher/transposition_cipher_encrypt_decrypt_file.py b/libcipher/transposition_cipher_encrypt_decrypt_file.py new file mode 100644 index 0000000..775df35 --- /dev/null +++ b/libcipher/transposition_cipher_encrypt_decrypt_file.py @@ -0,0 +1,38 @@ +import time, os, sys +import transposition_cipher as transCipher + + +def main(): + inputFile = "Prehistoric Men.txt" + outputFile = "Output.txt" + key = int(input("Enter key: ")) + mode = input("Encrypt/Decrypt [e/d]: ") + + if not os.path.exists(inputFile): + print("File %s does not exist. Quitting..." % inputFile) + sys.exit() + if os.path.exists(outputFile): + print("Overwrite %s? [y/n]" % outputFile) + response = input("> ") + if not response.lower().startswith("y"): + sys.exit() + + startTime = time.time() + if mode.lower().startswith("e"): + with open(inputFile) as f: + content = f.read() + translated = transCipher.encryptMessage(key, content) + elif mode.lower().startswith("d"): + with open(outputFile) as f: + content = f.read() + translated = transCipher.decryptMessage(key, content) + + with open(outputFile, "w") as outputObj: + outputObj.write(translated) + + totalTime = round(time.time() - startTime, 2) + print(("Done (", totalTime, "seconds )")) + + +if __name__ == "__main__": + main() diff --git a/libcipher/vigenere_cipher.py b/libcipher/vigenere_cipher.py new file mode 100644 index 0000000..6c10e7d --- /dev/null +++ b/libcipher/vigenere_cipher.py @@ -0,0 +1,65 @@ +LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + +def main(): + message = input("Enter message: ") + key = input("Enter key [alphanumeric]: ") + mode = input("Encrypt/Decrypt [e/d]: ") + + if mode.lower().startswith("e"): + mode = "encrypt" + translated = encryptMessage(key, message) + elif mode.lower().startswith("d"): + mode = "decrypt" + translated = decryptMessage(key, message) + + print("\n%sed message:" % mode.title()) + print(translated) + + +def encryptMessage(key, message): + """ + >>> encryptMessage('HDarji', 'This is Harshil Darji from Dharmaj.') + 'Akij ra Odrjqqs Gaisq muod Mphumrs.' + """ + return translateMessage(key, message, "encrypt") + + +def decryptMessage(key, message): + """ + >>> decryptMessage('HDarji', 'Akij ra Odrjqqs Gaisq muod Mphumrs.') + 'This is Harshil Darji from Dharmaj.' + """ + return translateMessage(key, message, "decrypt") + + +def translateMessage(key, message, mode): + translated = [] + keyIndex = 0 + key = key.upper() + + for symbol in message: + num = LETTERS.find(symbol.upper()) + if num != -1: + if mode == "encrypt": + num += LETTERS.find(key[keyIndex]) + elif mode == "decrypt": + num -= LETTERS.find(key[keyIndex]) + + num %= len(LETTERS) + + if symbol.isupper(): + translated.append(LETTERS[num]) + elif symbol.islower(): + translated.append(LETTERS[num].lower()) + + keyIndex += 1 + if keyIndex == len(key): + keyIndex = 0 + else: + translated.append(symbol) + return "".join(translated) + + +if __name__ == "__main__": + main() diff --git a/libcipher/xor_cipher.py b/libcipher/xor_cipher.py new file mode 100644 index 0000000..7d8dbe4 --- /dev/null +++ b/libcipher/xor_cipher.py @@ -0,0 +1,205 @@ +""" + author: Christian Bender + date: 21.12.2017 + class: XORCipher + + This class implements the XOR-cipher algorithm and provides + some useful methods for encrypting and decrypting strings and + files. + + Overview about methods + + - encrypt : list of char + - decrypt : list of char + - encrypt_string : str + - decrypt_string : str + - encrypt_file : boolean + - decrypt_file : boolean +""" + + +class XORCipher(object): + def __init__(self, key=0): + """ + simple constructor that receives a key or uses + default key = 0 + """ + + # private field + self.__key = key + + def encrypt(self, content, key): + """ + input: 'content' of type string and 'key' of type int + output: encrypted string 'content' as a list of chars + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ + + # precondition + assert isinstance(key, int) and isinstance(content, str) + + key = key or self.__key or 1 + + # make sure key can be any size + while key > 255: + key -= 255 + + # This will be returned + ans = [] + + for ch in content: + ans.append(chr(ord(ch) ^ key)) + + return ans + + def decrypt(self, content, key): + """ + input: 'content' of type list and 'key' of type int + output: decrypted string 'content' as a list of chars + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ + + # precondition + assert isinstance(key, int) and isinstance(content, list) + + key = key or self.__key or 1 + + # make sure key can be any size + while key > 255: + key -= 255 + + # This will be returned + ans = [] + + for ch in content: + ans.append(chr(ord(ch) ^ key)) + + return ans + + def encrypt_string(self, content, key=0): + """ + input: 'content' of type string and 'key' of type int + output: encrypted string 'content' + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ + + # precondition + assert isinstance(key, int) and isinstance(content, str) + + key = key or self.__key or 1 + + # make sure key can be any size + while key > 255: + key -= 255 + + # This will be returned + ans = "" + + for ch in content: + ans += chr(ord(ch) ^ key) + + return ans + + def decrypt_string(self, content, key=0): + """ + input: 'content' of type string and 'key' of type int + output: decrypted string 'content' + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ + + # precondition + assert isinstance(key, int) and isinstance(content, str) + + key = key or self.__key or 1 + + # make sure key can be any size + while key > 255: + key -= 255 + + # This will be returned + ans = "" + + for ch in content: + ans += chr(ord(ch) ^ key) + + return ans + + def encrypt_file(self, file, key=0): + """ + input: filename (str) and a key (int) + output: returns true if encrypt process was + successful otherwise false + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ + + # precondition + assert isinstance(file, str) and isinstance(key, int) + + try: + with open(file, "r") as fin: + with open("encrypt.out", "w+") as fout: + + # actual encrypt-process + for line in fin: + fout.write(self.encrypt_string(line, key)) + + except: + return False + + return True + + def decrypt_file(self, file, key): + """ + input: filename (str) and a key (int) + output: returns true if decrypt process was + successful otherwise false + if key not passed the method uses the key by the constructor. + otherwise key = 1 + """ + + # precondition + assert isinstance(file, str) and isinstance(key, int) + + try: + with open(file, "r") as fin: + with open("decrypt.out", "w+") as fout: + + # actual encrypt-process + for line in fin: + fout.write(self.decrypt_string(line, key)) + + except: + return False + + return True + + +# Tests +# crypt = XORCipher() +# key = 67 + +# # test enrcypt +# print(crypt.encrypt("hallo welt",key)) +# # test decrypt +# print(crypt.decrypt(crypt.encrypt("hallo welt",key), key)) + +# # test encrypt_string +# print(crypt.encrypt_string("hallo welt",key)) + +# # test decrypt_string +# print(crypt.decrypt_string(crypt.encrypt_string("hallo welt",key),key)) + +# if (crypt.encrypt_file("test.txt",key)): +# print("encrypt successful") +# else: +# print("encrypt unsuccessful") + +# if (crypt.decrypt_file("encrypt.out",key)): +# print("decrypt successful") +# else: +# print("decrypt unsuccessful")