author: Christian Bender date: 21.12.2017 class: XORCipher This class implements the XORcipher algorithm and provides some useful methods for encrypting and decrypting strings and files. Overview about methods  encrypt : list of char  decrypt : list of char  encryptstring : str  decryptstring : str  encryptfile : boolean  decryptfile : boolean simple constructor that receives a key or uses default key  0 private field 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 Empty list  XORCipher.encrypt, 5  One key  XORCipher.encrypthallo welt, 1 'i', '', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u' Normal key  XORCipher.encryptHALLO WELT, 32 'h', 'a', 'l', 'l', 'o', 'x00', 'w', 'e', 'l', 't' Key greater than 255  XORCipher.encrypthallo welt, 256 'h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't' precondition make sure key is an appropriate size 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 Empty list  XORCipher.decrypt, 5  One key  XORCipher.decrypthallo welt, 1 'i', '', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u' Normal key  XORCipher.decryptHALLO WELT, 32 'h', 'a', 'l', 'l', 'o', 'x00', 'w', 'e', 'l', 't' Key greater than 255  XORCipher.decrypthallo welt, 256 'h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't' precondition make sure key is an appropriate size 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 Empty list  XORCipher.encryptstring, 5 '' One key  XORCipher.encryptstringhallo welt, 1 'immn!vdmu' Normal key  XORCipher.encryptstringHALLO WELT, 32 'hallox00welt' Key greater than 255  XORCipher.encryptstringhallo welt, 256 'hallo welt' precondition make sure key is an appropriate size This will be returned 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 Empty list  XORCipher.decryptstring, 5 '' One key  XORCipher.decryptstringhallo welt, 1 'immn!vdmu' Normal key  XORCipher.decryptstringHALLO WELT, 32 'hallox00welt' Key greater than 255  XORCipher.decryptstringhallo welt, 256 'hallo welt' precondition make sure key is an appropriate size This will be returned 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 make sure key is an appropriate size actual encryptprocess 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 make sure key is an appropriate size actual encryptprocess Tests crypt  XORCipher key  67  test encrypt printcrypt.encrypthallo welt,key  test decrypt printcrypt.decryptcrypt.encrypthallo welt,key, key  test encryptstring printcrypt.encryptstringhallo welt,key  test decryptstring printcrypt.decryptstringcrypt.encryptstringhallo welt,key,key if crypt.encryptfiletest.txt,key: printencrypt successful else: printencrypt unsuccessful if crypt.decryptfileencrypt.out,key: printdecrypt successful else: printdecrypt unsuccessful
from __future__ import annotations


class XORCipher:
    def __init__(self, key: int = 0):
        """
        simple constructor that receives a key or uses
        default key = 0
        """

        # private field
        self.__key = key

    def encrypt(self, content: str, key: int) -> list[str]:
        """
        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

        Empty list
        >>> XORCipher().encrypt("", 5)
        []

        One key
        >>> XORCipher().encrypt("hallo welt", 1)
        ['i', '`', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u']

        Normal key
        >>> XORCipher().encrypt("HALLO WELT", 32)
        ['h', 'a', 'l', 'l', 'o', '\\x00', 'w', 'e', 'l', 't']

        Key greater than 255
        >>> XORCipher().encrypt("hallo welt", 256)
        ['h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't']
        """

        # precondition
        assert isinstance(key, int)
        assert isinstance(content, str)

        key = key or self.__key or 1

        # make sure key is an appropriate size
        key %= 256

        return [chr(ord(ch) ^ key) for ch in content]

    def decrypt(self, content: str, key: int) -> list[str]:
        """
        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

        Empty list
        >>> XORCipher().decrypt("", 5)
        []

        One key
        >>> XORCipher().decrypt("hallo welt", 1)
        ['i', '`', 'm', 'm', 'n', '!', 'v', 'd', 'm', 'u']

        Normal key
        >>> XORCipher().decrypt("HALLO WELT", 32)
        ['h', 'a', 'l', 'l', 'o', '\\x00', 'w', 'e', 'l', 't']

        Key greater than 255
        >>> XORCipher().decrypt("hallo welt", 256)
        ['h', 'a', 'l', 'l', 'o', ' ', 'w', 'e', 'l', 't']
        """

        # precondition
        assert isinstance(key, int)
        assert isinstance(content, str)

        key = key or self.__key or 1

        # make sure key is an appropriate size
        key %= 256

        return [chr(ord(ch) ^ key) for ch in content]

    def encrypt_string(self, content: str, key: int = 0) -> str:
        """
        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

        Empty list
        >>> XORCipher().encrypt_string("", 5)
        ''

        One key
        >>> XORCipher().encrypt_string("hallo welt", 1)
        'i`mmn!vdmu'

        Normal key
        >>> XORCipher().encrypt_string("HALLO WELT", 32)
        'hallo\\x00welt'

        Key greater than 255
        >>> XORCipher().encrypt_string("hallo welt", 256)
        'hallo welt'
        """

        # precondition
        assert isinstance(key, int)
        assert isinstance(content, str)

        key = key or self.__key or 1

        # make sure key is an appropriate size
        key %= 256

        # This will be returned
        ans = ""

        for ch in content:
            ans += chr(ord(ch) ^ key)

        return ans

    def decrypt_string(self, content: str, key: int = 0) -> str:
        """
        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

        Empty list
        >>> XORCipher().decrypt_string("", 5)
        ''

        One key
        >>> XORCipher().decrypt_string("hallo welt", 1)
        'i`mmn!vdmu'

        Normal key
        >>> XORCipher().decrypt_string("HALLO WELT", 32)
        'hallo\\x00welt'

        Key greater than 255
        >>> XORCipher().decrypt_string("hallo welt", 256)
        'hallo welt'
        """

        # precondition
        assert isinstance(key, int)
        assert isinstance(content, str)

        key = key or self.__key or 1

        # make sure key is an appropriate size
        key %= 256

        # This will be returned
        ans = ""

        for ch in content:
            ans += chr(ord(ch) ^ key)

        return ans

    def encrypt_file(self, file: str, key: int = 0) -> bool:
        """
        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)
        assert isinstance(key, int)

        # make sure key is an appropriate size
        key %= 256

        try:
            with open(file) as fin, open("encrypt.out", "w+") as fout:
                # actual encrypt-process
                for line in fin:
                    fout.write(self.encrypt_string(line, key))

        except OSError:
            return False

        return True

    def decrypt_file(self, file: str, key: int) -> bool:
        """
        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)
        assert isinstance(key, int)

        # make sure key is an appropriate size
        key %= 256

        try:
            with open(file) as fin, open("decrypt.out", "w+") as fout:
                # actual encrypt-process
                for line in fin:
                    fout.write(self.decrypt_string(line, key))

        except OSError:
            return False

        return True


if __name__ == "__main__":
    from doctest import testmod

    testmod()

# Tests
# crypt = XORCipher()
# key = 67

# # test encrypt
# 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")
