ece428-assignment2_v2

advertisement
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
Download