-
-
Notifications
You must be signed in to change notification settings - Fork 46.9k
introduced shuffled_shift_cipher.py in /ciphers #1424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
cclauss
merged 5 commits into
TheAlgorithms:master
from
chttrjeankr:shuffled_shift_cipher
Oct 23, 2019
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
17f5771
introduced shuffled_shift_cipher.py in /ciphers
chttrjeankr 70e667d
made requested changes
chttrjeankr 95fe810
introduced doctests, type hints
chttrjeankr 994aad7
test_end_to_end() inserted
chttrjeankr 70f1062
Make test_end_to_end() a test ;-)
cclauss File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.