Python Programming – Penniel P. Wu, PhD. 334
Lecture #11 Python Programming Project II – Encryption/Decryption
The Proposed
Algorithm
The algorithm contains two segments: encryption and decryption. The encryption algorithm is
performed by a given quadratic equation. The decryption algorithm is the opposite of
encryption, which is performed by the “inverse” of the quadratic equation. Their core concepts
are expressed in Table 2. A later section will discuss their mathematic theories and the
practicability for use as an encryption mechanism in details.
Table 2: Mechanism
Encryption Algorithm Decryption Algorithm
Let E be ax2 + bx + c with a > 0
Convert P to get x
C = E(f(x)) = E(ax2 + bx + c)
Let D be g(f(x))
Solve x = −𝑏 ±√𝑏2−4𝑎𝑐
2𝑎 with x ≥ 0
D(x) = D(−𝑏 ±√𝑏2−4𝑎𝑐
2𝑎)
Convert x to get P
Apparently, only when the result of calculation based on the selected mathematical equation
can be “inversed” back to its original value, the selected mathematical equation and its inverse
are qualified for use as encryption and decryption mechanism.
Quadratic
Equation and Its
Inverse
Aufmann, Barker, and Lockwood (2003) defines the term “quadratic equation” as an equation
of the standard form ax2 + bx + c = 0 with a ≠ 0. The following illustrates how to find the roots
of x by completing the square.
x2 + 𝑏
𝑎x +
𝑐
𝑎 = 0
x2 + 𝑏
𝑎x = -
𝑐
𝑎
(𝑥 +𝑏
2𝑎)
2 = -
𝑐
𝑎 +
𝑏2
4𝑎2 =
𝑏2−4𝑎𝑐
4𝑎2
𝑥 +𝑏
2𝑎=
±√𝑏2 − 4𝑎𝑐
2𝑎
Solving for x then yields the following formula.
x = −𝑏 ±√𝑏2−4𝑎𝑐
2𝑎
In mathematics, the inverse of a function f is defined as a function, typically donated by the
letter g, such that g(f(x)) = x. Since a quadratic function can be expressed as f(x) = ax2 + bx + c,
its inverse function g of f can be expressed as:
g = −𝑏 ±√𝑏2−4𝑎𝑐
2𝑎
In other words, the inverse of the quadratic is the curve formed by the union of the following
two functions:
Python Programming – Penniel P. Wu, PhD. 335
g = −𝑏+√𝑏2−4𝑎𝑐
2𝑎 and g =
−𝑏−√𝑏2−4𝑎𝑐
2𝑎
The following is an example that explains how the calculation and inversion work. It
necessary to note that when ax2 + bx + c = d with d≠0, the calculation can be performed on ax2
+ bx + (c-d) = 0.
Let f(x) = x2-4x-8, while x = 3.
Therefore, f(3) = 32-4×3-8 = -11
The inversion can be performed by solving x2 - 4x -8 = -11, or
in the standard form x2 -4x + 3 = 0.
Since x = −𝑏±√𝑏2−4𝑎𝑐
2𝑎, x is either 3 or 1.
Design
Concepts
In order to test the proposed encryption and decryption mechanism, the instructor develops a
set of Python applications. The “Encryption” application takes three integer entries, a, b, and
c, to specify a quadratic function f(x) = ax2 + bx + c. It also takes a string input as the
“plaintext”.
When the “Encrypt” button is clicked, the string input will be broken to a character array. A
for..in loop will convert each character to its ASCII code, such as 65 for “A”, and then send
the converted ASCII code to a function named “calculate()” similar to that in one of the above
sample codes. The “calculate()” function will assign the ASCII value to an variable x and
perform a calculation based on the quadratic function f(x) = ax2 + bx + c. The calculated result
will be temporarily appended to a string variable. The for loop will then iterate through the
entire character array until the last character is processed. In order to delimit every adjacent
hash-like value, the instructor chooses comma (,) as delimiter. A comma is placed between
every two-calculated hash-like values. The following figure demonstrates how the applications
work.
Figure 1. Sample Applications
and
During a data exchange scheme, as illustrated by the following figure, the sender must
privately notify the recipient what the single-key is (the key consists of values of chosen a, b,
and c) before the transmission of “plaintexts”. The sender then encrypts the “plaintexts” and
only transmits the “ciphertexts” to the recipient through the Internet. Upon receiving the
“ciphertexts”, the recipient uses the decryption mechanism to obtain the “plaintexts”. During
the transmission, eavesdroppers can capture packets with sniffer tools; however, they must
crack the encryption mechanism to obtain the “plaintexts”.
Figure 2: Data Exchange Scheme
Sender
Recipient
Encrypt Decrypt
eavesdropper
I am
Bob.
36951,
23134,
37582,
42321,
Internet 36951,2
3134,37
582,423
21,3312
I am
Bob.
Privately exchange a, b, c
Python Programming – Penniel P. Wu, PhD. 336
???
The “Decrypt” application simply inverse the ciphertext to the original plaintext using the
following formulas with a condition that the negative x is eliminated.
x = −𝑏±√𝑏2−4𝑎𝑐
2𝑎
The Encryption
/ Decryption
Mechanism
Seeing that the value of x of a given ax2 + bx + c = 0 can be obtained by the following
formulas, the instructor heuristically evaluates the feasibility to use a quadratic formula for
computing hash-like values.
x = −𝑏±√𝑏2−4𝑎𝑐
2𝑎
The “math” package of Python language provides a function, sqrt(n), to return the square
root of a given number n. By importing the “math” package, programmers can use the sqrt()
function to find the square root of 6.25, as shown below.
import math
print(math.sqrt(6.25))
The above formulas can be expressed as the follows in Python code. It is necessary to note that
programmers can use any programming language to write similar code as well as applications
for encryption and decryption, although the instructor chooses to use Python as the language
for demonstration.
x = (-b + math.sqrt(b*b - 4*a*c)) / 2*a
and
x = (-b – math.sqrt(b*b - 4*a*c)) / 2*a
The following is a simple function named “calculate()” that takes four parameters, x, a, b, and
c, to perform the arithmetic calculation based on the formula f(x) =x2 - 4x – 8 and then return
the result of calculation. The “calculate()” function is a simplified version created to illustrate
the proposed algorithm. Values of a, b, and c are set to be 1, -4, and -8 for the sake of
demonstration. The value of x is set to 65; therefore, the “calculate()” function will convert the
integer 65 (which is the ASCII code of the capital letter ‘A’ and is said to be the “plaintext” in
this case) with the designated quadratic function (f(x) =x2 - 4x – 8) to produce another integer
3957 which is said to be the “ciphertext”.
def calculate(x, a, b, c):
return a*x*x+b*x+c
x = 65
a = 1
b = -4
c = -8
d = calculate(x, a, b, c)
print("f(x) =", d)
or, simply
36951,23134,3
7582,42321,33
124,…..
Python Programming – Penniel P. Wu, PhD. 337
def calculate(x, a, b, c):
return a*x*x+b*x+c
x = 65
a = 1
b = -4
c = -8
print("f(x) =", calculate(x, a, b, c))
The following inverse() function can solve a quadratic function by letting x2 - 4x - 8 = 3957,
and it will produce two x values, 65 and -61, with only one of them being the “plaintext”. The
reason why the instructor wrote the statement, c=c-d, is because the equation needs to be
converted to x2 - 4x - 8 - 3957 = 0. The value of c was -8 before the conversion, and will be -8
– 3957 after the conversion.
import math
def inverse(a, b, c, d):
# convert ax2+bx+c=d to ax2+bx+c-d=0
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
print("n1 =", n1, "n2 =", n2)
a = 1
b = -4
c = -8
d = 3957
inverse(a, b, c, d)
The above code has a problem of calculation bias. When the value of a is larger than 1 or less
than -1, the returned n1 and n2 are multiples of the given ASCII value. For example, the
equation, 2x2-8x-16, is technically 2 × (x2-4x-8); therefore, f(65) = 2x2-8x-16=7914 (or 2 ×
3957).
During the inverse calculation, the equation 2x2-8x-16 is simplified to x2-4x-8 for the sake of
lowered complexity in calculation, as illustrated below.
f(x) = 2x2-8x-16 = 2 × (x2-4x-8)
thus,
f(65) = 2x2-8x-16 = 2 × (x2-4x-8) = 7914 = 2 × 3957
so,
x2-4x-8 = 3957
The above sample code does not take into consideration how the simplification of equation
during calculation could affect the results. The following version of code illustrates how the
bias of calculation is a problem. It uses f(x) = 2x2-8x-16 as formula, so a is set to be 2, b is -8,
and c is -16, while x is still 65.
import math
def inverse(a, b, c, d):
Python Programming – Penniel P. Wu, PhD. 338
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
print("n1 =", n1, "n2 =", n2)
a = 2
b = -8
c = -16
d = 7914
inverse(a, b, c, d)
The following is the result of the above code. However, n1 and n2 are expected to be 65 and -
61.
('n1 =', 260.0, 'n2 =', -244.0)
Interestingly, 260 = 65 × 4 and -244 = -61 × 4. In other words, 260 = 65 × 22 and -244 = -61 ×
22. Since a = 2, 260 = 65 × a2 and -244 = -61 × a2. The following is the code that can avoid
such calculation bias.
n1 = n1 / (a*a);
n2 = n2 / (a*a);
Additionally, since ASCII code can only be a positive integer between 0 and 255; therefore,
the negative value (such as -61) is eliminated and 65 is the “plaintext”. The following is a
revised version that will just return the positive value of x and ignore the negative one. It also
eliminates the possible calculation bias.
import math
def inverse(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (n1 < 0): return n2
else: return n1
a = 1
b = -4
c = -8
d = 3957
print("x =", inverse(a, b, c, d))
Both n1 and n2 must be integers because values of ASCII codes are integers. The following
could provide some sort of insurance because the “int()” function can convert a floating-point
value to an integer.
if (n1 < 0): return int(n2)
else: return int(n1)
The following is functionally equivalent to the above code, although their logics could seem to
be reversed. By the way, ASCII code 0 is defined as NULL, which is typically not used.
Therefore, the cases of (n1 == 0) or (n2 == 0) can be omitted in this program.
Python Programming – Penniel P. Wu, PhD. 339
if (n1 > 0): return int(n1)
else: return int(n2)
The following place both the “calculate()” and “inverse()” functions in one single Python
script file. It also uses the input() function to take user inputs from keyboard, so the values of
x, a, b, and c are provided by the user (and later serves as the “private key”).
import math
def calculate(x, a, b, c):
return a*x*x+b*x+c
def inverse(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (n1 < 0): return n2
else: return n1
x = int(input("Enter the ASCII code of a character: "))
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
d = calculate(x, a, b, c)
print("ciphertext =", d)
print("plaintext =", inverse(a, b, c, d))
The above coding activity, which is tested in Learnin Activity #1, lead the instructor to find
two interesting phenomena that requires programmer’s attention.
• When a is positive (a>0), n1 is always the desired inversed value. When a is negative
(a<0), n2 is always the desired inversed value.
• When a is greater than 1 or less than -1, the desired inversed value is either n1/a2 or n2/a2.
With the above interesting phenomena in mind, the following is another way to select the right
option: n1 or n2.
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
The following is functionally equivalent to the above. By definition, a of a quadratic equation,
ax2 + bx + c = 0, cannot be 0. The case, (a == 0), simply does not exist.
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a < 0): return n2
else: return n1
The following is the revised version that reflects the above two phenomena. The instructor
purposely simplifies the code to explain how the solution works. Experienced programmer can
optimize the following code for fast performance and less resource-consumption.
Python Programming – Penniel P. Wu, PhD. 340
import math
def calculate(x, a, b, c):
return a*x*x+b*x+c
def inverse(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
x = int(input("Enter the ASCII code of a character: "))
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
d = calculate(x, a, b, c)
print("ciphertext =", d)
print("plaintext =", inverse(a, b, c, d))
While the above program proves that the “inverse” operation of a user-selected single
quadratic equation is feasible, the following program proves the applicability for using it to
produce hash-like values. It is necessary to note that the identifier of “calculate()” function
changes to “getCipher”, and the identifier of “inverse()” to “getPlaintext”.
import math
def getCipher(x, a, b, c):
return a*x*x+b*x+c
def getPlaintext(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
a = 3
b = 7
c = 19
d = 0;
for x in range(256):
d = getCipher(x, a, b, c)
print("ASCII =", x)
print("ciphertext =", d)
print("plaintext =", getPlaintext(a, b, c, d))
The above code is tested in Learning Activity #2.
Python Programming – Penniel P. Wu, PhD. 341
Encrypt a
phrase or a
sentence
In Python, a string literal is considered a List of characters; therefore, programmers can simply
pass a string to the list() function to convert a string literal to a List of characters. The
following demonstrates how the conversion works.
>>> s = "Apple"
>>> l = list(s)
>>> print(l)
['A', 'p', 'p', 'l', 'e']
>>> print(l[0], l[1], l[2], l[3], l[4])
('A', 'p', 'p', 'l', 'e')
The following is a simple code that will take a string literal (such as “Apple”) from the user as
the plaintext and convert it to a List of character named “arrChar”. The for..in loop will
retrieve every element of the “arrChar” List and display them on screen one by one. The
variable “ch” is a delegate of elements in “arrChar”. During each iteration, “ch” represents an
element of “arrChar”.
plaintext = input("Enter a phrase: ")
arrChar = list(plaintext)
for ch in arrChar:
print(ch)
In Python, the ord() function returns the ASCII value of a character, as shown below. The
chr() function, on the other hand, converts an ASCII value to a character.
>>> ord('a')
97
>>> chr(97)
'a'
The following code, as a revised version, will convert every element in the “arrChar” List to
the ASCII value.
plaintext = input("Enter a phrase: ")
arrChar = list(plaintext)
for ch in arrChar:
print(ord(ch))
The following is a sample output of the above code. It also illustrates how a given phrase (such
as “Apple”) is converted to a list of ASCII values.
Enter a phrase: Apple
65
112
112
108
101
The following combines the “getCipher()” function with the above sample codes. The
“plaintext” variable keeps a string literal given by the user through keyboard. The list()
function converts the string literal to a List of characters named “arrChar”. Values of a, b, and
c are given the user through keyboard, while the value of x is provided by the “ch” variable of
the for..in loop. During an iteration, the ord() function will convert the current value of “ch” to
it ASCII value, then call the “getCipher()” function to perform a calculation of ax2+bx+c and
return the calculation result to “d”. The value of “d” will be converted to a string literal by
“str()” function and appended to the variable “cipher” with an additional character “,”. The
Python Programming – Penniel P. Wu, PhD. 342
addition comma (“,”) will serve as delimiter. Finally, the program will print the concatenated
string literal (stored in “cipher”).
def getCipher(x, a, b, c):
return a*x*x+b*x+c
###### handle user inputs ######
cipher = ""
plaintext = input("Enter a phrase: ")
arrChar = list(plaintext) ## convert to List of characters
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
### convert character to ASCII ###
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
cipher += str(d) + ","
print(cipher)
The following is the sample output of the above code. The phrase (assuming it is a password)
“Megumi” is converted to a ciphertext: “16537,28945,30135,39137,33849,31349,”.
Enter a phrase: Megumi
Enter value of a coefficient: 3
Enter value of b coefficient: -17
Enter value of c coefficient: 59
16537,28945,30135,39137,33849,31349,
The above code has a small “bug” that needs the programmer’s attention. At the end of the
ciphertext, there is an unwanted comma (“,”) that should be remove. The following dashed
line indicates the unwanted comma.
16537,28945,30135,39137,33849,31349,
In Python, the len() function can return the total number of elements in a List. The following
statement will determine the index of the last element of the “arrChar” array and assigns that
value to the “last” variable.
last = len(arrChar) - 1
The following is the revised code of the for..in loop. The newly added if statement instructs the
program not to append a comma (“,”) when the iteration is working on the last element of the
“arrChar” array; otherwise, append a comma.
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
if (ch == arrChar[last]):
cipher += str(d)
else:
cipher += str(d) + ","
Python Programming – Penniel P. Wu, PhD. 343
The above code, however, has another bug that must be removed. In the above code, the
expression, (ch == arrChar[last]), could encounter a situation in which any character
in the phrase is the same as the last character. For example, in the phrase “Bigger River”, the
last character is “r” while there is another “r” in the phrase. In this case, both “r” will not be
appended with the comma.
The following is the solution of the above problem. It uses a variable named “counter” to
count if “ch” is representing the last character of the phrase. Since the index of the first
element of “arrChar” List is 0, the initial value of “counter” should be 0. During each iteration,
the value of “counter” will increase by 1.
### convert character to ASCII ###
last = len(arrChar) - 1
counter = 0
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
if (counter == last):
cipher += str(d)
else:
cipher += str(d) + ","
counter=counter+1
The following is the revised complete code. It will produce an output similar to
“16537,28945,30135,39137,33849,31349” which does not has the ending comma.
def getCipher(x, a, b, c):
return a*x*x+b*x+c
###### handle user inputs ######
cipher = ""
plaintext = input("Enter a phrase: ")
arrChar = list(plaintext) ## convert phrase to List of
characters
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
### convert character to ASCII ###
last = len(arrChar) - 1
counter = 0
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
if (counter == last):
cipher += str(d)
else:
cipher += str(d) + ","
counter=counter+1
Python Programming – Penniel P. Wu, PhD. 344
print(cipher)
The above code is functionally sufficient to serve as the “encryption application”. It can
encrypt a given phrase to a list of scrambled values called “ciphertext” with the value of a, b,
and c being the “private key”. According to the above example, the “private key” can be
expressed as (3, -17, 59) while the ciphertext is “16537,28945,30135,39137,33849,31349,”.
Decrypt the
ciphertext
In Python, the split() function can return a list of strings by breaking the given string using the
specified delimiter. The output of splitting is a List of strings. The following example
demonstrates how to use a comma (“,”) as the delimiter to break a given string literal to a List
of strings.
>>> s = "16537,28945,30135,39137,33849,31349"
>>> l = s.split(',')
>>> print(l)
['16537', '28945', '30135', '39137', '33849', '31349']
>>> print(l[3])
39137
The following demonstrates how to use the “split()” function to break two string literals: one is
the ciphertext, the other is the “private key”. By the way, the “private key” will be entered by
the user in the format similar to: 3,-17,59. The string literal of ciphertext will be split to a
List of string type named “arrString”, while the string literal of the “private key” is split to
another string List named “pk”.
ciphertext = "16537,28945,30135,39137,33849,31349"
arrString = ciphertext.split(',')
privateKey = input("Enter the private key: ")
pk = privateKey.split(',')
By the way, the value of a, b, and c are int type. It is necessary to use the int() function to
convert them from string to int type. The value of a will come from the first element of the
“pk” List and is denoted as pk[0]. The value of b will come from the second element of the
“pk” List and is denoted as pk[1].
a = int(pk[0])
b = int(pk[1])
c = int(pk[2])
The following is a for..in loop. During every iteration, it will retrieve an element from the
“arrString” List (represented by “str”), convert the retrieve string literal (such as “16537”) to
an integer using the “int()” function and assign the converted integer to “d”, and then call the
“getPlainTet()” function by passing values of a, b, c, and d to get the positive value of x (an
ASCII code). Finally, the chr() function will convert the ASCII value to its corresponding
character. The character will be appended to a variable named “plaintext”.
for str in arrString:
d = int(str)
plaintext += chr( int(getPlaintext(a, b, c, d)) )
The following statement will display the appended string literal on screen and that string is the
original phrase known as “plaintext”.
print(plaintext)
The following is the complete code.
Python Programming – Penniel P. Wu, PhD. 345
import math
def getPlaintext(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
###### handle user inputs ######
plaintext = ""
ciphertext = "16537,28945,30135,39137,33849,31349"
arrString = ciphertext.split(',')
privateKey = input("Enter the private key: ")
pk = privateKey.split(',')
a = int(pk[0])
b = int(pk[1])
c = int(pk[2])
### convert ASCII to character ###
for str in arrString:
d = int(str)
plaintext += chr( int(getPlaintext(a, b, c, d)) )
print(plaintext)
The above code is functionally sufficient to serve as the “decryption application”.
The GUI
version
To make the “Encrypt” application becomes a GUI-based application, the instructor chooses to
use the input box provided by the “InputBox.py” file as GUI tool to take user inputs; therefore,
the following statement is changed
plaintext = input("Enter a phrase: ")
to
InputBox.ShowDialog("Enter a phrase:")
plaintext = InputBox.GetInput()
Similarly, the following is changed
a = int(input("Enter value of a: "))
to
InputBox.ShowDialog("Enter value of a:")
a = int(InputBox.GetInput())
In order to display the output in a message box, the following is changed
print(cipher)
Python Programming – Penniel P. Wu, PhD. 346
to
###### Message Box Code ######
from tkinter import *
root = Tk()
root.title('Message Box')
Label(root, justify=LEFT, text = cipher, width=60,
wraplength=360).grid()
root.mainloop()
The instructor also adds the following statement, so the private key will be displayed in the
message box.
cipher += "\n\nPrivacy Key: (" + str(a) + "," + str(b) + "," +
str(c) +")"
Additionally, the following is added to the code, so the ciphertext will also be saved to a file
named “encrypt.txt”.
#### write cipher to a file ####
encfile = open("encrypt.txt",'w')
encfile.write(cipher)
encfile.close()
At the Decrypt application side, the following line is changed
ciphertext = input("Enter the ciphertext: ")
to
InputBox.ShowDialog("Enter the ciphertext:")
ciphertext = InputBox.GetInput()
The following statement is changed to
privateKey = input("Enter the private key: ")
to
InputBox.ShowDialog("Enter the private key:")
privateKey = InputBox.GetInput()
The following is changed
print(plaintext)
to
###### Message Box Code ######
from tkinter import *
root = Tk()
root.title('Message Box')
Label(root, justify=LEFT, text = cipher, width=60,
wraplength=360).grid()
root.mainloop()
Python Programming – Penniel P. Wu, PhD. 347
Crackability
The term “crackability” refers to the degree of being “crackable”. The instructor adopts this
term to describe how easy the proposed algorithm can be cracked by “unattempted recipients”.
In order to crack the proposed algorithm, the “unattempted recipients” must satisfy the
following conditions:
• Already know that the algorithm is based on a specially selected quadratic function in the
form of f(x) = ax2 + bx + c.
• Be able to correctly guess what values of a, b, and c are.
• Grasp the patterns used for representing the ciphertexts, such as “24351,29671,28456”,
“24351-29671-28456” “24351:29671:28456”, or simply “243512967128456”.
• The numerical code that represent a character, such as the 65 for “A” in ASCII.
Theoretically and mathematically, the proposed algorithm is crackable. However, it will take a
gigantic amount of computing power and intelligence. The reasons are:
• Values of a, b, and c of the quadratic function f(x) = ax2 + bx + c are randomly picked by
the user each time before the encryption starts. For example, Bob uses f(x) = 3x2 - 4x + 9
and f(x) = -47x2 + 3x + 7 to send his social security number and credit card numbers to
Alice, as shown in Table 3.
Table 3. Sample E(P) = C
Function Plaintext Ciphertext
3x2 - 4x + 9 962-44-8531 9528,8541,7913,5904,7913,7913,5904,9193,8224,7608,7016,
-47x2 + 3x + 7 5168302449753382 -131857,-112693,-136883,-147217,-122087,-108137,-117343,-126925,-126925,-152525,-142003,-131857,-122087,-122087,-147217,-
117343,
• Each character of a sensitive data is an individual P, which must go through E(P) to
produce an individual C. Appendix A provides a sample code which assumes a, b, and c
are all picked from the range 1 to 99. With C being 240076, there are 970299 (or 993)
combinations of (a, b, c). If the sensitive data contains n characters, which means there are
C1, C2, …, Cn, then the total combinations of (a, b, c) is n × 970299. Table 4 is a list of
possible combination if a, b, and c are randomly picked integers from a given range, with
an assumption that there are n character in the “plaintexts”.
Table 4. Possible Combination of (a, b, c, Cn)
Range No. of Combination
-100 ~ 100 2013 × n
-1000 ~ 1000 20013 × n
-10000 ~ 10000 200013 × n
-100000 ~ 100000 2000013 × n
-1000000 ~ 1000000 20000013 × n
Table 5. Sample Computed Results
a b c Result
-47 ...
-47
...
...
-47
3 ...
3
...
...
3
-7 ...
7
...
...
5007
40572/1338642271 ................
5168302449753382
................
................
5268413559754483
• The content of sensitive data also makes the algorithm difficult to crack. For example, it
will take a gigantic effort and intelligence to guess which value in Table 5 is the correct
credit card sent number by Bob.
Interestingly, a mathematical phenomenon could boost the crackability. As shown in Table 6,
square root of integers from 81 to 99 all have a digit 9 as the whole number part, although their
fractional parts are different. When using a “chomp(√𝐶𝑖)” function to remove the fractional
part of √𝐶𝑖, then √𝐶81 = √𝐶82 = … = √𝐶99 = 9. Therefore, -47x2 + 3x + 81, -47x2 + 3x +
82, …, and -47x2 + 3x + 99 could possibly produce the same result. This phenomenon is
Python Programming – Penniel P. Wu, PhD. 348
known as the “precision issue”, which may vanish when the value of Ci is a larger integer,
such as 8215.
Table 6. Precision Issue in Square Root
Ci √𝑪𝒊 Ci √𝑪𝒊 Ci √𝑪𝒊
81
82
83
84
...
95
96
97
98
99
9.0
9.05538513813
9.11043357914
9.16515138991
.............
9.74679434480
9.79795897113
9.84885780179
9.89949493661
9.94987437106
8100
8200
8300
8400
...
9500
9600
9700
9800
9900
90.0
90.5538513813
91.1043357914
91.6515138991
.............
97.4679434480
97.9795897113
98.4885780179
98.9949493661
99.4987437106
810000
820000
830000
840000
...
950000
960000
970000
980000
990000
900.0
905.538513813
911.043357914
916.515138991
.............
974.679434480
979.795897113
984.885780179
989.949493661
994.987437106
A later section will discuss about how to programmatically avoid this phenomenon.
Optimization
As stated early, the instructor simplifies the above sample code in order to clearly illustrate the
encryption and decryption mechanisms. Programmers can optimize the code in five aspects:
(a) resource utilization, (b) processing speed, (c) length of allowed string for encryption and
decryption, (d) exchange of the single key, and (e) delivery of “ciphertexts”.
ASCII code and Unicode use a series of consecutive integers to represent characters. Each
integer represents a special character, such as 65 for “A”, 66 for “B”, and 67 for “C”. As
demonstrated in a previous section, using a set of relatively small consecutive integers to
present character can cause the “precision issue”. Programmers can randomly assign numerical
code to each character as demonstrated in Table 7. The larger the number the more secured the
proposed algorithm could be.
Table 7. Randomly Assigned Numerical Code Char A B C D E F G H I J K L M
Code 5305 4427 6512 7243 3195 2781 6236 4813 5418 2149 7743 4185 2134
Char N O P Q R S T U V W X Y Z
Code 4320 2443 1624 9352 4601 8180 1314 2238 8166 3754 6902 3493 8522
Char a b c d e f g h i j k l m
Code 1810 7456 9064 3301 1487 1423 7188 3015 5324 1144 2611 4231 8743
Char n o p q r s t u v w x y z
Code 3481 2192 6624 5727 4618 2148 1079 5240 4217 7094 6631 9095 3784
Char 0 1 2 3 4 5 6 7 8 9 ! @ #
Code 1817 6456 5639 3681 1485 7423 4852 3715 2324 1674 2619 4298 8753
Char $ % ^ & * ( ) - + = < > ?
Code 5269 3591 8024 1983 7956 6393 5282 1714 1312 4645 7978 1026 6637
Char { } [ ] | \ , . / ~ ` ;
Code 1658 4237 2181 3846 5037 8561 4431 5967 8064 6239 1678 9671 4538
The following is the sample code that creates a function named “ord2()” that can return the
value assigned to a character based on the above table.
def chr2(c):
if (c == 'A'): return 5305
if (c == 'B'): return 4427
if (c == 'C'): return 6512
Python Programming – Penniel P. Wu, PhD. 349
....
if (c == 'Z'): return 8522
if (c == 'a'): return 1810
if (c == 'b'): return 7456
...
if (c == 'a'): return 7456
...
if (c == '?'): return 6637
The instructor prepares a Python file named “EncCodes.py” that can be imported to a Python
script to use the above “ord2()” function. Students will need to import the library file using the
following statement.
import EncCodes
The following demonstrates how to use the “ord2()” provided by the “EncCodes” module to
get the above assignment value to a character. The output of the following is 1810 because ‘a’
is assigned 1810 as value according to Table 7.
print(EncCodes.ord2('a'))
The following illustrates how to modify the source code of one of the learning activities to
adopt the randomly generated numerical codes to increase the complexity of the encryption
algorithm and reduce the crackability.
#### Encrypt ######
import InputBox
import EncCodes
..........
..........
for ch in arrChar:
x = EncCodes.ord2(ch)
d = getCipher(x, a, b, c)
In the “EncCodes.py” file, there is a “char2()” function which is created to inverse the “ord2()”
function. The following illustrates how the “char2()” function works.
def chr2(n):
if (n == 5305) : return 'A'
if (n == 4427) : return 'B'
.........
.........
if (n == 9671) : return '`'
if (n == 4538) : return ';'
The following illustrates how to use the “char2()” function. The output is the character “h”
because the assigned numerical code of “h” is 3015.
print(EncCodes.chr2(3015))
The following illustrates how to modify the source code of one the learning activities to adopt
the “char2()” function for decrypting the ciphertext.
for str in arrString:
d = int(str)
plaintext += EncCodes.chr2( int(getPlaintext(a, b, c, d)) )
Review
Question
1. Given a quadratic function, ax2 + bx + c = 0, which is a valid solution of x if written in
Python?
Python Programming – Penniel P. Wu, PhD. 350
A. (b - math.sqrt(b*b - 4*a*c)) / 2*a
B. (b - math.sqrt(b*b - 4*a*c)) / 2*a*a
C. (-b + math.sqrt(b*b - 4*a*c)) / 2*a
D. (-b + math.sqrt(b*b - 4*a*c)) / 2*a*a
2. Given a quadratic function, f(x) = 3x2 + 5x - 69 , which function can return the value of
f(65)?
A. def getF(x): return 2*x*x+5*x-69
B. def getF(x): return a*x*x+b*x+c
C. def getF(x, a, b, c): return 2*x*x+5*x-69
D. def getF(x, a, b, c): return a*x*x+b*x+c
3. Which can convert the equation c=d to c-d=0 if written in Python?
A. c=d
B. c+=d
C. c=c-d
D. d=d-c
4. Given the following code, what is the output of calc(4, 2)?
def calc(n1, a): n1 = n1 / (a*a)
A. 1
B. 2
C. 4
D. 0
5. Given the following string literal, which can break the string literal into a Python list of
characters?
s = "cash"
A. c = list(s)
B. c = arr(s)
C. c = arrChar(s)
D. c = s
6. Given the following code, what is the output of l[3]?
s = "16537,28945,30135,39137,33849,31349"
l = s.split(',')
A. 39137
B. 30135
C. 28945
D. 16537
7. Given the following code, which can display the letter "H"?
str = "P-A-T-H"
s = str.split('-')
A. s[0]
B. s[1]
C. s[2]
D. s[3]
Python Programming – Penniel P. Wu, PhD. 351
8. Which is a sample Python statement that can open and create a text file named "apple.txt"
for saving data?
A. f = open("apple.txt")
B. f = open("apple.txt",'w')
C. f = open('w', "apple.txt")
D. f = open("apple.txt",'save')
9. Given the following code, what is the possible outcome?
print(ord('A'))
A. ord('A')
B. 65
C. A
D. 'A'
10. Given the following code, which is the output of the statement, print(chr2(6))?
def chr2(c):
if (c == 'A'): return 5305
if (c == 'a'): return 4437
if (c == '6'): return 1324
if (c == '5'): return 2674
A. 1324
B. 6
C. chr2(6)
D. '6'
Python Programming – Penniel P. Wu, PhD. 352
Lab #11 Encryption and Decryption Applications
Learning Activity #1:
1. Open the Command Prompt (or Terminal Emulator for Linux user).
2. Change to “cis247” directory.
3. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_1.py” with the following
content:
import math
def calculate(x, a, b, c):
return a*x*x+b*x+c
def inverse(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a)
n2 = n2 / (a*a)
if (n1 < 0): return int(n2)
else: return int(n1)
x = int(input("Enter the ASCII code of a character: "))
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of d: "))
d = calculate(x, a, b, c)
print("ciphertext =", d)
print("plaintext =", inverse(a, b, c, d))
4. Test the program. Using 67 for x (ASCII code), 2 for a, -17 for b, and 94 for c to form the formula: f(x) = 2x2-
17x+94. The following is the sample output.
Enter the ASCII code of a character: 67
Enter value of a: 2
Enter value of b: -17
Enter value of d: 94
ciphertext = 7933
plaintext = 67
5. Download the “assignment template”, and rename it to lab11.doc if necessary. Capture a screen shot similar to
the above and paste it to a Microsoft Word document named “lab11.doc” (or .docx).
Learning Activity #2
1. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_2.py” with the following
content:
import math
def getCipher(x, a, b, c):
or: if (n1 > 0): return int(n1)
else: return int(n2)
Python Programming – Penniel P. Wu, PhD. 353
return a*x*x+b*x+c
def getPlaintext(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a < 0): return int(n2)
else: return int(n1)
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
for x in range(256):
d = getCipher(x, a, b, c)
print("ASCII =", x)
print("ciphertext =", d)
print("plaintext =", getPlaintext(a, b, c, d))
2. Test the program.
Enter value of a: 3
Enter value of b: 7
Enter value of c: 19
ASCII = 0
ciphertext = 19
plaintext = 0.0
ASCII = 1
.........
.........
ASCII = 255
ciphertext = 196879
plaintext = 255.0
3. Capture a screen shot similar to the above and paste it to a Microsoft Word document named “lab11.doc”
(or .docx).
Learning Activity #3
1. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_3.py” with the following
content:
import math
def getCipher(x, a, b, c):
return a*x*x+b*x+c
def getPlaintext(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
######### encryption
or: if (a > 0): return int(n1)
else: return int(n2)
Python Programming – Penniel P. Wu, PhD. 354
plaintext = input("Enter a phrase: ")
arrChar = list(plaintext) ## convert to List of characters
a = int(input("Enter value of a: "))
b = int(input("Enter value of b: "))
c = int(input("Enter value of c: "))
cipher = ""
### convert character to ASCII ###
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
cipher += str(d) + ","
print(cipher)
######## decryption
arrString = cipher.split(',')
plaintext = ""
for str in arrString:
if (str != ''):
d = int(str)
plaintext += chr( int(getPlaintext(a, b, c, d)) )
print(plaintext)
4. Test the program.
Enter a phrase: Bob sent a message to Alice
Enter value of a: 3
Enter value of b: 7
Enter value of c: -19
13511,37721,29479,3277,40461,31291,37051,41
161,3277,28887,3277,36387,31291,40461,40461
,28887,32529,31291,3277,41161,37721,3277,13
111,35729,33791,30077,31291,
Bob sent a message to Alice
5. Capture a screen shot similar to the above and paste it to a Microsoft Word document named “lab11.doc”
(or .docx).
Learning Activity #4: Two separated applications
1. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_4.py” with the following
content:
#### Encrypt ######
def getCipher(x, a, b, c):
return a*x*x+b*x+c
###### handle user inputs ######
cipher = ""
plaintext = input("Enter a phrase: ")
arrChar = list(plaintext) ## convert phrase to List of characters
a = int(input("Enter value of a: "))
Python Programming – Penniel P. Wu, PhD. 355
b = int(input("Enter value of b: "))
c = int(input("Enter value of d: "))
### convert character to ASCII ###
last = len(arrChar) - 1
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
if (ch == arrChar[last]):
cipher += str(d)
else:
cipher += str(d) + ","
print(cipher)
2. Test the program.
Enter a phrase: This is a secret.
Enter value of a: -7
Enter value of b: 24
Enter value of d: 193
-47183,-73023,-74462,-89622,-6207,-74462,-89622,-
6207,-63342,-6207,-89622,-68790,-66038,-88043,-
68790,-91215,-13515
3. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_4_1.py” with the following
content:
### Decrypt ####
import math
def getPlaintext(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
###### handle user inputs ######
plaintext = ""
ciphertext = input("Enter the ciphertext: ")
arrString = ciphertext.split(',')
privateKey = input("Enter the private key: ")
pk = privateKey.split(',')
a = int(pk[0])
b = int(pk[1])
c = int(pk[2])
### convert ASCII to character ###
for str in arrString:
d = int(str)
plaintext += chr( int(getPlaintext(a, b, c, d)) )
Python Programming – Penniel P. Wu, PhD. 356
print(plaintext)
4. Test the program.
Enter the ciphertext: -47183,-73023,-74462,-89622,-
6207,-74462,-89622,-6207,-63342,-6207,-89622,-68790,-
66038,-88043,-68790,-91215,-13515
Enter the private key: -7,24,193
This is a secret.
5. Capture a screen shot similar to the above and paste it to a Microsoft Word document named “lab11.doc”
(or .docx).
Learning Activity #5
1. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_5.py” with the following
content:
#### Encrypt ######
import InputBox
def getCipher(x, a, b, c):
return a*x*x+b*x+c
###### handle user inputs ######
cipher = ""
InputBox.ShowDialog("Enter a phrase:")
plaintext = InputBox.GetInput()
arrChar = list(plaintext) ## convert phrase to List of characters
InputBox.ShowDialog("Enter value of a:")
a = int(InputBox.GetInput())
InputBox.ShowDialog("Enter value of b:")
b = int(InputBox.GetInput())
InputBox.ShowDialog("Enter value of c:")
c = int(InputBox.GetInput())
### convert character to ASCII ###
last = len(arrChar) - 1
counter = 0
for ch in arrChar:
x = ord(ch)
d = getCipher(x, a, b, c)
if (counter == last):
cipher += str(d)
else:
cipher += str(d) + ","
counter=counter+1
cipher += "\n\nPrivacy Key: (" + str(a) + "," + str(b) + "," + str(c) +")"
#### write cipher to a file ####
encfile = open("encrypt.txt",'w')
encfile.write(cipher)
Python Programming – Penniel P. Wu, PhD. 357
encfile.close()
###### Message Box Code ######
from tkinter import *
root = Tk()
root.title('Message Box')
Label(root, justify=LEFT, text = cipher, width=60, wraplength=360).grid()
root.mainloop()
2. Test the program. Record the private key.
3. Use Windows Explorer to go to the “CIS247” directory to find the “encrypt.txt” file, and then open it with
Notepad. The ciphertext should be saved in the file, as shown below.
4. Use Notepad (or “gedit” for Linux user) to create a new text file named “lab11_5_1.py” with the following
content:
### Decrypt ###
import InputBox
import math
def getPlaintext(a, b, c, d):
c=c-d;
n1 = (-b+math.sqrt(b*b-4*a*c))/2*a;
n2 = (-b-math.sqrt(b*b-4*a*c))/2*a;
n1 = n1 / (a*a);
n2 = n2 / (a*a);
if (a > 0): return n1
else: return n2
###### handle user inputs ######
plaintext = ""
InputBox.ShowDialog("Enter the ciphertext:")
ciphertext = InputBox.GetInput()
arrString = ciphertext.split(',')
InputBox.ShowDialog("Enter the private key:")
Python Programming – Penniel P. Wu, PhD. 358
privateKey = InputBox.GetInput()
pk = privateKey.split(',')
a = int(pk[0])
b = int(pk[1])
c = int(pk[2])
### convert ASCII to character ###
for str in arrString:
d = int(str)
plaintext += chr( int(getPlaintext(a, b, c, d)) )
###### Message Box Code ######
from tkinter import *
root = Tk()
root.title('Message Box')
Label(root, justify=LEFT, text = plaintext, width=60, wraplength=360).grid()
root.mainloop()
5. Test the program. Use Notepad to open the encrypt.txt file to copy and paste the ciphertext.
6. Capture a screen shot similar to the above and paste it to a Microsoft Word document named “lab11.doc”
(or .docx). [Feel free to convert the Word document to a .pdf file.]
Submittal
1. Upon the completion of learning activities, compress the following files and name the zipped file lab11.zip.
• lab11_1.py
• lab11_2.py
• lab11_3.py
• lab11_4.py
• lab11_5.py
• lab11.doc (or .docx or .pdf)
2. Upload the zipped file as response to Question 11 (available at Blackboard).
Programming Exercise #11_1:
1. Make a copy the “lab11_5.py” file and rename it to “encryptapp.py”.
2. Make a copy the “lab11_5_1.py” file and rename it to “decryptapp.py”.
3. Download the “EncCodes.zip” file and extract it to the directory where you store both “encryptapp.py” and
“decryptapp.py”.
4. Review the “Optimization” section of the lecture note, and then use the “ord2()” function provided by the
“EncCodes.zip” file to replace the “ord()” function in the “encryptapp.py” file. Similarly, replace the “chr()”
function of the “decryptapp.py” with “chr2()”.
Python Programming – Penniel P. Wu, PhD. 359
5. Make sure the output look similar to the followings.
6. Download the “programming exercise template”, and then rename it to ex11.doc. Copy and paste the source
code(s) to the template. Capture a screen shot similar to Fig. ex11 and paste it to a Microsoft Word document
named ex11.doc (or ex11.docx). [Feel free to convert the Word document to a .pdf file.]
Programming Exercise #11_2:
1. Make a copy of the “lab11_3.py” and rename it to “ex11_2.py”. Then, add the following two lines to the very
beginning of the file. Be sure to replace YourNameHere with your full name.
# Student: YourNameHere
# Programming Exercise: 11_2
2. Write appropriate Python code to enforce that the given values of a, b, and c must satisfy the condition, b2-4ac ≥ 0, display a
rejection message if the condition is not made. [For example, when a = 2, b =3, and c = 7, b2-4ac =32-4×2×7 =9 – 56 = -47.
Then, √𝑏2 − 4𝑎𝑐 = √−47 which is not mathmetically computable.]
3. In an appropriate location, ask the user to enter an integer value (between 10 and 99) as “key”.
4. Modify the following code to strengthen the encryption algorithm by adding the value of “key” to the ASCII
code before assigning the calculated result to the “x” variable.
x = ord(ch)
5. Modify the following code by subtracting the value of “key” from the value returned by the “getPlainText()”
function.
plaintext += chr( int(getPlaintext(a, b, c, d)) )
6. Test the program using a = 2, b =3, and c = 7.
Enter a phrase: A friend in need is a friend indeed
Enter value of a: 2
Enter value of b: 3
Enter value of c: 7
Error!! The given a, b, and c is not a computable combination.
7. Test the program by using a = 3, b = 7, c = 10, key = 67. Make sure the output looks similar to the following.
Enter a phrase: A friend in need is a friend indeed
Enter value of a: 3
Python Programming – Penniel P. Wu, PhD. 360
Enter value of b: 7
Enter value of c: -10
Enter value of key [10-99]: 67
53186,30086,86856,99540,89946,85838,95216,84826,30086,89946,95216,30086,95216,85838
,85838,84826,30086,89946,100636,30086,81826,30086,86856,99540,89946,85838,95216,848
26,30086,89946,95216,84826,85838,85838,84826,
A friend in need is a friend indeed
8. Capture a screen shot similar to the above figure(s) and paste it to a Microsoft Word document named ex11.doc
(or ex11.docx). [Feel free to convert the Word document to a .pdf file.]
Submittal (Please read the instructions carefully)
1. Create a .zip file named “ex11.zip” containing ONLY the following self-executable files and Word document.
Be sure to copy and paste your source codes to the Word document.
• ex11_1.py
• ex11_2.py
• ex11.doc (or .docx, or .pdf) [you may receive zero point if this word document is missing]
2. Upload the zip file as response of Question 12.
Grading criteria:
You will earn credit only when the following requirements are fulfilled. No partial credit is given.
• You successfully submit both source file and executable file.
• Your source code must be fully functional and may not contain syntax errors in order to earn credit.
• Your code must be fully functional to earn the credit. No partial credit is given.
• You will receive zero points if either .py or .doc (or .docx) file is not submitted.