PA2
Transcription
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.