#!/usr/bin/python3
# RSA skeleton code from the https://aaronbloomfield.github.io/ics/hws/hw-rsa.html assignment
import sys, random, math, hashlib
# An RSA key, either public (if d is None), private (if e is None), or both
# (if neither are None)
class rsakey:
l = None # the bit length
e = None # the public key (or None if it's just a private key)
d = None # the private key (or None if it's just a public key)
n = None # the modulus (n=p*q)
def __init__(self,_l,_e,_d,_n):
(self.l,self.e,self.d,self.n) = (_l,_e,_d,_n)
# Ciphertext class
class ciphertext:
c = [] # the list of the encrypted blocks
l = None # the length of the original plaintext file or string in bytes
b = None # the length of each block in bytes
def __init__(self,_c,_l,_b):
(self.c,self.l,self.b) = (_c,_l,_b)
# Whether to print verbose messages. This is useful for debugging, as it will
# never be set to true (via the -verbose flag) during grading.
verbose = False
# Whether the values of p and q are displayed during key generation. This is
# useful for debugging, and it *is* something that we will test during grading.
showPandQ = False
# This will write an RSA key to a file (or files) in the standard format used
# for this assignment. If the key contains both d and e, then two files
# (one private, one public) are written. Given the file basename of
# <filebasename>, the key files are <filebasename>-public.key and
# <filebasename>-private.key.
def writeKeyToFile(key, filebasename):
if key.e is not None: # it's a public key
with open(filebasename + "-public.key","w") as f:
print("public\n" + str(key.l) + "\n" + str(key.e) + "\n" + str(key.n) + "\n",file=f)
if key.d is not None: # it's a private key
with open(filebasename + "-private.key","w") as f:
print("private\n" + str(key.l) + "\n" + str(key.d) + "\n" + str(key.n) + "\n",file=f)
# This will read an RSA key from a file in the standard format used for this
# assignment
def readKeyFromFile(filename):
with open(filename) as f:
if f.readline().strip() == "public":
return rsakey(int(f.readline().strip()),int(f.readline().strip()),None,int(f.readline().strip()))
else:
return rsakey(int(f.readline().strip()),None,int(f.readline().strip()),int(f.readline().strip()))
# This will read cipher text from a file in the standard format used for this
# assignment
def readCipherTextFromFile(inputFileName):
c = []
with open(inputFileName) as f:
l = f.readline().strip().split(" ")
while True:
x = f.readline().strip()
if x == '':
break
c.append(int(x))
return ciphertext(c,l[0],l[1])
# This will write cipher text to a file in the standard format used for this
# assignment
def writeCipherTextToFile(outputFileName, cipherText):
with open(outputFileName,"w") as f:
print(cipherText.l,cipherText.b,file=f)
for c in cipherText.c:
print(c,file=f)
# this function is included here because there is a Java version, but it
# is not needed in Python: to convert a SHA-256 hash to hex form, just use:
# hashlib.sha256(bytes(plaintext,'ascii')).hexdigest()
def convertHash():
assert False
# Given an ASCII string, this will convert it to an integer representation
def convertFromASCII(text):
return int.from_bytes(bytes(text,'ascii'),"big")
# Given an integer representation of an ASCII string, this will convert it to
# ASCII
def convertToASCII(block):
h = hex(block)[2:]
if len(h) % 2 == 1:
h = '0' + h
return bytes.fromhex(h).decode('ascii')
# Given a bit size and a certainty, this will generate a (probably) prime
# number of the desired size
def generate_prime(bits, k):
count = 0
while True:
# generate a number, make sure it's odd
n = random.randint(2,2**(bits+1)-1)
if n % 2 == 0:
n += 1
count += 1
# run the Fermat primality test
iterations = 0
for _ in range(k):
a = random.randint(1,n-1)
if pow(a,n-1,n) != 1: # it's composite
break
else: # prime so far
iterations += 1
if iterations == k:
return n
#----------------------------------------
# You have to implement these functions
# Given the passed bitlength (int), it will generate a key. This returns a
# rsakey object.
def generateKeys(bitlength):
pass
# Given the passed rsakey object and string, this will perform the RSA
# encryption. It should return a ciphertext object.
def encrypt(key, plaintext):
pass
# Given the provided rsakey object and ciphertext object plaintext, this will
# perform the RSA decryption. It should return a string.
def decrypt(key, cipherText):
pass
# Given the passed rsakey, which will not have a private (d) key, it will
# determine the private key by attempting to factor n. It returns a rsakey
# object.
def crack(key):
pass
# Given the passed rsakey object and string, it will return a ciphertext object that
# is the digital signature of the text, signed with the private key.
def sign(key, plaintext):
pass
# Given the passed rsakey object, string, and ciphertext object, this will
# check the signature; it only returns True (if the signature is valid) or
# False (if not).
def checkSign(key,plaintext,signature):
pass
#----------------------------------------
# Don't modify this! Or, if you do modify this, make sure you submit the
# original version when you submit the assignment. This is necessary for our
# testing code.
def main():
global verbose, showPandQ, outputFileName, inputFileName
outputFileName = "output.txt"
inputFileName = "input.txt"
keyName = "default"
i = 1
while i < len(sys.argv):
if sys.argv[i] == "-verbose":
verbose = not verbose
elif sys.argv[i] == "-output":
i = i + 1
outputFileName = sys.argv[i]
elif sys.argv[i] == "-input":
i = i + 1
inputFileName = sys.argv[i]
elif sys.argv[i] == "-key":
i = i + 1
keyName = sys.argv[i]
elif sys.argv[i] == "-showpandq":
showPandQ = True
elif sys.argv[i] == "-keygen":
i = i + 1
bitLength = int(sys.argv[i])
key = generateKeys(bitLength)
writeKeyToFile (key,keyName)
elif sys.argv[i] == "-encrypt":
key = readKeyFromFile (keyName + "-public.key")
plaintext = open(inputFileName).read()
cipherText = encrypt(key, plaintext)
writeCipherTextToFile(outputFileName, cipherText)
elif sys.argv[i] == "-decrypt":
key = readKeyFromFile (keyName + "-private.key")
cipherText = readCipherTextFromFile(inputFileName)
plaintext = decrypt(key, cipherText)
with open(outputFileName,"w") as f:
print(plaintext,file=f,end='')
elif sys.argv[i] == "-sign":
key = readKeyFromFile (keyName + "-private.key")
plaintext = open(inputFileName).read()
signature = sign(key,plaintext)
writeCipherTextToFile(inputFileName+".sign", signature)
elif sys.argv[i] == "-checksign":
key = readKeyFromFile (keyName + "-public.key")
plaintext = open(inputFileName).read()
signature = readCipherTextFromFile(inputFileName+".sign")
result = checkSign(key,plaintext,signature)
if not result:
print("Signatures do not match!")
elif sys.argv[i] == "-crack":
key = readKeyFromFile (keyName + "-public.key")
cracked = crack(key)
writeKeyToFile (cracked,keyName+"-cracked")
elif sys.argv[i] == "-seed":
seed = int(sys.argv[++i])
random.seed(seed)
else:
print ("Unknown parameter: '" + str(sys.argv[i]) + "', exiting.")
exit()
i += 1
if __name__ == '__main__':
main()