This tutorial is intended to explain how to complete the Code 1.0 cyber security challenge. It is not, however, a fully-complete tutorial on the Python language. For simplicity, this tutorial will guide you through one possible path to the solution. We have pulled together all the information you need in this document.
More complex concepts which may have taken more time to explain and comprehend have been ignored or avoided. You will be expected to learn languages at a deeper level during the Code 1.0 class, but for now we’ll keep it simple. There is no reason for you not to succeed with this challenge, even if you are a complete beginner, if you follow this guide.
You can do this!
- Chapter 1. Sign Up with CS50 edX and set up your IDE environment
- Chapter 2. Python Language Syntax (code formatting rules)
- Chapter 3. Python data and variables (numbers, letters, and such)
- Chapter 4. Python operators
- Chapter 5. Flow Control with the If statement (If this is true then do that, otherwise do something else)
- Chapter 6. Python Loops (how to repeat something multiple times)
- Chapter 7. Python File I/O (Input(reading from) / Output (writing to) a file)
- Chapter 8. Python conversion functions (How to change letters to numbers and such)
- Chapter 9. ASCII table (numbers that represent letters)
- Chapter 10. Hashing and Encryption (How to decode and encrypt data)
- Chapter 11. The Secret.in file format (you will read from this)
- Chapter 12. The Results.out file format (you will write to this)
- Chapter 13. A Step-by-step guide
- Chapter 14. Sending your results to us (and gloating about your success)
Much of the Code 1.0 course follows Harvard's CS50 curriculum. Additionally, we'll use the online CS50 IDE environment to write and run our code.
To get started, Enroll in the the CS50x course at edX.org. Look for the big "Enroll" button:
You will then need to create an account for edX.org. Remember this, as you'll need it to log into the course later.
Next, select Audit This Course (No Certificate). That makes the content freely available for you.
Important They'll send you an email to activate your account. You MUST click on the link they sent you in order to continue using edX.
In order to log into the CS50 IDE, you must first create a GitHub account. If you already have a GitHub account, you can use it to log into the CS50 IDE.
- Create an accout at GitHub / OR use your existing account.
- Then, log into ide.cs50.io with your GitHub account.
Congrats! You now have access to the CS50 IDE, which powered by a service called AWS Cloud9.
You are now ready to work!
The programming environment that we will throughout this course is called the CS50 IDE. It's a web-based programming environment, and should work well on any modern computer: Macs, PCs running Windows or Linux, and even Chromebooks.
There are three main sections of the CS50 IDE:
- The File Pane lists the files and directories in your workspace.
- The Editor is a text editor; this is where we will write much of our code.
- The Terminal gives you access to a Linux command line.
If you accidentally close the Editor or Terminal, you can always reopen them. If your CS50 IDE looks different from the screenshot above, never fear! You still have access to all the tools.
Here's how you'd open a new Terminal:
This brief video shows how to navigate the CS50 IDE, how to work in the python shell, and how to execute a simple python program.
Python is an extremely powerful language with many attributes and capabilities that many appreciate when contrasted to other computer languages. In addition, Python also contains an interactive interpreter which allows immediate commands to be entered with actions performed and results displayed in real time. It is for that reason that we chose it as a simple and effective tool to introduce the concept of computer programing. Our examples will utilize the CS50 IDE and the Python interactive command interpreter window it provides.
Syntax is defined as “the arrangement of words and phrases to create well-formed sentences in a language.” In the case of computer science, the syntax of a computer language is the set of rules that defines the combinations of symbols that are considered to be a correctly structured document or fragment in that language.
Python statements have a particular order and format to them that the Python interpreter can process. All computer languages have their own specific syntax. If you format a statement incorrectly Python will respond with an error. You will have to correct the syntax in order to continue.
When working with any programming language, you include comments in the code to notate your work. This helps explain portions of your code, and lets other developers – you included – know what you were thinking when you wrote the code. This is a necessary practice, and good developers using comments wisely.
In Python, there are two ways to annotate your code. The first is to include comments that detail or indicate what a section of code – or snippet – does. The second makes use of multi-line comments or paragraphs that serve as documentation for others reading your code.
Think of the first type as a comment for yourself, and the second as a comment for others. There is no right or wrong way to add a comment, however. You can do whatever feels comfortable.
Single-line comments are created simply by beginning a line with the hash
character (a #
), and they are automatically terminated by the end of line.
This type of comment is called an inline comment. For example:
# This would be a comment in Python
A Comment that spans multiple lines – used to explain things in more detail –
are created by wrapping the comments in triple-quotes (either '''
or """
).
These are known as docstrings:
'''This would be a multiline comment
in Python that spans several lines and
describes your code or anything you want it to.
'''
Remember to comment as often as possible. It's important! Python has no mandatory statement termination characters and blocks are specified by indentation. Indent to begin a block, dedent to end one. Statements that expect an indentation level end in a colon (:). We’ll see some specific examples later.
Computer languages including Python work with and manipulate data in various forms such as numbers and text. Variables are used to name and temporarily store data while the computer is processing or performing actions on it. You can think of variables as a box or container for pieces data. Every variable has a name and a type.
The variable name is a descriptive label used to work with that particular storage container (a location in the computer's memory) of data. Every variable has a “type” which describes the type or nature of the data in the container. A variable that contains just number values is a different “type” than one that contains text number characters or letters.
Python is a strongly typed (i.e. types are enforced), dynamically, implicitly typed (i.e. you don't have to declare variables), case sensitive (i.e. var and VAR are two different variables). This means that when you use a variable, Python automatically looks at the data you are storing and if it is a number value, assigns it the correct type.
Values are assigned to variable names with the assignment operator, the equals
sign (=
). So you can write code like:
a = 3
Python will determine that 3 is an integer and store it in a variable named a
as an integer type. You can use a
to add, a = a + 2
, or use the python
shorthand a +=2
, or you can perform other mathematical operations because it
is an integer type. In both of the previous examples, the variable a
would
contain the value 5.
You can assign text to a variable by writing something like:
message = "Hello"
Python again automatically recognizes that "Hello" is text data and assigns the
variable names message
with the type str
or string. You can add or
concatenate more text to message by writing:
message = message + " world"
Now, the variable message
would contain the data “Hello world” (notice the
space in front of " world"
). But if you attempted to add an integer value
(e.g. message = message + 5
), python would raise an Exception (or an error).
Variable names can be almost any continuous string of characters long or short.
It is always good practice to make variable names meaningful to describe the
data or value they contain. For instance if you were writing a program for an
automotive use and needed to store the pressure of air in a tire, you could name
the variable a
, t
, or tire_pressure
. All are valid variable names, but
only one is self-descriptive. Can you guess which one!? Later in a very complex
program it would be more meaningful if the variable name let you know at a
glance what information it represented.
Python defines many different data types. We will only concern ourselves with a few simple types you will need to perform this challenge, Numbers and Strings.
Numeric data types store numbers. Number objects are created when you assign a value to them. For example:
var1 = 1
var2 = 10
Python supports three numeric types:
int
(integers)float
(floating point or decimal values)complex
(complex numbers have a real and imaginary component; We won't use them in this tutorial)
Here are some examples of numbers:
int | float |
---|---|
10 | 0.0 |
100 | 15.20 |
-786 | -21.9 |
-4721885298529 | 32.3e18 |
Strings in Python are identified as a contiguous set of characters represented
in quotation marks. Python allows for either pairs of single or double quotes,
and you can have quotation marks of one kind inside a string that uses the other
kind (i.e. "He said 'hello'."
is valid).
The plus (+
) sign is the string concatenation operator and allows you to add
strings together to create longer strings. The asterisk (*
) is the repetition
operator and will create multiple copies of the string. Equality testing is done
using two equals signs (==
).
Subsets or parts of strings can be taken using the indexing and slice
operator ([]
and [:]
). An index is a kind of pointer to a particular
location in the string, starting at 0. In Python the first character location
or index is always 0 and not 1. You might need to use the subset slice operator
soon (hint hint)!
Here are some examples.
message = 'Hello World!'
print (message) # Prints the complete string
print (message[0]) # Prints first character of the string
print (message[2:5]) # Prints characters starting from 3rd to 5th
print (message[2:]) # Prints a subset of the string starting from the 3rd character
print (message * 2) # Prints the string two times
print (message + "TEST") # Prints concatenated strings
The above code would produce the following results:
Hello World!
H
llo
llo World!
Hello World!Hello World!
Hello World!TEST
You can try this for yourself in the CS50 IDE! To launch a python interpreter,
find the terminal window, and at the $
prompt, type python
. At this point,
you should see a >>>
prompt. This is the python prompt; You are now working
with a python interactive shell. Type:
str = "Hello World!"
Then to print the string, you can write the following code (NOTE: you don't have
to type the >>>
, but you should see them on your screen).
>>> print(str)
Try each of the commands in the example above.
Operators are the constructs which can manipulate the value of operands. Consider the expression: 4 + 5 = 9. Here, 4 and 5 are called operands and + is called an operator.
Python supports the following types of operators.
- Arithmetic Operators
- Comparison (Relational) Operators
- Assignment Operators
- Logical Operators
- Bitwise Operators
Assume the variable a
contains the value 10 and the variable b
contains
the value 20, then:
Operator | Description | Example | Result |
---|---|---|---|
+ Addition |
Adds values on either side of the operator. | a + b |
30 |
- Subtraction |
Subtracts right hand operand from left hand operand. | a – b |
-10 |
* Multiplication |
Multiplies values on either side of the operator | a * b |
200 |
/ Division |
Divides left hand operand by right hand operand | b / a |
2 |
These operators compare the values on either sides of them and decide the relation among them. They are also called Relational operators.
Assume variable a
contains the value 10 and variable b
contains the value 20, then:
==
Equality: If the values of two operands are equal, then the expression is true.a == b
is not true.!=
Not Equal: If values of two operands are not equal, then the expression is true.a != b
is true.>
Greater than: If the value on the left is greater than the value on the right, then the expression is true.a > b
is not true.<
Less than: If the value on the left is less than the value on the right, then the expression is true:a < b
is true.>=
Greater than or equal to: If the value on the left is greater than or equal to the value on the right, then the expression is true.a >= b
is not true.<=
Less than or equal to: If the value on the left is less than or equal to the value on the right, then condition becomes true.a <= b
is true.
You've already seen how the =
assignment operator can be used, but python also
has some compound assignment operators. These are simply shorthand for common
arithmetic operators, as you'll see below.
Assume variable a
contains the value 10 and variable b
contains the value 20, then:
=
Assignment: Stores the value in the right in the variable on the left, e.g.a = 10
orb = 20
.+=
Compound add: Adds the value on the right to whatever is current stored in the variable and replaces that result in the variable. e.g.a += 5
would result ina
containing the value 15. This is the same as:a = a + 5
.-=
Compound subtract: Similar to compound addition,a -= 3
would result ina
containing the value 7. This is the same asa = a - 3
.*=
Compound multiplication:a *= 5
would result ina
containing the value 50. This is the same as:a = a * 5
/=
Compound division:a /= 2
would result ina
containing the value 5, which is the same asa = a / 2
.
Python's logical operators let you combine boolean (True or False) expressions; they include:
and
Logical AND lets you ask if two expressions are BOTH True.or
Logical OR lets you ask if either of two expressions are True (but not necessarily both)not
Logical NOT allows you to negate an expression.
Here are some examples, assuming that a
contains the value 10 and b
contains
the value 20, then:
a == 10 and b == 20
would be True.a == 10 and b == 1000
would be False because the right-hand expression (b == 1000
is False)a == 10 or b == 1000
would be True because one of the expressions (a == 10
) is Truenot a == 1000
would be True, becausea == 1000
is False, but thenot
operator flips this to True.
The bitwise operator works on bits (binary digits, i.e. 1 and 0) and performs
bit by bit operation. Each character or byte of data is made up of a group
(usually 8) bits. The bits are ordered so that each position represents binary
data where the only valid values are 0 and 1s. Each of these are added together
according to their position order to make up 8 bit values from 0 to 255. The
operations below compare the bit positions individually between a and b and
utilize the operation indicated by the operator to create a resulting bit
sequence. In the examples below, the "logical and" &
operator only evaluates
as true. Assume a = 60
and b = 13
. These values are expressed in binary
format below:
a = 0011 1100
b = 0000 1101
The result of various bitwise operators are expressed below.
Bitwise And (the &
operator) returns a 1 if both values are equal to 1.
Otherwise it returns a 0.
a = 0011 1100
b = 0000 1101
-----------------
a&b = 0000 1100
Bitwise OR (the |
operator) returns a 1 of either value contains a 1,
otherwise it returns a 0.
a = 0011 1100
b = 0000 1101
-----------------
a|b = 0011 1101
XOR or Exclusive OR (the ^
operator) will copy the bit if it is set in
one operand but not both. HINT this is important!
a = 0011 1100
b = 0000 1101
-----------------
a^b = 0011 0001
Binary Ones Complement (the unary ~
operator) has the effect of "flipping"
bits, or swapping from 0 to 1 or from 1 to 0.
a = 0011 1100
~a = 1100 0011
Python allows us to ask Yes or No questions, based on the value contained
in a variable, the result of a calculation, or some other expression. This
generall takes the form of an if
statement.
Here's an example that tells us if the value stored in an integer variable is equal to 42.
some_value = 3
if some_value == 42:
print("Yes! This value is equal to 42")
In this example, the answer to our question is, "No". Sometimes we may also
want to perform an action if that's the case. We can use an else
clause for
that scenario:
some_value = 3
if some_value == 42:
print("Yes! This value is equal to 42")
else:
print("No! It's not 42 :(")
We may also want to ask a subsequent question; for example: Is the value
equal to 42, and if not, is it equal to zero? We can use the elif
clause
for this.
some_value = 3
if some_value == 42:
print("Yes! This value is equal to 42")
elif some_value == 0:
print("No, but the value is ZERO")
else:
print("No! It's not 42 :(")
You can continue to ask questions using if
, elif
, and else
throughout
your program, and you can even nest these questions inside other constructs
(like loops).
Computer programs are very good at doing things over and over very quickly. One way to do this is to perform a loop. Python has two main looping constructs.
The while
loop is often used to perform a task until some criteria, or condition
evaluates to false. For example, we can write code that prints all of the
integers from 0 to 10:
num = 0
while num <= 10:
print("num = ", num)
num += 1
Notice that we have code that does the following:
- We create a
num
variable whose value is zero. - We construct a while loop; The condition is the expression:
num <= 10
. If this expression ever evaluates to False, the body of the while loop will not get executed. - This sarts the body of the while loop: It simply prints the value contained in
num
- Finally, we update the value stored in
num
by adding 1 to it. If we accidentally forgot this step, we would create an infinite loop. That's because the last two lines of code are in the body of the loop, and the while loop will execute as long asnum <= 10
evaluates to True. If we never change the value innum
(which is currently zero), this code will run forever.
The other looping construct in Python, is the for
loop. It is typically used
when you have a collection of objects or values on which you want to perform
an action.
Assume that we have a string, and we want to print every character in the string individually. Here's an exmaple that will do that:
greeting = "Hello World!"
for character in greeting:
print(character)
This code will print every character in the greeting
variable on a separate
line. In this case, our greeting
is a collection of characters.
Here's an example that prints ONLY the even numbers in a collection of integers:
values = [1, 2, 3, 4, 5, 6, 7, 8]
for num in values:
if num % 2 == 0:
print(num, " is even!")
In this example, we've combined both a for
loop and an if
statement to
inspect every item in a list of integers, but only perform an action if some
calculation is true! (num % 2 == 0
means num mod 2 -- or divide by w and
check if there is no (0) remainder).
Python makes it relatively easy to read from and write to files. The built-in
open
function allows you to open a file. It returns a file object, which
includes methods for reading and writing.
f = open(filename, mode)
However, when using open, we need to specify what mode we want to use when opening the file. The most common modes are:
'r'
when the file will only be read'w'
for only writing (an existing file with the same name will be erased)'a'
opens the file for appending
Examples of reading and writing to a file are shown below:
f = open("something.txt") # if you omit the mode, the default is 'r'
content = f.read() # read the entire file and store it's contents as a string
f.close() # it's a good practice to close the file when you no longer need it.
f = open("something.txt", "w') # open the file for writing (this will create
# a new file if it doesn't exist, and will
# erase the contents of an existing file).
f.write("Hello World!") # we can write a string to a file.
f.close() # and close it when we're finished.
You can learn more about file i/o at the official python documentation
There are a number of times when you may need to convert a variable from one data type to another. There are built-in conversion functions that will do this for you.
For example, you may have a string that contains numeric information. You'll
need to convert that to an integer using the int()
function before you could
perform any arithmetic on that data. Here's an exmple:
data = "123" # This is a string.
data = int(data) # The int() function converts it to an integer
data = data + 1
print(data) # should print: 124
Likewise, you could convert an integer (or nearly any other type of data) into
a string using the str()
function:
n = 5 # this is an integer (notice the lack of quotes)
n = str(n) # we've not converted it to a string: "5"
# now you can perform operations that expect a string:
message = "I have " + n + " apples"
print(message) # would print "I have 5 apples"
Finally, to complete the exercise below, you will need to make use of two functions that allow us to convert between a character and an integer value representing that character's ASCII code (see the next chapter).
chr()
will convert an integer into it's corresponding character.ord()
will convert a character into it's corresponding ascii code.
For example:
value = chr(65)
print(value) # this would print 'A'
value = ord('a')
print(value) # this would print 97
ASCII stands for American Standard Code for Information Interchange. Computers can only understand numbers, so an ASCII code is the numerical representation of a character such as 'a' or '@' or an action of some sort. ASCII was developed a long time ago and now the non-printing characters are rarely used for their original purpose(teletype machines). The original ASCII code consisted of 128 Characters of printable and non-printable device control codes. Each was represented by a seven bit binary code with values ranging from decimal 0 through 127.
Hint: every ASCII character can be represented by a 3 digit decimal number!
The following ASCII Table shows you the convesion from a decimal valu to a character value.
Dec Char Dec Char Dec Char Dec Char
--------- --------- --------- ----------
0 NUL (null) 32 SPACE 64 @ 96 `
1 SOH (start of heading) 33 ! 65 A 97 a
2 STX (start of text) 34 " 66 B 98 b
3 ETX (end of text) 35 # 67 C 99 c
4 EOT (end of transmission) 36 $ 68 D 100 d
5 ENQ (enquiry) 37 % 69 E 101 e
6 ACK (acknowledge) 38 & 70 F 102 f
7 BEL (bell) 39 ' 71 G 103 g
8 BS (backspace) 40 ( 72 H 104 h
9 TAB (horizontal tab) 41 ) 73 I 105 i
10 LF (NL line feed, new line) 42 * 74 J 106 j
11 VT (vertical tab) 43 + 75 K 107 k
12 FF (NP form feed, new page) 44 , 76 L 108 l
13 CR (carriage return) 45 - 77 M 109 m
14 SO (shift out) 46 . 78 N 110 n
15 SI (shift in) 47 / 79 O 111 o
16 DLE (data link escape) 48 0 80 P 112 p
17 DC1 (device control 1) 49 1 81 Q 113 q
18 DC2 (device control 2) 50 2 82 R 114 r
19 DC3 (device control 3) 51 3 83 S 115 s
20 DC4 (device control 4) 52 4 84 T 116 t
21 NAK (negative acknowledge) 53 5 85 U 117 u
22 SYN (synchronous idle) 54 6 86 V 118 v
23 ETB (end of trans. block) 55 7 87 W 119 w
24 CAN (cancel) 56 8 88 X 120 x
25 EM (end of medium) 57 9 89 Y 121 y
26 SUB (substitute) 58 : 90 Z 122 z
27 ESC (escape) 59 ; 91 [ 123 {
28 FS (file separator) 60 < 92 \ 124 |
29 GS (group separator) 61 = 93 ] 125 }
30 RS (record separator) 62 > 94 ^ 126 ~
31 US (unit separator) 63 ? 95 _ 127 DEL
So any letter in the alphabet can be represented by a numeric value. For example,
a capital B
is 66 while a lowercase m
is 109.
A cryptographic hash function is a special class of hash function that has certain properties which make it suitable for use in cryptography.
It is a mathematical algorithm that maps data of arbitrary size to a bit string of a fixed size (a hash function) which is usually designed to also be a one-way function, that is, a function which is infeasible to invert. The only way to recreate the input data from an ideal cryptographic hash function's output is to attempt a brute-force search of possible inputs to see if they produce a match, or use a "rainbow table" of matched hashes. Bruce Schneier has called one-way hash functions "the workhorses of modern cryptography".[1].
The input data is often called the message, and the output (the hash value or hash) is often called the message digest or simply the digest. The hash routine you will be using for this challenge is neither one-way nor is it difficult to crack by serious security professionals. This hash routine is only intended to introduce you to the concepts of obscuring sensitive data by encryption and character data represented by transformed numeric, binary, or unreadable characters in a file, while providing an interesting assignment to challenge a new programmer to think about his code.
The challenge routine simply obscures the clear text data by using the following process:
- Converting each character of the text data into a 3 digit integer equivalent from the ASCII tables chr to decimal chart.
- Adding 128 to the converted ASCII code
- Performing an exclusive or operation (XOR) on each character’s ASCII code with the number 99.
- Converting the integer decimal value to a 3 character string representing the same numeric value.
- Writing the 3 character string values consecutively to the encrypted file for each character in the original clear text.
To decrypt the data again, simply perform the previous steps in exactly the reverse order. You will know if you have successfully decrypted the file if you produce readable text that makes some sense and does not appear jumbled.
Download the Secret.in file. There are a couple ways you can access this file.
One option is to run the followwing command in you CS50 IDE terminal. This will fetch the file from github:
wget https://raw.githubusercontent.com/Tech901/code-precourse-challenge/master/tutorial/Secret.in
Another option is a) create a file named Secret.in
in your CS50 IDE, then open
this link,
and copy and paste the content into your file. Don't forget to save it!
Finally, you can download the file to your local computer, then drag & drop it into the CS50 IDE:
This file contains the encrypted information that you must decode. If you open it in notepad (or any other text editor) it looks like rows and rows of numbers. The secret text you are trying to decode is hidden from plain view by encoding the letters of the text into numbers so that they cannot be read without decoding them with the same routine that was used to encode them.
In the Secret.in
file, each letter, space, and punctuation are represented by
a 3-digit code. The first 3 numbers, 183, represent the first letter of the message.
The next 3 numbers, 139, represent the next letter, and so on and so on through
the end of the file. In the chapter on Hashing and Encryption,
we explained how to decode these numbers so that the secret text can be read.
Be careful not to save over or modify the Secret.in
file as that could
scramble the code. If you think you may have altered it, you can always download
it again.
The Results.out
file is a file that will be created by the Python program you
write. It will contain the results from your program so that we can determine if
you have successfully completed the assignment. The first part of the file will
contain the decoded secret message in clear text that can be directly read in
notepad or any file viewer (such as the CS50 IDE's editor). After the full clear
text of the secret message, a coded signature message will follow. This will be
your email address encoded with the same cipher routine that was used to code the
original Secret.in
file that you decoded.
When we check your Results.out
file, we should see the decoded secret message
in clear text, followed by a series of 3 digit numbers. When we decode that message,
we should see your email address.
If you don't need help, then you don't have to read this chapter! Feel free to write your program on your own. There are many possible solutions to this assignment. What follows is just one possible solution, and it's here if you need a bit of help.
Each character of the original clear text data was hashed by converting the
character to an integer representing that character from the ASCII table. A
constant value of 128 was added to that integer and the resulting value was XORed
with the integer 99. The final resulting integer was converted into a three
character string and written into the Secret.in
file. Each three character
string was written sequentially from beginning to end.
In order to achieve success you must write a Python script in the CS50 IDE to decrypt the secret by reversing the hash process described above. You must also use the original hash to write your encrypted email address to the end of the results file.
Now let's start by breaking down the steps in the process needed to complete this program:
- Read the numbers from the
Secret.in
file. - Examine them 3 at a time.
- Convert each of them from a string of 3 numeric characters to an integer value.
- XOR the integer value with 99
- Subtract 128 from that value
- Convert the resulting integer value to a character represented in the ASCII table
- Add that single character string to the decoded message string
- Repeat steps 2 – 7 until the complete
Secret.in
message has been decoded and written toResults.out
. - Use the same method to encrypt your email address.
- Write your encrypted email address to the end of
Results.out
- Follow the instructions in Chapter 14
to send your
Results.out
file to Code 1.0 for grading
Now let's walk through the steps require to write Python code (program) to perform each step in the process:
First let's make sure your programming environment is ready: Open https://cs50.io
in your browser. Create a new tab in the upper File tab window by clicking on
and selecting New File. This will open a new tab called untitled. Now in
the File menu click Save. In the File name: prompt type precourse.py
and click
Save. You will now be in a tab called precourse.py. You may begin writing and
editing your program here. Don’t forget to save your program often by clicking
Save from the File menu or just hold down the ctrl
key and press s
for save
(or Cmd
+ s
on a Mac). After you save your code you can click on the
terminal tab and at the Linux $
prompt type the following to run your code:
python precourse.py
Press enter, and you will see your results in the terminal window.
You should put comments in your code so that you and others can understand it
when they look at it. Remember that anything that comes after a #
on a line is
a comment for humans to read and is completely ignored by Python.
At the top of the editor screen start your program with comments to include the program’s name and brief descriptive purpose. Always include the programmer's name and the date it was created. This should look something like:
# Program name : precourse.py
# Decode a hashed message stored in Secret.in and write the
# decoded message into Results.out as part of the Code 1.0 application process.
# Created by <your name here> on March 16, 2017
Remember that indentation is important to Python. Keep the same indentation level (number of spaces) unless you are creating a block of statements to execute in a while or for loop.
You need to open the file using Python’s open function. The Syntax for opening a file object is:
object = open(file_name [, access_mode])
Your code should look something like:
secret = open("Secret.in", "r") # Open the Secret.in file for reading
This will open the Secret.in
file in read mode and create a file object named
secret
. Now you need to read the file data into a variable to work with it.
Let’s call the variable coded_text
. We will use Python’s read()
method to
read the entire file into a single variable.
The syntax for read()
method is:
file_object.read(size)
where size is the number of bytes to read from the file. If size is omitted, then the whole file will be read. You will type in something like:
coded_text = secret.read()
Now the string variable coded_text
contains the entire contents of Secret.in
.
We will need a string variable to contain the decoded message. Let's define a variable that contains an empty string; something like:
message = "" # initialize the message variable
We no longer need the file so let’s close it by typing: secret.close()
. Now we
need to write our decoded data to Results.out
. We will again use the open
function but with the "w"
mode for writing:
results = open("Results.out", "w") # open file Results.out for writing
Since the file contained many more than 3 characters, we will need to repeat
this next section multiple times until we get to the end of the data. To do this
we will need a loop control structure. There are several ways to do this but let’s
use a while()
loop to repeat the execution of our block of statements until
we run out of data to process. The while loop syntax is:
while Boolean expression:
statements to execute
Note the statements inside the while loop are indented 4 spaces.
We need a counter variable as an iterator to keep up with how many times we have
looped or iterated through the statements in the while loop. It will also
provide the index pointer value to “slice” up the data in the coded_text
variable into consecutive 3 byte blocks. The letter i
is often used as a
numeric iteration counter in loops. We will also use the len()
function to
return the length of (number of characters in) coded_text
. Type something like
the following to set up the while loop:
i = 0
while i < len(coded_text):
The next block of statements to be repeated by the while loop will be indented an additional 4 spaces under the while statement.
You need to do several things here to convert the data. First you have to take a
slice or subset of 3 characters at a time from coded_text
. As you may remember
from Chapter 3 on strings, in Python you can use the indexed slice operator,
whose syntax is variable_name[s:n]
,to select n
characters from a variable
starting with the s
th character. So under the while statement we will add code
to do this:
i = 0 # initialize the iterator variable to 0
while i < len(coded_text): # set up while loop using i to work through coded_text
slice_var = codedtext[i:i + 3] # slice_var holds 3 chars of coded_text at a time
int_var = int(slice_var) # converts the text contained in coded_text into an integer
Here we need to perform a bitwise operation on the group of 8 bits (binary digits)
that make up the integer value stored in int_var
. The XOR operator in python
is the caret symbol: ^
. For example: result = (v ^ n)
, where v
is the
original number whose bits we want to XOR with n
.
Here we add the following code inside the while loop:
int_var = (int_var ^ 99) # XOR the value with 99 and store the result back in int_var
# don’t forget to indent 4 spaces...
int_var = int_var – 128 # indent 4 spaces...
Now we use the data conversion function chr()
to convert the integer back to
a character equivalent from the ASCII table.
chr_var = chr(intvar) # Please tell me you remembered to indent 4 spaces!
In python we can use the +
operator to concatenate strings together. That is,
we'll append the value in chr_var
onto the end of our message
:
message = message + chrvar
Finally, since we're processing three characters at time from the secret
string,
we need to add 3 to the while loop iteration variable every time we execute
the body of the looop. We can use the +=
operator to add 3 to whatever value
is stored in i
:
i += 3 # Add 3 to the index so we get the next three chrs in coded_text
This is the last statement in the while loop's body. The next statement will not be indented inside the while loop block.
We still have the open file object called results
. We will use the write()
method to write data to the file. The entire message string will be written. We
will add this statement to the program:
results.write(message) # write the message string to Results.out (no indent)
Now we will add a new line to make the Results.out
file more readable when we
add the next section to it.
results.write("\n") # write a newline character to the file, so the next part starts on a new line.
You have finished the first part of the challenge. So far your program should look something like this...
# Program name : precourse.py
# Decode a hashed message stored in Secret.in and write the
# decoded message into Results.out as part of the Code 1.0 application process.
# Created by <your name here> on March 16, 2017
secret = open("Secret.in", "r") # Open the Secret.in file for reading
message = "" # initialize the message to an empty string
coded_text = secret.read()
results = open("Results.out", "w") # open file Results.out for writing
i = 0 # initialize the iterator variable to 0
while i < len(coded_text): # set up while loop using i to work through coded_text
slice_var = coded_text[i:i+3] # slice_var holds 3 chars of coded_text at a time
int_var = int(slice_var) # convert to an integer,
int_var = (int_var ^ 99) # XOR with 99
int_var = int_var – 128 # subtract 128
chr_var = chr(int_var) # convert back into a character
message = message + chr_var # build the decoded message string
i += 3 # add three to the counter before next loop
results.write(message) # write the message string to Results.out (no indent)
results.write("\n") # write a newline to Results.out so next part starts a new line
Notice the block of indented statements under the while statement. That lets Python know they are only to be executed while the condition is true
We will use the same steps as above but in reverse order to create a 3 digit encrypted string of numbers for each letter in your email address. Review the comments and you should understand the reversible routine from above.
email = "[email protected]" # create string variable with text to hash
coded_text = "" # initialize an empty string to hold your encoded email address
i = 0 # re-initialize the iterator variable
while i < len(email): # sets up the while loop using i to work through your email
slice_var = email[i] # sets slice_var to hold 1 character of email at a time
int_var = ord(slice_var) # converts the character into an ASCII integer value
int_var = int_var + 128 # add 128 ASCII code
int_var = (int_var ^ 99) # XOR with 99 and store in int_var
chr_var = str(int_var) # convert the integer value into a string
coded_text = coded_text + chr_var # append the encoded string to our coded_text
i = i + 1 # increment the counter before next loop
Finally, we'll use the write()
method again to write the encoded email address
to the final results file.
results.write(coded_text) # write the message string to Results.out (no indent)
print("Encoded email written to Results.out")
results.close() # And close the file.
Save your program by going to File Menu and clicking on save (or use Ctrl+S or Cmd+S to save the file). Then, run your python program from the Terminal:
$ python precourse.py
Once this completes successfully, you should see a new file, Results.out
listed
in the file pane (to the left of your editor and terminal). You can double-click
the Results.out
file to open it in an editor and see the results. If you can
read the text message and there are three numbers after the message for each
letter in your email address, Congratulations!
You most likely have successfully completed the challenge.
Once you have completed the assignment and created the Results.out
file,
create an email and attach your Results.out
file and your precourse.py
to
that email.
To download your files from the CS50 IDE, right-click on Results.out
in
the File Pane, then select the Download option. This should transfer the file
to your computer from the CS50 IDE. Do the same with your precourse.py
file.
Send the email to: mailto:[email protected].
The subject line should say “Code1.0 Pre-Course Assignment Results.out”. In the body of the email please tell us what you thought of the assignment and give us any constructive suggestions to improve it. We'd also love to hear how you solved the challenge and if you needed the step by step tutorial or not.
Good Luck!