Skip to content
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

Cipher Update #15

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 132 additions & 0 deletions libcipher/rsa_cipher.py
Original file line number Diff line number Diff line change
@@ -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()
54 changes: 54 additions & 0 deletions libcipher/rsa_key_generator.py
Original file line number Diff line number Diff line change
@@ -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()
177 changes: 177 additions & 0 deletions libcipher/shuffled_shift_cipher.py
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()
Loading