DES/RSA ALGORITHM SIMULATORS ASSIGNMENT #2 ECE 428 – COMPUTER NETWORKS AND SECURITY Author: Kevin Kim Hugh Soong Ilija Baniski Table of Contents List of Figures ............................................................................................................... ii Introduction ................................................................................................................... 1 Purpose ....................................................................................................................... 1 DES Algorithm Implementation .................................................................................... 2 RSA Algorithm Implementation.................................................................................... 6 Appendix ........................................................................................................................ 7 DES Algorithm Implementation Source Code .............................................................. 7 des.cpp ................................................................................................................ 7 des_util.h ........................................................................................................... 13 RSA Algorithm Implementation Source Code ............................................................ 15 RSADriver.java .................................................................................................. 15 RSA.java ........................................................................................................... 16 RSAKey.java ..................................................................................................... 18 i List of Figures Figure 1 - Outline of the DES algorithm implementation in C++ ....................................... 2 Figure 2 - DES algorithm original plaintext ...................................................................... 3 Figure 3 - DES algorithm ciphertext (string format) .......................................................... 3 Figure 4 - DES algorithm ciphertext (binary format) ......................................................... 5 Figure 5 - Plaintext from decrypted/modified ciphertext (randomly chosen bit) ................ 5 Figure 6 - RSA algorithm original plaintext ...................................................................... 6 Figure 7 - RSA algorithm ciphertext................................................................................. 6 Figure 8 - RSA algorithm decrypted plaintext .................................................................. 6 Figure 9 - RSA algorithm encryption keys ....................................................................... 6 ii Introduction Purpose The purpose of this report is to provide thorough and detailed explanations on how the accuracy of the DES and RSA algorithm implementations were verified. The report will provide a clear explanation on how the DES algorithm was implemented, the source code of the implementation, as well as samples of plaintext and ciphertext. Similarly, The report will provide a clear explanation on how the RSA algorithm was implemented, the source code of the implementation, the two keys, and samples of plaintext and ciphertext. 1 DES Algorithm Implementation The DES algorithm was implemented in C++. The following is an outline of the DES implementation. A function was created for each step of the DES algorithm to ensure that the implementation was correct. boolpermute(char * text, intlen_in, char * out, intlen_out, ptbl table); intip_tbl[64]; intiip_tbl[64]; intep_tbl[48]; intp_tbl[32]; int pc1_tbl[56]; int pc2_tbl[48]; intls_tbl[16]; intsboxes[8][64]; intleft_shift_map[56]; voidp1(char * key, char * p); voidp2(char * shifted, char * p/*, int round*/); voidpi(char * word, char * p); voidexpand(char * h_word, char * p); voidsubstitute(char *e_word, char * p); voidpr(char * s_word, char * p); voidlast_swap(char * p_word, char * p); voidiip(char * word, char * p); voidf_box(char * text, int round); voiddes (char * msg_string, char * key, char * output, int size, int decrypt); voiddo_des (char * input, char * output, int decrypt); voidgenerate_keys(char * key); voidshow(char *object, size_t size); Figure 1 - Outline of the DES algorithm implementation in C++ The implementation method is based on an observation made about the DES algorithm operation in general: the operation of the algorithm mainly involves permuting a string of bits in accordance to a permutation table. Because of this, a permute function was created that accepts a bit string and permutation table, and outputs the permuted bit string. This general function was used as a basis to almost all operations of the DES algorithm, from the bit shifting and PC1/PC2 permutations of the keys to the expansion and permutation of the message strings. Outside of this, there is a des function that breaks strings down into 64-bit chunks that can be processed through the DES algorithm, and a do_des function that will execute the DES algorithm on the input message. To verify the correctness of the DES implementation, debug statements were placed whenever a bit string was modified, and the changes were outputted in binary. This way, it is possible to verify – bit by bit – that a bit has moved from one position to its correct destination. Also, we were able to locate a 3rd party DES implementation “calculator”, which was able to accept a plaintext string as input, and output the encrypted message, as well as display each and every permutation and key that is generated during encryption, which we then used to verify our implementation. The calculator can be found at the following URL: http://people.eku.edu/styere/Encrypt/JS-DES.html. 2 The following plaintext and ciphertext can be obtained when using the key (in hex) 3b 38 98 37 15 20 f7 5e. Using our implementation, the text can be encrypted and decrypted successfully in DES: Original Plaintext: The Data Encryption Standard (DES) is a block cipher (a form of shared secret encryption) that was selected by the National Bureau of Standards as an official Federal Information Processing Standard (FIPS) for the United States in 1976 and which has subsequently enjoyed widespread use internationally. It is based on a symmetric-key algorithm that uses a 56-bit key. The algorithm was initially controversial with classified design elements, a relatively short key length, and suspicions about a National Security Agency (NSA) backdoor. DES consequently came under intense academic scrutiny which motivated the modern understanding of block ciphers and their cryptanalysis. Figure 2 - DES algorithm original plaintext Ciphertext (string format): _¬µ_¬©hT_¬ø√ç h“ %<)H¬ø¬µ_¬©l∑√§O√µ√ܬ¥‹_√Æ__k ⁄√鬕”P∆¬ØJ˜{yh_√ç_Y√ú√é-TŸ ¬Æ_√ßZ¬∏√°-:√Ö√ç>_R0d._ M_√∫– _ƒ_6‡qN∏\™w√ä¬∏¬©∆√ج¥¬®√ïB√∏˝gJ‰¬©{.— ’¬•‘≤√≤√£¬ÆKL_9√†¬Ø√∑‹c,fi¬ªeh‹{√≤‰q˜√ü^~¬Ø∏¬†_√≠√ÆW_ √≠√µ√™d√µ7_∂fi√ì#J√ú–√£DZ‹∂√ò Figure 3 - DES algorithm ciphertext (string format) Ciphertext (binary format): 00010011 10110101 00001111 11000000 11101010 11110000 00111100 00101001 01001000 01101100 10110111 10001010 11011100 00000110 10010100 11011010 11101011 10110100 01001010 11110111 01111011 00010011 01011001 10000110 00001010 10101000 00010100 00101101 00111010 10000001 00110000 01100100 00101110 10011100 11010000 00000111 01110001 01001110 10111000 11111100 10101001 11000110 01000010 10111111 11111101 01111011 00101110 11010001 10011000 10001011 10101000 10001000 11111000 11010110 11001000 01100101 01101000 01110001 11110111 10100111 11001010 00000100 10010010 10010010 10011011 10010000 10110110 11011110 11101110 10101001 01101000 11000000 01001111 00010100 11010011 01111001 11101011 10001101 11101010 00000100 11000100 01011100 10010101 01100111 11010101 01001011 11011100 11011100 01011110 10010100 01100100 00100011 01101000 11010010 10110101 10011011 00010001 01010000 01101000 00101101 01011010 00111110 00001001 00000100 10101010 10101011 01001010 10110100 01001100 01100011 01111011 01111110 01010111 10011011 01001010 01010100 00001001 00000011 10101110 01101011 11000110 00000111 01010100 11111100 00011010 01001101 00110110 01110111 10101100 11100100 11010100 00001110 00101100 10011000 11111000 00001000 00110111 10000110 00000110 00100101 10101001 10101011 00001001 11111000 11101010 11011001 10000111 01010010 00011101 11100000 11100110 11001101 10101001 10110010 00111001 11011110 11100100 10111000 00001101 00010011 11010000 3 10001011 11100100 00100110 01000100 01100000 11110010 01110111 01001001 00111111 10101010 11101101 11011010 01110111 01010110 11000100 11000111 01011000 10011111 10000000 10101110 11000001 11100001 01111100 10011000 00111110 00000010 01001111 00001111 01000111 11010001 11001101 10110110 01000001 11111111 00001011 00101111 11101111 11011111 00011010 10001111 10001110 11001110 11010101 01110110 11011111 01010001 11011011 00100000 00001100 11011110 01000011 01000100 11100101 11001011 00101011 11000011 00111001 10101000 11011100 01011100 10000101 10101100 10010110 11111100 10010001 11011000 01111111 01010001 01100011 10101111 11000100 00010000 10111000 00100100 11000001 00001011 00100010 00100100 10100110 10101111 00001111 00001110 01111101 01010010 00111111 11101100 00111000 10100101 00000110 10011010 10110100 01111001 10001011 01010001 01110010 10010000 00110001 01010111 11000011 11111100 00110010 11001101 01011010 10011100 00010001 00111000 00100001 11101011 00010011 00000111 01110000 01010110 10100010 00111011 11010111 11101110 00111011 11111100 01111010 11001100 11101101 11110101 00010110 11010101 01001011 01011000 01100010 01110110 01000110 01111110 01101110 00000101 00000101 10101100 00110100 10001000 00101010 01100010 00111101 11111101 11110111 00100001 00100001 00100010 01000100 01111000 00011010 10011111 10011111 01001101 10000010 10101001 01011010 11011100 01011001 11101110 01110101 00000101 01000110 00100000 11111110 10001110 01100100 11011010 00001110 11001111 11111011 00001110 01011101 11111010 00101000 01110100 11010010 11001100 01100000 00010001 00011110 10111000 10010010 10011101 11001010 00111101 01111101 11011101 11010110 11000110 10011010 01101100 00110010 10110010 00000111 11110101 10110110 00101111 00100001 01111100 10110011 10100100 11110001 11101011 00010000 10011000 00111111 11110010 10110110 10100010 01000001 10000001 01100001 10010100 01100100 01000001 01001001 01111100 11110011 11110010 01011011 01000011 11000100 10010101 10010001 00100011 10011110 00100101 11100111 11101111 00101010 01111111 10000010 10110000 11011011 10101110 10111001 11010000 11011010 10001101 11101110 11010101 00100001 11111010 01011101 00011101 01100111 00110001 10011010 01110100 10100010 00000111 10010011 00110011 00010010 11001101 00101000 01000110 11110011 10101111 11010011 00111000 01100100 01001101 00000011 00011111 00000010 10000101 11001011 00011001 10111001 00100001 10010001 01001011 10000010 11100110 01100011 01100011 11001011 11111100 11100101 00010110 01110001 00100010 01100001 11001010 01010101 01110000 00110010 10110000 10101001 11101100 01111010 01001000 11110001 00001000 11011100 00010000 00100011 10101011 01001000 01010100 00100110 00011011 10001101 10000011 10101101 10010001 10110000 11000100 00000000 11010111 00010011 10111011 11101111 00110111 10010000 00101110 11101011 00011100 11011110 10011010 01110111 11110010 01011111 00110100 01000010 10111110 11011011 01100001 01111100 01110000 11000001 11010011 10111110 11100101 10110100 10011101 11111110 00100110 01110100 11110101 10111011 11111111 00001100 00101101 10001000 00110010 00100010 00001101 00011110 11000011 00010011 11001100 10011101 11001011 01011111 00111001 00111001 01100010 10110101 4 00011001 10110111 11010111 00011001 10110100 00001110 01111111 00101000 00110100 00101101 10001110 10111111 00011011 00110001 01010111 00110100 11011110 11101100 10011111 10011000 00100100 01110000 11100111 11110111 00000111 00000000 00010010 11000000 11001011 10011011 10001001 00111010 01011010 00100100 01001010 01000100 01010011 11001001 01110000 10111110 11000111 10110001 10010011 10110111 10001001 11000101 10110100 11000010 01010111 10011010 00010110 11110101 01001101 11101001 10011101 10010011 10111110 10011100 01001001 11000001 00110110 00000101 00000110 11110001 00100011 11010000 11010110 11000100 10101000 01011011 10000111 10011010 11011100 01000001 00101010 00110101 00100011 10111101 11010001 11000010 00010010 00000111 01110100 11110001 00010001 00011111 11001111 11011000 11111010 10011000 11111101 00011001 00000101 11100011 00110100 10001000 11110011 01011010 00010011 01100011 01111010 11100101 11100000 10011110 11110001 11001100 11010001 11111111 01100100 11100011 01111000 01111001 00011111 01101101 10001001 11110001 10110111 01011000 01101101 00011111 10010000 11111001 01100101 10110011 00011000 11101000 11010010 01111111 10000011 11101011 10010010 11111110 00100111 00110000 01111100 01001010 10001100 11001000 01001111 00010011 10000011 11100000 00111011 10101001 01000111 11001100 00010100 10111101 01101111 11100101 01100000 10001011 00101000 01101110 01101001 00100011 Figure 4 - DES algorithm ciphertext (binary format) When the 3rd bit of the 499th byte is flipped, the following plaintext message is obtained. Notice the error only affects a single word. One instance of “National Security Agency” becomes “a√µ{_≈√ú√¶F√Öl Security Agency”: Decrypted Plaintext: The Data Encryption Standard (DES) is a block cipher (a form of shared secret encryption) that was selected by the National Bureau of Standards as an official Federal Information Processing Standard (FIPS) for the United States in 1976 and which has subsequently enjoyed widespread use internationally. It is based on a symmetric-key algorithm that uses a 56-bit key. The algorithm was initially controversial with classified design elements, a relatively short key length, and suspicions about a√µ{_≈√ú√¶F√Öl Security Agency (NSA) backdoor. DES consequently came under intense academic scrutiny which motivated the modern understanding of block ciphers and their cryptanalysis. Figure 5 - Plaintext from decrypted/modified ciphertext (randomly chosen bit) 5 RSA Algorithm Implementation The RSA algorithm for this assignment was implemented in Java. The reason for this was to take advantage of the built in BigInteger class to process the large integers required by RSA. The algorithm was implemented using two classes: the RSA class and the RSAKey class. The RSAKey class was used to represent the keys, both private and public. The RSA class was responsible for all number generations and encryption and decryption calculations. A third class was also used to drive the RSA simulation. The correctness of our implementation was verified by checking the correctness of independent functions. For testing purposes small prime numbers were used. Also, the fact that a long message can be encrypted and successfully decrypted shows that the implementation is not incorrect. We also checked the ciphertext against a third party RSA implementation to further verify the correctness. One of the samples that we ran on our RSA implementation is the following: Original Plaintext: ECE428: Assignment 2 - Implementation of RSA Figure 6 - RSA algorithm original plaintext Ciphertext: 8077922667640783203 5046253358820482373 8077922667640783203 1712729367884595395 6519377116757479951 5208185422105530023 439297322082640741 3698387137592412698 4914288835186659083 522485690492515212 522485690492515212 6870139161631047745 5831946615431867907 5258601660850745835 690019284392425593 5508373806630766094 5258601660850745835 7250153813889153509 3698387137592412698 6519377116757479951 3698387137592412698 1500530885814479914 3698387137592412698 1040946361584735089 690019284392425593 8866940273540819672 8159042064335923026 5508373806630766094 690019284392425593 5508373806630766094 5258601660850745835 7250153813889153509 2594058306111789289 7250153813889153509 6870139161631047745 5806716958016824933 5258601660850745835 3698387137592412698 5806716958016824933 2376823557801398871 3698387137592412698 5672934259409692898 6673580357575861323 4914288835186659083 Figure 7 - RSA algorithm ciphertext Decrypted Plaintext: ECE428: Assignment 2 - Implementation of RSA Figure 8 - RSA algorithm decrypted plaintext Encryption Keys: Public key: (8934552687755886703, 6586553260683432281) Private key: (8934552687755886703, 3673604478326708129) Figure 9 - RSA algorithm encryption keys 6 Appendix DES Algorithm Implementation Source Code des.cpp #include <iostream> #include <string.h> #include "des_util.h" using namespace std; char get_permuted_byte(char * text, int len, int ind[8]); int main() { // the 64bit encryption key char mainkey[8]; mainkey[0] = 0x3b; mainkey[1] = 0x38; mainkey[2] = 0x98; mainkey[3] = 0x37; mainkey[4] = 0x15; mainkey[5] = 0x20; mainkey[6] = 0xf7; mainkey[7] = 0x5e; // the original message char * msg = "ECE428: Assignment 2 - Implementation of DES"; int msg_size = strlen(msg); // place holders for the encrypted and decrypted messages char encrypted[strlen(msg)]; char decrypted[strlen(msg)]; des(msg, mainkey , encrypted, msg_size, 0); des(encrypted, mainkey , decrypted, msg_size, 1); cout << " original: " << msg << endl; cout << "encrypted: " << encrypted << endl; cout << "decrypted: " << decrypted << endl; return 0; } /** * This function permutes the provided text using the table as a key. * The output is "returned" in the out parameter. * * @param text the original text to be permuted. * @param len_in the length (in bytes) of the original text. * @param out place holder for the permuted text. * @param len_out length (in bytes) of the expected output. * @param table an enumeration that specifies which table with be used as a permutation key. * * @return true on success, false on failure. */ bool permute(char * text, int len_in, char * out, int len_out, ptbl table) { int * tbl; // determine which table to use if (table == IP_TBL) tbl = ip_tbl; else if (table == IIP_TBL) tbl = iip_tbl; else if (table == EP_TBL) tbl = ep_tbl; else if (table == P_TBL) 7 tbl = p_tbl; else if (table == PC1_TBL) tbl = pc1_tbl; else if (table == PC2_TBL) tbl = pc2_tbl; else if (table == LS_TBL) tbl = left_shift_map; else return false; // fill the "char * out" parameter one byte at time int ctr = 0; int i; for (i = 0; i < len_out; ++i) { // extract the indices for the next byte to be filled int ind[8] = { tbl[ctr], tbl[ctr+1], tbl[ctr+2], tbl[ctr+3], tbl[ctr+4], tbl[ctr+5], tbl[ctr+6], tbl[ctr+7] }; // get one permuted byte out[i] = get_permuted_byte(text, len_in, ind); // increment the counter by 8bits ctr += 8; } return true; } /** * Create one byte of permuted bits. Use the provided indices to determine * which bits should be placed in the output byte. * * @param text the text from where bits are extracted. * @param len the length (in bytes) of the provided text. * @param ind[8] the 8 indices specifing which bits to be used. */ char get_permuted_byte(char * text, int len, int ind[8]) { char out = 0; // the output character/byte char hot_bit; // byte where only one bit is a 1 int b; // byte index int o; // offset within byte for (int i = 0; i < 8; ++i) // 1 byte, 8 bits { hot_bit = 0; // reset hot_bit b = (ind[i]-1) / 8; // find the byte index o = (ind[i]-1) % 8; // calculate the offset // sanity check, make sure we can access the byte we want if ((b > len) || (b == len && o > 0)) { cout << "Invalid index ind[" << ind[i] << "]bit. The text length is " << len << "B" << endl; return NULL; } // get the byte we are interested in char byte = text[b]; // create the hot_bit, and "embed" it in the output value hot_bit = ((byte >> (7-o)) & 1) << (7-i); out = out | hot_bit; } return out; } // takes the message string and performs des on it // for decrypt: 0 encrypts, 1 decrypts /** * Performs DES on the provided message, with the provided key. * * @param msg_string the message to be encrypted. * @param key the key used for encryption. * @param output place holder for the output. * @param size the size (in bytes) of the input and output. * @param decrypt 0 = encrypt the message; 1 = decrypt the message. 8 */ void des (char * msg_string, char * key, char * output, int size, int decrypt) { int length = 0; int c1 = 0; char m[8]; char r[8]; int c2 = 0; //generate keys generate_keys(key); if (decrypt == 0) { length = size; } else { length = size + (8 - (size%8)); } //break message into 64-bit pieces, then perform des for (int i = 0; i < length; i++) { m[c2] = msg_string[i]; if ( (i == (length - 1)) && (c2 < 7) && (decrypt == 0) ) { //pad the string c2++; while (c2 < 8) { m[c2] = '\0'; c2++; } // perform des on 64bits do_des(m, r, decrypt); // write the encrypted 64bits (8bytes) to the output for (int i2 = 0; i2 < 8; i2++) { output[c1] = r[i2]; c1++; } c2 = 0; } else if ((c2 == 7)) { // no padding required // perform des on 64 bits do_des(m, r, decrypt); // write the encrypted 64bits (8bytes) to the output for (int i2 = 0; i2 < 8; i2++) { output[c1] = r[i2]; c1++; } c2 = 0; } else { c2++; } } } /** * Performs DES on a 64bit message. * * @param input the message to undergo DES. * @param output place holder for the result. * @param decrypt 0 = encrypt the message; 1 = decrypt the message. */ void do_des (char * input, char * output, int decrypt) { char pi_word[8]; char ls_word[8]; if (decrypt == 0) // encrypt { // initial permutation pi(input, pi_word); // do fbox for each round 9 for (int i = 0; i < 16; i++) f_box(pi_word, i); // after doing fbox rounds, do the final swap last_swap(pi_word, ls_word); // inverse initial permutation iip(ls_word, output); } else if (decrypt == 1) { // decrypt // initial permutation pi(input, pi_word); // do fbox for each round for(int i = 15; i >= 0; i--) f_box(pi_word, i); // after doing fbox rounds, do the final swap last_swap(pi_word, ls_word); // inverse initial permutation iip(ls_word, output); } } /** * Generate the keys needed for DES. These are stored as a global array. * * @param key the main key from which the other keys are generated. */ void generate_keys(char * key) { // get the pc1 key char pc1_keys[PC1_KEYS_LEN]; p1(key, pc1_keys); // do the left shifts char lcs_keys[NUM_ROUNDS][PC1_KEYS_LEN]; int i; for (i = 0; i < NUM_ROUNDS; ++i) { if (i == 0) left_shift(pc1_keys, lcs_keys[i], i, PC1_KEYS_LEN ); else left_shift(lcs_keys[i-1], lcs_keys[i], i, PC1_KEYS_LEN ); } // get the pc2 keys for (i = 0; i < NUM_ROUNDS; ++i) permute(lcs_keys[i], PC1_KEYS_LEN, keys[i], PC2_KEYS_LEN, PC2_TBL); } /** * Process the s-boxes for a given 48bit word. * Returns 32 bits 000000 00][0000 0000][00 000000] [000000 00][0000 0000][00 000000] . * * @param e_word the input word (48bits) * @param p the output word (32bits). */ void substitute(char *e_word, char * p) { //split the 48 bit word into 8 6bit parts char s1, s2, s3, s4, s5, s6, s7, s8; char r1, r2, r3, r4, r5, r6, r7, r8; s8 s7 s6 s5 s4 s3 s2 s1 = = = = = = = = (e_word[5] & 0x3f) ; ( ((e_word[4] & 0xf) << 2) | ( ((e_word[3] & 0x3) << 4) | ((e_word[3] & 0xfc) >> 2); (e_word[2] & 0x3f) ; ( ((e_word[1] & 0xf) << 2) | ( ((e_word[0] & 0x3) << 4) | ((e_word[0] & 0xfc) >> 2) ; ((e_word[5] & 0xc0) >> 6) ) ; ((e_word[4] & 0xf0) >> 4) ); ((e_word[2] & 0xc0) >> 6) ) ; ((e_word[1] & 0xf0) >> 4) ); 10 //perform sbox calculation r1 = sboxes[0][((int)s1 & 255)]; r2 = sboxes[1][((int)s2 & 255)]; r3 = sboxes[2][((int)s3 & 255)]; r4 = sboxes[3][((int)s4 & 255)]; r5 = sboxes[4][((int)s5 & 255)]; r6 = sboxes[5][((int)s6 & 255)]; r7 = sboxes[6][((int)s7 & 255)]; r8 = sboxes[7][((int)s8 & 255)]; //combine 8 parts into 32bit word //char * result = new char[4]; p[3] = (r7 << 4) & 0xf0; p[3] = p[3] | r8; p[2] = (r5 << 4) & 0xf0; p[2] = p[2] | r6; p[1] = (r3 << 4) & 0xf0; p[1] = p[1] | r4; p[0] = (r1 << 4) & 0xf0; p[0] = p[0] | r2; return; } /** * Permutation to get the pc1 key. * * @param key the main key. * @param p place holder for the generated pc1 key. */ void p1(char * key, char * p) { permute(key, KEY_LEN, p, PC1_KEYS_LEN, PC1_TBL); } /** * Permutation to get a pc2 key. * * @param shifted the value from which the key is generated. * @param p place holder for the generated pc2 key. */ void p2(char * shifted, char * p/*, int round*/) { permute(shifted, PC1_KEYS_LEN, p, PC2_KEYS_LEN, PC2_TBL); } /** * Perform the initial permutation. * * @param word the word to be permuted. * @param p place holder for the permuted word. */ void pi(char * word, char * p) { permute(word, (64/8), p, (64/8), IP_TBL); } /** * Perform the inverse initial permutation. * * @param word the word to be permuted. * @param p place holder for the permuted word. */ void iip(char * word, char * p) { permute(word, (64/8), p, (64/8), IIP_TBL); } /** * Perform the last swap. * * @param word the original word. * @param p place holder for the shifted word. */ void last_swap(char * p_word, char * p) { p[0] = p_word[4]; 11 p[1] p[2] p[3] p[4] p[5] p[6] p[7] = = = = = = = p_word[5]; p_word[6]; p_word[7]; p_word[0]; p_word[1]; p_word[2]; p_word[3]; } /** * Perform the expansion. * * @param h_word the word to be permuted. * @param p place holder for the permuted word. */ void expand(char * h_word, char * p) { permute(h_word, (32/8), p, (48/8), EP_TBL); } /** * Perform the permutation following the s-boxes. * * @param s_word the word to be permuted. * @param p place holder for the permuted word. */ void pr(char * s_word, char * p) { permute(s_word, (32/8), p, (32/8), P_TBL); } /** * Perform left shift. This function is using a custom permutation table * to perform the shifts. * * @param in_key the input key to be permuted. * @param out_key the permuted/generated key. * @param round used to determine how many shifts need to be performed. * @param size the size of the keys. */ void left_shift(char * in_key, char * out_key, int round, int size) { // go through the left shift table for (int j = 0; j < sizeof(ls_tbl); j++) { if (j == round) // the desired round { if (ls_tbl[j] == 2) // perform shift twice { char temp[7]; permute(in_key, size, temp, size, LS_TBL); permute(temp, size, out_key, size, LS_TBL); } else {// only shift once permute(in_key, size, out_key, size, LS_TBL); } } } } /** * Performs the f_box calculations. * * @param text the text that enters the f_box. * @param round the round of the f_box calculation. */ void f_box(char * text, int round) { // get the right half (which becomes the left half) char left[4]; for (int i = 0; i < 4; i++) { left[i] = text[i+4]; } // expand the last 32bits to 48bits char exp[(48/8)]; expand(text+4, exp); 12 // xor the expanded text with the appropriate pc2_key for (int i = 0; i < 6; i++){ exp[i] = exp[i] ^ keys[round][i]; } // substitute the expanded 48bits into the last 32bits of the original text substitute(exp, text+4); // f-box permutation char tmp[4]; // 32bits pr(text+4, tmp); // combine the two halves for (int i = 0; i < 4; ++i) { // xor the last half with the first half text[i+4] = text[i] ^ tmp[i]; text[i] = left[i]; } } // helper function that outputs in binary void show(char *object, size_t size) { const unsigned char *byte; for ( byte = (unsigned char *)object; size--; ++byte ) { unsigned char mask; for ( mask = 1 << (8 - 1); mask; mask >>= 1 ) { putchar(mask & *byte ? '1' : '0'); } putchar(' '); } putchar('\n'); } des_util.h #include <iostream> #include <string> #define #define #define #define NUM_ROUNDS 16 KEY_LEN (64/8) PC1_KEYS_LEN (56/8) PC2_KEYS_LEN (48/8) // 64bits // 56bits // 48bits #ifndef CHAR_BIT #define CHAR_BIT 8 #endif using namespace std; enum permutation_table { IP_TBL, IIP_TBL, EP_TBL, P_TBL, PC1_TBL, PC2_TBL, LS_TBL } typedef ptbl; char keys[NUM_ROUNDS][PC2_KEYS_LEN]; // 16 keys, 48 bits each bool permute(char * text, int len_in, char * out, int len_out, ptbl table); int ip_tbl[64] = { 58, 50, 42, 60, 52, 44, 62, 54, 46, 64, 56, 48, 34, 36, 38, 40, // initial permutation 26, 18, 10, 2, 28, 20, 12, 4, 30, 22, 14, 6, 32, 24, 16, 8, 13 57, 59, 61, 63, 49, 51, 53, 55, 41, 43, 45, 47, 33, 35, 37, 39, 25, 27, 29, 31, 17, 9, 1, 19, 11, 3, 21, 13, 5, 23, 15, 7 }; int iip_tbl[64] = { // inverse initial permutation 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 }; int ep_tbl[48] = { // expansion permutation 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 }; int p_tbl[32] = { // permutation function (after S-boxes) 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 }; int pc1_tbl[56] = { // permuted choice one (PC1) 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 }; int pc2_tbl[48] = { // permuted choice two (PC2) 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 }; int ls_tbl[16] = { // left shift map 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; int sboxes[8][64] = { { 14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8, 4, 15, 1, 12, 14, 8, 8, 2, 13, 14, 6, 9, 2, 1, 11, 7, 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13 }, { 15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, 9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5, 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2, 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9 }, { 10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1, 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7, 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12 }, { 7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9, 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8, 14 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14 }, { 2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6, 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13, 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3 }, { 12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8, 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10, 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13 }, { 4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6, 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7, 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12 }, { 13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2, 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13, 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11 } }; int left_shift_map[56] = { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 1, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 29 }; void p1(char * key, char * p); void p2(char * shifted, char * p/*, int round*/); void pi(char * word, char * p); void expand(char * h_word, char * p); //processes the sboxes for a given 48bit word. returns 32 bits //000000 00][0000 0000][00 000000] [000000 00][0000 0000][00 000000] void substitute(char *e_word, char * p); void pr(char * s_word, char * p); void last_swap(char * p_word, char * p); void iip(char * word, char * p); // takes the message string and performs des on it void des (char * msg_string, char * key, char * output, int size, int decrypt); // takes a 64-bit message and encrypts it through des void do_des (char * input, char * output, int decrypt); void generate_keys(char * key); void show(char *object, size_t size); void bitshiftl(char * object, int size); void left_shift(char * in_key, char * out_key, int round, int size); void f_box(char * text, int round); RSA Algorithm Implementation Source Code RSADriver.java import java.math.BigInteger; public class RSADriver { 15 public static void main (String argv[]) { RSA r = new RSA(); // the prime numbers used in this RSA BigInteger p = new BigInteger("4294963237"); BigInteger q = new BigInteger("2080239619"); // // BigInteger p = new BigInteger("23"); BigInteger q = new BigInteger("37"); // generate the required values for performing RSA BigInteger n = r.generateN(p, q); BigInteger t = r.generateTotient(p, q); BigInteger e = r.generateE(t); BigInteger d = r.generateD(t, e); // sanity check, output the generated values, and some gcd's System.out.println("n:" + n.toString()); System.out.println("t:" + t.toString()); System.out.println("e:" + e.toString()); System.out.println("d:" + d.toString()); System.out.println("gcd(t,e): " + t.gcd(e).toString()); System.out.println("gcd(t,d*e): " + t.gcd(d.multiply(e)).toString()); // create the keys RSAKey publickey = new RSAKey(n,e); RSAKey privateKey = new RSAKey(n, d); // message to encrypt String msg = "ECE428: Assignment 2 - Implementation of RSA"; System.out.println(" Original message: " + msg); // encrypt the message String cryptic = r.encrypt(msg, publickey); System.out.println("Encrypted message: " + cryptic); // decrypt the message String decrypted = r.decrypt(cryptic, privateKey); System.out.println("Decrypted message: " + decrypted); } } RSA.java import java.math.*; import java.util.ArrayList; import java.util.Random; public class RSA { /** * Calculates the 'N' value to perform RSA. * * @param p The first prime number. * @param q The second prime number. * @return The calculated 'N' value. */ public BigInteger generateN(BigInteger p, BigInteger q) { return p.multiply(q); } /** * Calculates the totient value needed for RSA. * * @param p The first prime number. * @param q The second prime number. * @return The calculated totient. 16 */ public BigInteger generateTotient(BigInteger p, BigInteger q) { return p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE)); } /** * Generates the 'e' value needed to encrypt a message using the RSA algorithm. * The generated value is a co-prime number to the provided totient. * * @param t The provided totient. * @return The generated value for 'e'. */ public BigInteger generateE(BigInteger t) { //determine max size for testNumber int bitLength = t.bitLength(); BigInteger testNumber = new BigInteger(bitLength, new Random()); boolean isCoprime = false; int gcd; // e is relatively prime to t if gcd(t,e) = 1 while(!isCoprime){ gcd = t.gcd(testNumber).intValue(); if ((gcd == 1) && (testNumber.compareTo(t) <= -1)) isCoprime = true; else testNumber = new BigInteger(bitLength, new Random()); } return testNumber; // e } /** * Performs the Extended Euclidean algorithm (EEA) for finding the GCD between numbers 'x' and 'y'. * The result is used to determine the value of 'd', required for decryption of RSA encrypted message. * * The implementation of the EEA is referenced from Donald Knuth. * @param tot The totient. * @param e The 'e' value. * @return The calculated 'd' value. */ public BigInteger generateD(BigInteger tot, BigInteger e) { BigInteger[] a = new BigInteger[3]; BigInteger[] b = new BigInteger[3]; BigInteger[] c = new BigInteger[3]; a[0] a[1] a[2] b[0] b[1] b[2] = = = = = = BigInteger.ONE; BigInteger.ZERO; tot; BigInteger.ZERO; BigInteger.ONE; e; while (b[2].compareTo(BigInteger.ZERO) != 0) { BigInteger q = a[2].divide(b[2]); for (int i = 0; i < 3; i++) { c[i] = a[i].subtract(b[i].multiply(q)); a[i] = b[i]; b[i] = c[i]; } } return a[1]; } 17 /** * Encrypts the provided <code>message</code> using the provided <code>publicKey</code>. * * @param message The message to be encrypted. * @param publicKey The public key used to encrypt the message. * @return A space-separated string of each encrypted byte in the <code>message</code>. */ public String encrypt(String message, RSAKey publicKey) { char msg [] = message.toCharArray(); ArrayList<BigInteger> cryptext = new ArrayList<BigInteger>(); // go through the message, and encrypt it byte-by-byte for (int i = 0; i < message.length(); i++) { int c = (int)msg[i]; cryptext.add(encryptByte(new BigInteger(Integer.toString(c)), publicKey)); } // convert the encrypted text to a string String cryptS = ""; for (int i = 0; i < cryptext.size(); ++i) cryptS += cryptext.get(i).toString() + " "; return cryptS; } /** * Decrypts the provided <code>cryptext</code> using the provided <code>privateKey</code>. * @param cryptext The encrypted message. * @param privateKey The private key used to decrypt the message. * @return The decrypted message as a string. */ public String decrypt(String cryptext, RSAKey privateKey) { // split the encrypted message into byte chunks of the original message String[] cryptS = cryptext.split(" "); char plaintext[] = new char[cryptS.length]; // decrypt the message byte-by-byte for (int i = 0; i < cryptS.length; i++) plaintext[i] = (char)encryptByte(new BigInteger(cryptS[i]), privateKey).intValue(); // create a string and return it as the decrypted message. String result = new String(plaintext); return result; } /** * Encrypts a single byte using the provided key. * * @param m The message, which is just a single byte. * @param key The key used for encryption (and decryption). * @return The encrypted (or decrypted) byte. */ public BigInteger encryptByte(BigInteger m, RSAKey key) { return m.modPow(key.gete(), key.getn()); } } RSAKey.java import java.math.*; 18 /** * This class provides an abstraction of an RSA key. * It contains an 'N' value, and a 'key' value. In terms of the RSA * algorithm the key can either be the 'e' value or the 'd' value. */ public class RSAKey { private BigInteger n; private BigInteger keyNum; /** * Create an RSA key * @param n The value of 'N' * @param keyNum The value of 'e'/'d' */ RSAKey(BigInteger n, BigInteger keyNum) { this.n = n; this.keyNum = keyNum; } /** * Access the 'N' value * @return The 'N' value */ public BigInteger getn() { return n; } /** * Access the key value of this key. * @return The 'e' value. */ public BigInteger gete() { return keyNum; } /** * Access the key value of this key. * @return The 'd' value. */ public BigInteger getd() { return keyNum; } } 19