caesar_cipher.py 7.8 KB
Newer Older
M
Maxim R 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
from string import ascii_letters


def encrypt(input_string: str, key: int, alphabet=None) -> str:
    """
    encrypt
    =======
    Encodes a given string with the caesar cipher and returns the encoded
    message

    Parameters:
    -----------
    *   input_string: the plain-text that needs to be encoded
    *   key: the number of letters to shift the message by

    Optional:
    *   alphabet (None): the alphabet used to encode the cipher, if not
        specified, the standard english alphabet with upper and lowercase
        letters is used

    Returns:
    *   A string containing the encoded cipher-text

    More on the caesar cipher
    =========================
    The caesar cipher is named after Julius Caesar who used it when sending
    secret military messages to his troops. This is a simple substitution cipher
    where very character in the plain-text is shifted by a certain number known
    as the "key" or "shift".

    Example:
    Say we have the following message:
    "Hello, captain"

    And our alphabet is made up of lower and uppercase letters:
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

    And our shift is "2"

    We can then encode the message, one letter at a time. "H" would become "J",
    since "J" is two letters away, and so on. If the shift is ever two large, or
    our letter is at the end of the alphabet, we just start at the beginning
    ("Z" would shift to "a" then "b" and so on).

    Our final message would be "Jgnnq, ecrvckp"

    Further reading
    ===============
    *   https://en.m.wikipedia.org/wiki/Caesar_cipher

    Doctests
    ========
    >>> encrypt('The quick brown fox jumps over the lazy dog', 8)
    'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo'
P
PyDevthon 已提交
55

M
Maxim R 已提交
56 57
    >>> encrypt('A very large key', 8000)
    's nWjq dSjYW cWq'
P
PyDevthon 已提交
58

M
Maxim R 已提交
59 60 61 62 63 64 65
    >>> encrypt('a lowercase alphabet', 5, 'abcdefghijklmnopqrstuvwxyz')
    'f qtbjwhfxj fqumfgjy'
    """
    # Set default alphabet to lower and upper case english chars
    alpha = alphabet or ascii_letters

    # The final result string
66
    result = ""
M
Maxim R 已提交
67 68 69 70 71 72 73 74 75 76 77 78

    for character in input_string:
        if character not in alpha:
            # Append without encryption if character is not in the alphabet
            result += character
        else:
            # Get the index of the new key and make sure it isn't too large
            new_key = (alpha.index(character) + key) % len(alpha)

            # Append the encoded character to the alphabet
            result += alpha[new_key]

Y
Yurii 已提交
79
    return result
P
PyDevthon 已提交
80

W
William Zhang 已提交
81

M
Maxim R 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
def decrypt(input_string: str, key: int, alphabet=None) -> str:
    """
    decrypt
    =======
    Decodes a given string of cipher-text and returns the decoded plain-text

    Parameters:
    -----------
    *   input_string: the cipher-text that needs to be decoded
    *   key: the number of letters to shift the message backwards by to decode

    Optional:
    *   alphabet (None): the alphabet used to decode the cipher, if not
        specified, the standard english alphabet with upper and lowercase
        letters is used

    Returns:
    *   A string containing the decoded plain-text

    More on the caesar cipher
    =========================
    The caesar cipher is named after Julius Caesar who used it when sending
    secret military messages to his troops. This is a simple substitution cipher
    where very character in the plain-text is shifted by a certain number known
    as the "key" or "shift". Please keep in mind, here we will be focused on
    decryption.

    Example:
    Say we have the following cipher-text:
    "Jgnnq, ecrvckp"

    And our alphabet is made up of lower and uppercase letters:
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

    And our shift is "2"

    To decode the message, we would do the same thing as encoding, but in
    reverse. The first letter, "J" would become "H" (remember: we are decoding)
    because "H" is two letters in reverse (to the left) of "J". We would
    continue doing this. A letter like "a" would shift back to the end of
    the alphabet, and would become "Z" or "Y" and so on.

    Our final message would be "Hello, captain"

    Further reading
    ===============
    *   https://en.m.wikipedia.org/wiki/Caesar_cipher

    Doctests
    ========
    >>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8)
    'The quick brown fox jumps over the lazy dog'

    >>> decrypt('s nWjq dSjYW cWq', 8000)
    'A very large key'

    >>> decrypt('f qtbjwhfxj fqumfgjy', 5, 'abcdefghijklmnopqrstuvwxyz')
    'a lowercase alphabet'
    """
    # Turn on decode mode by making the key negative
    key *= -1

    return encrypt(input_string, key, alphabet)


def brute_force(input_string: str, alphabet=None) -> dict:
    """
    brute_force
    ===========
    Returns all the possible combinations of keys and the decoded strings in the
    form of a dictionary

    Parameters:
    -----------
    *   input_string: the cipher-text that needs to be used during brute-force

    Optional:
    *   alphabet:  (None): the alphabet used to decode the cipher, if not
        specified, the standard english alphabet with upper and lowercase
        letters is used

    More about brute force
    ======================
    Brute force is when a person intercepts a message or password, not knowing
    the key and tries every single combination. This is easy with the caesar
    cipher since there are only all the letters in the alphabet. The more
    complex the cipher, the larger amount of time it will take to do brute force

    Ex:
    Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the
    following message:

    "dbc"

    we could then just write out every combination:
    ecd... and so on, until we reach a combination that makes sense:
    "cab"

    Further reading
    ===============
    *   https://en.wikipedia.org/wiki/Brute_force

    Doctests
    ========
    >>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20]
    "Please don't brute force me!"

    >>> brute_force(1)
    Traceback (most recent call last):
    TypeError: 'int' object is not iterable
    """
    # Set default alphabet to lower and upper case english chars
    alpha = alphabet or ascii_letters

    # The key during testing (will increase)
P
PyDevthon 已提交
197
    key = 1
M
Maxim R 已提交
198 199

    # The encoded result
200
    result = ""
M
Maxim R 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213

    # To store data on all the combinations
    brute_force_data = {}

    # Cycle through each combination
    while key <= len(alpha):
        # Decrypt the message
        result = decrypt(input_string, key, alpha)

        # Update the data
        brute_force_data[key] = result

        # Reset result and increase the key
214
        result = ""
P
PyDevthon 已提交
215
        key += 1
M
Maxim R 已提交
216 217

    return brute_force_data
P
PyDevthon 已提交
218

H
Harshil Darji 已提交
219

H
Harshil Darji 已提交
220
def main():
P
PyDevthon 已提交
221
    while True:
M
Maxim R 已提交
222
        print(f'\n{"-" * 10}\n Menu\n{"-" * 10}')
223
        print(*["1.Encrypt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n")
M
Maxim R 已提交
224 225 226 227 228 229

        # get user input
        choice = input("\nWhat would you like to do?: ").strip() or "4"

        # run functions based on what the user chose
        if choice not in ("1", "2", "3", "4"):
230
            print("Invalid choice, please enter a valid choice")
W
William Zhang 已提交
231
        elif choice == "1":
Y
Yurii 已提交
232
            input_string = input("Please enter the string to be encrypted: ")
M
Maxim R 已提交
233 234 235
            key = int(input("Please enter off-set: ").strip())

            print(encrypt(input_string, key))
W
William Zhang 已提交
236
        elif choice == "2":
Y
Yurii 已提交
237
            input_string = input("Please enter the string to be decrypted: ")
M
Maxim R 已提交
238 239 240
            key = int(input("Please enter off-set: ").strip())

            print(decrypt(input_string, key))
W
William Zhang 已提交
241
        elif choice == "3":
Y
Yurii 已提交
242
            input_string = input("Please enter the string to be decrypted: ")
M
Maxim R 已提交
243 244 245 246 247
            brute_force_data = brute_force(input_string)

            for key, value in brute_force_data.items():
                print(f"Key: {key} | Message: {value}")

W
William Zhang 已提交
248
        elif choice == "4":
249
            print("Goodbye.")
H
Harshil 已提交
250
            break
251 252


W
William Zhang 已提交
253
if __name__ == "__main__":
254
    main()