PA2

Programming Assignment Two: PA2 (mycrypt)
Milestone: Wednesday night, April 29 @ 11:59pm
Final: Tuesday night, May 5 @ 11:59pm
Overview:
Hackers abound and you have been asked to write a crypto program to encrypt/decrypt data. The program will
be able to read the data to be encrypted either from a file specified on the command line or from stdin (data
could either be typed in at the keyboard or stdin redirected from a file or via a pipe). The encrypted data will be
written to stdout which can be redirected to a file.
You will be writing a program that takes 5 inputs from the command line:
[cs30xyz@ieng9]:pa2$ ./pa2 passPhrase key1 key2 rotVal FILE
The program will ask the user to enter a pass phrase of at least 8 characters, two 32-bit crypto keys, and a
rotation key in the range [-63, +63]. The program will XOR the first 8 characters of the pass phrase and the two
32-bit crypto keys to form a 64-bit cypto mask that will be used to XOR the data in 8 byte chunks (two 32-bit
register operations per 8 byte chunk) plus individual single byte masks for the trailing bytes. With each 8 bytes
of data encrypted with the 64-bit mask, the mask will be rotated according to the rotation key value (rotating left
if the rotation key is positive and rotating right if the rotation key is negative).
Grading:
README: 10 points
Compiling: 5 points
Using our Makefile; no warnings.
Style: 10 points
Correctness: 75 points
-10 points for each unimplemented module or module written in the wrong language (C vs
Assembly and vice versa).
Includes both abnormal and normal output, both to the correct destination (stderr vs stdout).
Make sure you have all files tracked in Git - we will be checking for multiple commits of each
file and that they were meaningful commits.
Wrong Language: -10 points
-10 for each module in the wrong language, C vs. Assembly or vice versa.
Extra Credit: 5 points
NOTE: If what you turn in does not compile with given Makefile, you will receive 0 points for this assignment.
Setup and Git:
You are required to use Git with this and all future programming assignments. Look at the PA0 writeup for
additional information on Git.
Setting up a local repository
Create your pa2 directory and initialize a local git repository:
[cs30xyz@ieng9]:$ mkdir ~/pa2
[cs30xyz@ieng9]:$ cd ~/pa2
[cs30xyz@ieng9]:pa2$ git init
Starter Files
[cs30xyz@ieng9]:pa2$
[cs30xyz@ieng9]:pa2$
[cs30xyz@ieng9]:pa2$
[cs30xyz@ieng9]:pa2$
[cs30xyz@ieng9]:pa2$
[cs30xyz@ieng9]:pa2$
cp
cp
cp
cp
cp
cp
~/../public/pa2.h ~/pa2
~/../public/pa2_strings.h ~/pa2
~/../public/pa2_globals.c ~/pa2
~/../public/Makefile-PA2 ~/pa2/Makefile
~/../public/test.h ~/pa2
~/../public/testrotate.c ~/pa2
Example Output
A sample stripped executable for you to try, and compare your output against, is available at:
~/../public/pa2test
When there is a discrepancy between the sample output in this document and the pa2test output,
follow the pa2test output.
Below are some example outputs of this program.
1. Invalid Input
1.1. Wrong number of arguments
[cs30xyz@ieng9]:pa2$ ./pa2 cse30rules 4
Usage: ./pa2 passPhrase key1 key2 rotVal FILE
passPhrase (must at least eight characters long)
key1
(must be numeric; decimal, octal or hexadecimal)
key2
(must be numeric; decimal, octal or hexadecimal)
rotVal
(must be a decimal value within the range [-63 - 63])
FILE
(must be the name of a file or '-' for stdin)
1.2. Miscellaneous mistakes with input
[cs30xyz@ieng9]:pa2$ ./pa2 cse30ru 43a 999999999999 79 sdfaa
Passphrase must be at least 8 chars long
"43a" is not an integer
Converting "999999999999" base "0": Result too large
Rotation key must be within the range of [-63 <-> 63]
sdfaa: No such file or directory
--- Found 5 errors. --1.3. Using hexadecimal value for rotation value
[cs30xyz@ieng9]:pa2$ ./pa2 cse30rules 0xf 04556 0x4 test1 > test2
"0x4" is not an integer
--- Found 1 errors. --2. Valid Input
2.1. Reading from file and redirecting to output file
[cs30xyz@ieng9]:pa2$ ./pa2 cse30rules 12 123456 7 test1 > test2
2.2. Piping to stdin and redirecting to output file
[cs30xyz@ieng9]:pa2$ cat test1 | ./pa2 cse30rules 12 12 4 - > test2
2.3. Using hexadecimal values for key1 and octal for key2
[cs30xyz@ieng9]:pa2$ ./pa2 cse30rules 0x5f 054 12 test1 > test2
2.4. Using a negative number for rotation value
[cs30xyz@ieng9]:pa2$ ./pa2 cse30rules 0x32 013 -43 test1 > test2
Overview
The function prototypes for the various C and Assembly functions are as follows.
C routines
int main( int argc, char *argv[] );
int parseInput( char *str, FILE **inFile );
int parseKey( char *str, unsigned long *key );
int parsePassPhrase( char *str, char *passphrase );
Assembly routines:
void createMask( unsigned long keys[], char passphrase[], unsigned long mask[] );
void mycrypt( FILE *inFile, unsigned long mask[], int rotateValue );
int parseRotateValue( char *str, long *rotateValue );
void rotate( unsigned long mask[], int rotateCnt );
C Modules
1. main.c
int main( int argc, char *argv[] );
This module is responsible for driving the program. It will properly parse all of the command line arguments,
create the mask, perform the encryption/decryption, and close the file. If any errors arise it will skip creating the
mask and encryption/decryption, but will be responsible for all of the error printing.
First, include the following line at the beginning of your main() function. It will disable buffering on stdout
which will help in matching the output of the test program. This line must be included to receive full credit on all
tests.
(void) setvbuf( stdout, NULL, _IONBF, 0 );
Second, make sure the number of arguments match the EXPECTED_ARGS. Note that the program name is
included in argc, so you may handle this however you see fit.
Third, parse the passphrase, then the two keys (one at a time), then the rotation value, then the input file. After
calling the corresponding parse function check the return value and print any corresponding error messages,
but always continue parsing.
Note, some error messages should be sent to stderr using fprintf() and some should be formatted with
snprintf() and sent using perror(). As a rule of thumb, if errno was ever set, then perror() is the
preferred method. In this case, the only errors that set errno are those that return ERANGE_ERR after calling
strtol() or strtoul().
Fourth, if there were any errors print the number of errors encountered and proceed to the fifth step. If there
were no errors, then create the mask and call mycrypt().
Fifth, close inFile.
2. parseInput
int parseInput( char *str, FILE **inFile );
This module is responsible for parsing the specified input file (the fifth command line argument). It will set
inFile (through pointer dereference) and return zero if the string is parsed successfully. If an error is
encountered return the appropriate value.
Check if the string matches just the dash “-“, in which case set the inFile to stdin. If not, it is assumed that
the string is the name of a file to be opened. Open the file, make sure it opens successfully and set inFile to
the returned FILE pointer.
Return Values:
If successful, return zero. If the string represents a file name that fails to open, return FILE_ERR.
3. parseKey
int parseKey( char *str, unsigned long *key );
This module is responsible for parsing either of the key values (the second and third command line
arguments). It will set key (through pointer dereference) and return zero if the string is parsed successfully. If
an error is encountered return the appropriate value.
Convert the string to an unsigned long using strtoul() and the DEF_BASE defined in the header file. After
the conversion, check errno and endptr to test if any errors occurred in the parsing. If no errors occurred set
the key value accordingly.
Return Values:
If successful, return zero. If errno is set to non-zero, return ERANGE_ERR. If endptr points to a non-null
character, return ENDPTR_ERR.
4. parsePassPhrase
int parsePassPhrase( char *str, char *passphrase );
This module is responsible for parsing the passphrase (the first command line argument). It will set the
characters in the passphrase array and return zero if the string is parsed successfully. If an error is
encountered return the appropriate value.
Check if the string contains at least PASS_PHRASE_SIZE characters. If it is at least this long, copy the first
PASS_PHRASE_SIZE characters into the passphrase array.
Return Values:
If successful, return zero. If the string is too short, return LENGTH_ERR.
Assembly Modules
1. createMask
void createMask( unsigned long keys[], char passphrase[], unsigned long mask[] );
This module creates the two 32-bit mask values.
Load the first key and the first four characters of the passphrase, then xor them together. Store the result at the
first four bytes of the mask array. Then load the second key and the next four character of the passphrase,
then xor them together. Store the result at the next four bytes of the mask array.
Return Values:
None.
2. mycrypt
void mycrypt( FILE *inFile, unsigned long mask[], int rotateValue );
This function does the encrypting/decrypting of the data using the keys and masks we got from the user. In a
loop, read up a block of data (BUFSIZ bytes at a time) from inFile with fread(). While there is more data
to read (fread() returns a 0 to indicate it did not read up any items) perform the follow. Encrypt 8 byte
chunks of the data with the 64-bit mask (XOR first 4 bytes of data with mask[0] and the second 4 bytes of
data with mask[1]). Rotate the 64-bit mask with the rotation key using rotate(). Then do the same for the
next 8 bytes of data, and so on until there are less than 8 bytes of data left in your input buffer.
At this point you have to encrypt the remaining bytes in the buffer one at a time being sure to XOR each byte
with the next byte of the 64-bit mask. For example, if there are 5 bytes left to encrypt, XOR the first byte with
the most significant byte of mask[0], XOR the next byte with the next significant byte of mask[0], etc. The
last byte to encrypt will be XORed with the most significant byte of mask[1] in this example. We will discuss
how to do this and many other seemingly difficult operations in discussion sections.
When all the bytes in the input buffer have been encrypted, write the encrypted data out to stdout with
fwrite().
Return Values:
None.
3. parseRotateValue
int parseRotateValue( char *str, long *rotateValue );
This module is responsible for parsing the rotation value (the fourth command line argument). It will set the
rotateValue (through pointer dereference) and return zero if the string is parsed successfully. If an error is
encountered return the appropriate value.
Convert the string to a long using strtol() and base ten. After the conversion, check errno and endptr to
test if any errors occurred in the parsing. If no errors occurred check that the value falls into the correct range
defined by the global variables MIN_ROTATE_G and MAX_ROTATE_G. If the value falls into the correct range,
set the rotateValue accordingly.
Note, that you should use the global variables defined in pa2_globals.c for this module. There are those that
defined the min and max rotate values and those that define the correct values to return for each type of error.
Return Values:
If successful, return zero. If errno is set to non-zero, return ERANGE_ERR. If endptr points to a non-null
character, return ENDPTR_ERR. If the value is out of range return BOUND_ERR.
4. rotate
void rotate( usigned long mask[], int rotateCnt );
This function rotates the current 64-bit mask (mask[]) by rotateCnt places. If the rotateCnt is positive,
rotate left; if the rotateCnt is negative, rotate right. Only the lower 6 bits of the rotateCnt should be used
for the rotateCnt.
Return Values:
None.
Unit Testing
Provided in the Makefile for PA2 are rules for compiling and running tests on individual functions that are part
of this assignment. You are given the source for testrotate.c used to test your rotate() function. Use
testrotate.c as a template for you to write modules for your other test functions.
Unit tests you need to write:
testcreateMask.c
testparseKey.c
testparseRotateValue.c
Think of how to test each of these functions -- boundary cases, special cases, general cases, extreme limits,
error cases, etc. as appropriate for each function.
As part of the grading, we will run all the required unit tests using the targets in the Makefile and manually
grade your required unit test programs.
README File
Along with your source code, you will be turning in a README (use all caps and no file extension for example,
the file is README and not README.txt) file with every assignment. Use vi/vim to edit this file!
Your README file for this and all assignments should contain:
- Header with your name, cs30x login
- High level description of what your program does
- How to compile it (usually just typing "make")
- How to run it (give an example)
- An example of normal output and where that normal output goes (stdout or a file or ???)
- An example of abnormal/error output and where that error output goes (stderr usually)
- How you tested your program
- Anything else that you would want/need to communicate with someone who has not read the writeup
- Answers to questions (if there are any)
Questions to Answer for the README
1. What is the command to rename a file?
2. What is the command to copy a file?
3. What happens when you select text and then middle click in the vim editor when in insert/input mode?
4. What is a .vimrc file, and how do you create/edit them?
5. What is the command to cut a full line of text to the clipboard in vim? How do you paste it? (Both the
questions refer to using the keyboard, not using the mouse.)
6. How do you search for a string in vim?
7. How do you turn on line numbers in vim?
8. How can you quickly (with a single Linux command) change directory to a directory named fubar that is in
your home (login) directory? You cannot change directory to your home directory first and then change
directory to fubar. That would take two commands. State how you would do this with a single command no
matter where your current working directory might be.
9. How do you change the permissions on a file? Let's say want to give read permission to the group? Specify
the command to do this.
Extra Credit
There are 5 points total for extra credit on this assignment.
[2 Points] Early turnin, 48 hours before regular due date and time. (1 point if you get it 24 hours early)
[3 Points] Eliminating nops in the sample assembly file.
Copy over driver_isort.c and isort.s from the public directory:
[cs30xyz@ieng9]:pa2$ cp ~/../public/driver_isort.c ~/pa2
[cs30xyz@ieng9]:pa2$ cp ~/../public/isort.s ~/pa2
You will be modifying isort.s to perform assembly optimization.
NOTE: Only isort.s should have assembly optimization for extra credit. Do not modify any other assembly
functions for PA2 assignment.
Make sure you do not make any changes to driver_isort.c . All the optimization changes you need to make
should be in isort.s.
You can compile the example program for this extra credit using the following command:
[cs30xyz@ieng9]:pa2$ gcc -o driver_isort driver_isort.c isort.s
This program randomly populates an array of 400 ints, then calls isort() method in order to perform an insertion
sort. The isort() method takes in an array of ints (which, you know that the name of the array is actually a
pointer to the first element of the array) and length of the array. After insertion sort has completed, the program
calculates the value of the max integer value in the array minus the minimum integer value of the array.
Sample output:
[cs30xyz@ieng9]:pa2$ ./driver_isort
24
26
29
122
148
159
296
403
444
514
560
643
835
858
944 1047 1154 1237
1562 1810 1917 2044 2068 2200
2346 2397 2441 2624 2667 2676
2944 3036 3078 3321 3346 3371
3640 3733 3886 4015 4116 4126
4182 4426 4430 4663 4728 4856
5103 5536 5596 5711 5826 5847
5975 6000 6003 6084 6190 6402
6587 6591 6624 6711 6735 6865
6917 6934 6960 7318 7339 7341
7418 7527 7567 7617 7623 7724
8094 8153 8269 8483 8508 8545
8650 8710 8762 9245 9261 9319
9427 9449 9454 9555 9646 9687
10022 10168 10229 10238 10293 10334
10882 10885 10904 10908 11100 11155
11604 11684 11716 11889 12024 12045
12153 12234 12237 12245 12245 12389
12748 12932 12943 12952 12998 13128
13267 13311 13318 13586 13587 13826
14256 14260 14279 14290 14295 14365
14434 14577 14599 14615 14631 14658
15105 15217 15312 15349 15360 15372
15616 15620 15710 15748 15773 15794
15853 15898 15977 15989 16117 16128
16317 16334 16390 16460 16529 16552
16689 16776 16846 17066 17218 17321
17503 17540 17606 17616 17658 17680
18133 18134 18213 18230 18241 18432
18907 19064 19091 19103 19156 19166
19247 19421 19464 19554 19602 19663
19845 19857 19895 19955 19974 20027
20202 20254 20414 20446 20556 20763
20832 20943 20973 21006 21072 21096
21266 21342 21344 21386 21421 21440
21533 21720 21863 21864 22112 22133
22497 22570 22591 22649 22654 22713
22910 22934 23007 23219 23293 23329
23737 23772 23844 23992 24022 24029
24404 24408 24636 24710 25028 25042
25303 25376 25490 25540 25602 25750
26146 26167 26296 26576 26719 26781
27136 27152 27194 27243 27309 27361
28198 28340 28476 28501 28588 28735
29148 29151 29302 29497 29668 29750
199
659
1249
2218
2765
3383
4130
4890
5861
6402
6873
7373
7902
8573
9385
9708
10671
11502
12125
12444
13152
13828
14381
14984
15489
15796
16130
16591
17358
17708
18483
19210
19842
20094
20785
21140
21494
22421
22778
23602
24107
25058
25971
26823
27902
28809
29987
229
736
1514
2304
2908
3580
4160
4908
5928
6425
6885
7374
8092
8638
9421
9896
10752
11565
12137
12743
13155
13856
14397
14992
15508
15796
16299
16669
17369
18024
18892
19221
19845
20107
20798
21161
21518
22483
22853
23672
24265
25255
26112
27057
28133
29134
30013
30032 30129 30330 30393 30529 30786 30990 31007
31107 31472 31510 31735 31853 31864 32140 32187
32280 32291 32304 32401 32457 32487 32519 32668
Max - Min = 32644
Notice that there are a total of 6 nops in the assembly code (isort.s).
Your task is to eliminate as many of the nops as you can. All nops can be eliminated in isort.s.
Every nop eliminated will be worth half a point, so to get all 3 points you will have to eliminate all 6 nops. If the
optimized version does not have the same output as the unoptimized version, no points will be awarded.
Milestone and Turnin Instructions
Milestone Check - due Wednesday night, April 29 @ 11:59 pm
[16 points of Correctness Section]
Before final and complete turnin of your assignment, you are required to turnin several modules to your local
for Milestone check.
Files required for Milestone:
createMask.s
parseKey.c
parseRotateValue.s
rotate.s
pa2.h
pa2_strings.h
Makefile
Each module is worth 4 points for a total of 16 points. Each module must pass all of our unit tests in order to
receive full credit.
A working Makefile with all the appropriate targets and any required headers files must be turned in as well. All
four Makefile test cases must compile successfully via the commands make testcreateMask, make
testparseKey, make testparseRotateValue, and make testrotate.
In order for your files to be graded for the Milestone Check, you must use the milestone specific turnin script.
cd ~/pa2
cse30_pa2milestone_turnin
Complete Turnin - due Tuesday night, May 5 @ 11:59 pm
Once you have checked your output, compiled, executed your code, and finished your README file (see
below), you are ready to turn it in. Before you turn in your assignment, you should do make clean in order to
remove all the object files, lint files, core dumps, and executables.
How to Turn in an Assignment
First, you need to have all the relevant files in a subdirectory of your home directory. The subdirectory should
be named: pa#, where # is the number of the homework assignment.
Besides your source/header files, you may also have one or more of the following files. Note the capitalization
and case of each letter of each file.
Makefile: To compile your program with make -- usually provided or you will be instructed to modify an
existing Makefile.
README: Information regarding your program.
Again, we emphasize the importance of using the above names *exactly* otherwise our Makefiles won't find
your files.
When you are ready to submit your pa2, type:
Cd ~/pa2
cse30turnin pa2
Additionally, you can type the following to verify that everything was submitted properly.
cse30verify pa2
Failure to follow the procedures outlined here will result in your assignment not being collected properly and
will result in a loss of points. Late assignments WILL NOT are accepted.
If, at a later point you wish to make another submittal BEFORE the deadline:
cd
cse30turnin pa2
Or whatever the current pa# is, the new archive will replace/overwrite the old one.
To verify the time on your submission file:
cse30verify pa2
It will show you the time and date of your most recent submission. The governing time will be the one which
appears on that file, (the system time). The system time may be obtained by typing "date".
Your files must be located in a subdirectory of your home directory, named paX (where X is the assignment
number, without capitalizations). If the files aren't located there, they cannot be properly collected. Remember
to cd to your home directory first before running turnin.
If there is anything in these procedures which needs clarifying, please feel free to ask any tutor, the instructor,
or post on the Piazza Discussion Board.
Style Requirements
You will be graded for the style of programming on all the assignments. A few suggestions/requirements for
style are given below. Read carefully, and if any of them need clarification do not hesitate to ask.
- Use reasonable comments to make your code clear and readable.
- Use file headers and function header blocks to describe the purpose of your programs and functions. Sample
file/function headers are provided with PA0.
- Explicitly comment all the various registers that you use in your assembly code.
- In the assembly routines, you will have to give high level comments for the synthetic instructions, specifying
what the instruction does.
- You should test your program to take care of invalid inputs like nonintegers, strings, no inputs, etc. This is
very important. Points will be taken off if your code doesn't handle exceptional cases of inputs.
- Use reasonable variable names.
- Error output goes to stderr. Normal output goes to stdout.
- Use #defines and assembly constants to make your code as general as possible.
- Use a local header file to hold common #defines, function prototypes, type definitions, etc., but not variable
definitions.
- Judicious use of blank spaces around logical chunks of code makes your code much easier to read and
debug.
- Keep all lines less than 80 characters, split long lines if necessary.
- Use 2-4 spaces for each level of indenting in your C source code (do not use tab). Be consistent. Make sure
all levels of indenting line up with the other lines at that level of indenting.
- Do use tabs in your Assembly source code.
- Always recompile and execute your program right before turning it in just in case you commented out some
code by mistake.
- Before running turnin please do a make clean in your project directory.
- Do #include only the header files that you need and nothing more.
- Always macro guard your header files (#ifndef … #endif).
- Never have hardcoded magic numbers. This means we shouldn't see magic constants sitting in your code.
Use a #define if you must instead.