|
1 |
| -def encrypt(input_string: str, key: int) -> str: |
2 |
| - result = "" |
3 |
| - for x in input_string: |
4 |
| - if not x.isalpha(): |
5 |
| - result += x |
6 |
| - elif x.isupper(): |
7 |
| - result += chr((ord(x) + key - 65) % 26 + 65) |
8 |
| - elif x.islower(): |
9 |
| - result += chr((ord(x) + key - 97) % 26 + 97) |
10 |
| - return result |
| 1 | +from string import ascii_letters |
| 2 | + |
| 3 | + |
| 4 | +def encrypt(input_string: str, key: int, alphabet=None) -> str: |
| 5 | + """ |
| 6 | + encrypt |
| 7 | + ======= |
| 8 | + Encodes a given string with the caesar cipher and returns the encoded |
| 9 | + message |
| 10 | +
|
| 11 | + Parameters: |
| 12 | + ----------- |
| 13 | + * input_string: the plain-text that needs to be encoded |
| 14 | + * key: the number of letters to shift the message by |
| 15 | +
|
| 16 | + Optional: |
| 17 | + * alphabet (None): the alphabet used to encode the cipher, if not |
| 18 | + specified, the standard english alphabet with upper and lowercase |
| 19 | + letters is used |
| 20 | +
|
| 21 | + Returns: |
| 22 | + * A string containing the encoded cipher-text |
| 23 | +
|
| 24 | + More on the caesar cipher |
| 25 | + ========================= |
| 26 | + The caesar cipher is named after Julius Caesar who used it when sending |
| 27 | + secret military messages to his troops. This is a simple substitution cipher |
| 28 | + where very character in the plain-text is shifted by a certain number known |
| 29 | + as the "key" or "shift". |
| 30 | +
|
| 31 | + Example: |
| 32 | + Say we have the following message: |
| 33 | + "Hello, captain" |
| 34 | +
|
| 35 | + And our alphabet is made up of lower and uppercase letters: |
| 36 | + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 37 | +
|
| 38 | + And our shift is "2" |
| 39 | +
|
| 40 | + We can then encode the message, one letter at a time. "H" would become "J", |
| 41 | + since "J" is two letters away, and so on. If the shift is ever two large, or |
| 42 | + our letter is at the end of the alphabet, we just start at the beginning |
| 43 | + ("Z" would shift to "a" then "b" and so on). |
| 44 | +
|
| 45 | + Our final message would be "Jgnnq, ecrvckp" |
| 46 | +
|
| 47 | + Further reading |
| 48 | + =============== |
| 49 | + * https://en.m.wikipedia.org/wiki/Caesar_cipher |
| 50 | +
|
| 51 | + Doctests |
| 52 | + ======== |
| 53 | + >>> encrypt('The quick brown fox jumps over the lazy dog', 8) |
| 54 | + 'bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo' |
11 | 55 |
|
| 56 | + >>> encrypt('A very large key', 8000) |
| 57 | + 's nWjq dSjYW cWq' |
12 | 58 |
|
13 |
| -def decrypt(input_string: str, key: int) -> str: |
| 59 | + >>> encrypt('a lowercase alphabet', 5, 'abcdefghijklmnopqrstuvwxyz') |
| 60 | + 'f qtbjwhfxj fqumfgjy' |
| 61 | + """ |
| 62 | + # Set default alphabet to lower and upper case english chars |
| 63 | + alpha = alphabet or ascii_letters |
| 64 | + |
| 65 | + # The final result string |
14 | 66 | result = ""
|
15 |
| - for x in input_string: |
16 |
| - if not x.isalpha(): |
17 |
| - result += x |
18 |
| - elif x.isupper(): |
19 |
| - result += chr((ord(x) - key - 65) % 26 + 65) |
20 |
| - elif x.islower(): |
21 |
| - result += chr((ord(x) - key - 97) % 26 + 97) |
| 67 | + |
| 68 | + for character in input_string: |
| 69 | + if character not in alpha: |
| 70 | + # Append without encryption if character is not in the alphabet |
| 71 | + result += character |
| 72 | + else: |
| 73 | + # Get the index of the new key and make sure it isn't too large |
| 74 | + new_key = (alpha.index(character) + key) % len(alpha) |
| 75 | + |
| 76 | + # Append the encoded character to the alphabet |
| 77 | + result += alpha[new_key] |
| 78 | + |
22 | 79 | return result
|
23 | 80 |
|
24 | 81 |
|
25 |
| -def brute_force(input_string: str) -> None: |
| 82 | +def decrypt(input_string: str, key: int, alphabet=None) -> str: |
| 83 | + """ |
| 84 | + decrypt |
| 85 | + ======= |
| 86 | + Decodes a given string of cipher-text and returns the decoded plain-text |
| 87 | +
|
| 88 | + Parameters: |
| 89 | + ----------- |
| 90 | + * input_string: the cipher-text that needs to be decoded |
| 91 | + * key: the number of letters to shift the message backwards by to decode |
| 92 | +
|
| 93 | + Optional: |
| 94 | + * alphabet (None): the alphabet used to decode the cipher, if not |
| 95 | + specified, the standard english alphabet with upper and lowercase |
| 96 | + letters is used |
| 97 | +
|
| 98 | + Returns: |
| 99 | + * A string containing the decoded plain-text |
| 100 | +
|
| 101 | + More on the caesar cipher |
| 102 | + ========================= |
| 103 | + The caesar cipher is named after Julius Caesar who used it when sending |
| 104 | + secret military messages to his troops. This is a simple substitution cipher |
| 105 | + where very character in the plain-text is shifted by a certain number known |
| 106 | + as the "key" or "shift". Please keep in mind, here we will be focused on |
| 107 | + decryption. |
| 108 | +
|
| 109 | + Example: |
| 110 | + Say we have the following cipher-text: |
| 111 | + "Jgnnq, ecrvckp" |
| 112 | +
|
| 113 | + And our alphabet is made up of lower and uppercase letters: |
| 114 | + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| 115 | +
|
| 116 | + And our shift is "2" |
| 117 | +
|
| 118 | + To decode the message, we would do the same thing as encoding, but in |
| 119 | + reverse. The first letter, "J" would become "H" (remember: we are decoding) |
| 120 | + because "H" is two letters in reverse (to the left) of "J". We would |
| 121 | + continue doing this. A letter like "a" would shift back to the end of |
| 122 | + the alphabet, and would become "Z" or "Y" and so on. |
| 123 | +
|
| 124 | + Our final message would be "Hello, captain" |
| 125 | +
|
| 126 | + Further reading |
| 127 | + =============== |
| 128 | + * https://en.m.wikipedia.org/wiki/Caesar_cipher |
| 129 | +
|
| 130 | + Doctests |
| 131 | + ======== |
| 132 | + >>> decrypt('bpm yCqks jzwEv nwF rCuxA wDmz Bpm tiHG lwo', 8) |
| 133 | + 'The quick brown fox jumps over the lazy dog' |
| 134 | +
|
| 135 | + >>> decrypt('s nWjq dSjYW cWq', 8000) |
| 136 | + 'A very large key' |
| 137 | +
|
| 138 | + >>> decrypt('f qtbjwhfxj fqumfgjy', 5, 'abcdefghijklmnopqrstuvwxyz') |
| 139 | + 'a lowercase alphabet' |
| 140 | + """ |
| 141 | + # Turn on decode mode by making the key negative |
| 142 | + key *= -1 |
| 143 | + |
| 144 | + return encrypt(input_string, key, alphabet) |
| 145 | + |
| 146 | + |
| 147 | +def brute_force(input_string: str, alphabet=None) -> dict: |
| 148 | + """ |
| 149 | + brute_force |
| 150 | + =========== |
| 151 | + Returns all the possible combinations of keys and the decoded strings in the |
| 152 | + form of a dictionary |
| 153 | +
|
| 154 | + Parameters: |
| 155 | + ----------- |
| 156 | + * input_string: the cipher-text that needs to be used during brute-force |
| 157 | +
|
| 158 | + Optional: |
| 159 | + * alphabet: (None): the alphabet used to decode the cipher, if not |
| 160 | + specified, the standard english alphabet with upper and lowercase |
| 161 | + letters is used |
| 162 | +
|
| 163 | + More about brute force |
| 164 | + ====================== |
| 165 | + Brute force is when a person intercepts a message or password, not knowing |
| 166 | + the key and tries every single combination. This is easy with the caesar |
| 167 | + cipher since there are only all the letters in the alphabet. The more |
| 168 | + complex the cipher, the larger amount of time it will take to do brute force |
| 169 | +
|
| 170 | + Ex: |
| 171 | + Say we have a 5 letter alphabet (abcde), for simplicity and we intercepted the |
| 172 | + following message: |
| 173 | +
|
| 174 | + "dbc" |
| 175 | +
|
| 176 | + we could then just write out every combination: |
| 177 | + ecd... and so on, until we reach a combination that makes sense: |
| 178 | + "cab" |
| 179 | +
|
| 180 | + Further reading |
| 181 | + =============== |
| 182 | + * https://en.wikipedia.org/wiki/Brute_force |
| 183 | +
|
| 184 | + Doctests |
| 185 | + ======== |
| 186 | + >>> brute_force("jFyuMy xIH'N vLONy zILwy Gy!")[20] |
| 187 | + "Please don't brute force me!" |
| 188 | +
|
| 189 | + >>> brute_force(1) |
| 190 | + Traceback (most recent call last): |
| 191 | + TypeError: 'int' object is not iterable |
| 192 | + """ |
| 193 | + # Set default alphabet to lower and upper case english chars |
| 194 | + alpha = alphabet or ascii_letters |
| 195 | + |
| 196 | + # The key during testing (will increase) |
26 | 197 | key = 1
|
| 198 | + |
| 199 | + # The encoded result |
27 | 200 | result = ""
|
28 |
| - while key <= 94: |
29 |
| - for x in input_string: |
30 |
| - indx = (ord(x) - key) % 256 |
31 |
| - if indx < 32: |
32 |
| - indx = indx + 95 |
33 |
| - result = result + chr(indx) |
34 |
| - print(f"Key: {key}\t| Message: {result}") |
| 201 | + |
| 202 | + # To store data on all the combinations |
| 203 | + brute_force_data = {} |
| 204 | + |
| 205 | + # Cycle through each combination |
| 206 | + while key <= len(alpha): |
| 207 | + # Decrypt the message |
| 208 | + result = decrypt(input_string, key, alpha) |
| 209 | + |
| 210 | + # Update the data |
| 211 | + brute_force_data[key] = result |
| 212 | + |
| 213 | + # Reset result and increase the key |
35 | 214 | result = ""
|
36 | 215 | key += 1
|
37 |
| - return None |
| 216 | + |
| 217 | + return brute_force_data |
38 | 218 |
|
39 | 219 |
|
40 | 220 | def main():
|
41 | 221 | while True:
|
42 |
| - print(f'{"-" * 10}\n Menu\n{"-" * 10}') |
| 222 | + print(f'\n{"-" * 10}\n Menu\n{"-" * 10}') |
43 | 223 | print(*["1.Encrpyt", "2.Decrypt", "3.BruteForce", "4.Quit"], sep="\n")
|
44 |
| - choice = input("What would you like to do?: ") |
45 |
| - if choice not in ["1", "2", "3", "4"]: |
| 224 | + |
| 225 | + # get user input |
| 226 | + choice = input("\nWhat would you like to do?: ").strip() or "4" |
| 227 | + |
| 228 | + # run functions based on what the user chose |
| 229 | + if choice not in ("1", "2", "3", "4"): |
46 | 230 | print("Invalid choice, please enter a valid choice")
|
47 | 231 | elif choice == "1":
|
48 | 232 | input_string = input("Please enter the string to be encrypted: ")
|
49 |
| - key = int(input("Please enter off-set between 0-25: ")) |
50 |
| - if key in range(1, 95): |
51 |
| - print(encrypt(input_string.lower(), key)) |
| 233 | + key = int(input("Please enter off-set: ").strip()) |
| 234 | + |
| 235 | + print(encrypt(input_string, key)) |
52 | 236 | elif choice == "2":
|
53 | 237 | input_string = input("Please enter the string to be decrypted: ")
|
54 |
| - key = int(input("Please enter off-set between 1-94: ")) |
55 |
| - if key in range(1, 95): |
56 |
| - print(decrypt(input_string, key)) |
| 238 | + key = int(input("Please enter off-set: ").strip()) |
| 239 | + |
| 240 | + print(decrypt(input_string, key)) |
57 | 241 | elif choice == "3":
|
58 | 242 | input_string = input("Please enter the string to be decrypted: ")
|
59 |
| - brute_force(input_string) |
60 |
| - main() |
| 243 | + brute_force_data = brute_force(input_string) |
| 244 | + |
| 245 | + for key, value in brute_force_data.items(): |
| 246 | + print(f"Key: {key} | Message: {value}") |
| 247 | + |
61 | 248 | elif choice == "4":
|
62 | 249 | print("Goodbye.")
|
63 | 250 | break
|
|
0 commit comments