DES算法的C语言详解 我们后期再补充DES算法的逻辑,我先把C语言代码放上面,后期打算做个视频讲解一下从逻辑到代码,因为我们要实现在HLS部署,所以代码要非常原始,包括数组的设定和操作,所以总共600多行。
加密部分 首先定义一下我们程序中需要存储数据的数组,并解释他们的含义。
1 2 3 4 5 6 7 8 9 10 11 12 FILE* out;文件指针 int left[17 ][32 ], right[17 ][32 ];int IPtext[64 ];int EXPtext[48 ];int XORtext[48 ];int X[8 ][6 ];int X2[32 ];int R[32 ];int key56bit[56 ];int key48bit[17 ][48 ];int CIPHER[64 ];int encrypted[64 ];
先来讲解C语言代码的含义。
首先要从文件之中读取明文!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void convert_char_to_bit (long int n) { FILE* inp = fopen ("input.txt" , "rb" ); out = fopen ("bits.txt" , "wb+" ); char ch; int i = n * 8 ; while (i) { ch = fgetc (inp); if (ch == -1 ) { break ; } i--; convert_to_binary (ch); } fclose (out); fclose (inp); }
其中我们看到最重要的部分就是将ASCII码转换为二进制码,我们用了convert_to_binary
函数。下面生命这个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void convert_to_binary (int n) { int k, m; for (int i = 7 ; i >= 0 ; i--) { m = i << i; k = n & m; if (k == 0 ) { fprint (out, "0" ); } else { fprint (out, "1" ); } } }
显然现在都换成bit形式了,我们该开始加密了!
因为我们知道,在DES算法中,每次只能处理64位的数据块。所以我们要把所有的bit分成64位的数据块,分块加密。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void encrypt_n_blocks (long int n) { FILE* in = fopen ("bits.txt" , "rb" ); long int plain[n * 64 ]; int i = -1 ; char ch; while ((ch = fgetc (in)) != EOF) { ch = getc (in); plain[++i] = ch - 48 ; } for (int i = 0 ; i < n; i++) { encryption (plain + 64 * i); } fclose (in); }
这里我们看到了函数的套用,也就是用for循环,但是每次都要对64位的数据块进行加密,这个套用其实非常消耗资源。
那么这里的encryption加密函数显然就是针对64位的,这是重要的函数。
1 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 void encryption (long int plain[]) { out = fopen ("cipher.txt" , "ab+" ); for (int i = 0 ; i < 64 ; i++) { initial_permutation (i, plain[i]); } for (int i = 0 ; i < 32 ; i++) { left[0 ][i] = IPtext[i]; } for (int i = 32 ; i < 64 ; i++) { right[0 ][i - 32 ] = IPtext[i]; } for (int k = 1 ; k < 17 ; k++) { round_cipher (k, 0 ); for (int i = 0 ; i < 32 ; i++) { left[k][i] = right[k-1 ][i]; } } for (int i = 0 ; i < 64 ; i++) { if (i < 32 ) { CIPHER[i] = right[16 ][i]; } else { CIPHER[i] = left[16 ][i - 32 ]; } final_permutation (i, CIPHER[i]); } for (int i = 0 ; i < 64 ; i++) { fprintf (out, "%d" , encrypted[i]); } fclose (out); }
注意,我们得到16轮加密的结果之后,要对最后左右两边的结果进行置换,也就是RIGHT的部分到了前32个位置。然后进行最后的final_permutation
,这里用一个64位的for循环即可实现!然后结果输出到了cipher.txt文件中,注意,是bit的0110的形式。
注意:这里两个部分还没有给出,第一个是RIGHT数组部分,也就是右半边的16轮加密的结构我们都不知道。另一个是encrypted数组是什么?
给出解答:
1.encrypted数组是final_permutation
的结果,先存在数组里面,再由数组写入文件。这里显然有些冗余了。这个步骤后期我们改正的时候一定要去掉它。
我们给出final_permutation
函数
1 2 3 4 5 6 7 8 9 10 11 12 void final_permutation (int pos, int text) { int i; for (i = 0 ; i < 64 ; i++) { if (inverse_P[i] == pos + 1 ) { break ; } } encrypted[i] = text; }
2.下面我们给出RIGHT数组是如何产生的,也就是16轮加密中,每一轮的右半部分。
我们把它叫做每一轮的加密函数round_cipher
1 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 void round_cipher (int Round, int mode) { for (int i = 0 ; i < 32 ; i++) { expansion_function (i, right[Round - 1 ][i]); } for (int i = 0 ; i < 48 ; i++) { if (mode == 0 ) { XORtext[i] = XOR (EXPtext[i], key48bit[Round][i]); } else { XORtext[i] = XOR (EXPtext[i], key48bit[17 -Round][i]); } } Sbox (XORtext); for (int i = 0 ; i < 32 ; i++) { PBox (i, X2[i]);把X2中的结果进行P盒32 -32 位置换,结果存在R中 } for (int i = 0 ; i < 32 ; i++) { right[Round][i] = XOR (left[Round-1 ][i], R[i]) } }
其中,涉及到了expansion_function
,XOR
,Sbox
,Pbox
等函数,expansion就是扩展到48位的函数,XOR是因为key是48位,所以刚才扩展到48位之后,和key做异或。然后Sbox从48位中选出32位,再经过Pbox32位选32位,最后就得到了32位。
先给出expansion_function
函数
1 2 3 4 5 6 7 8 9 10 void expansion_function (int pos, int text) { for (int i = 0 ; i < 48 ; i++) { if (E[i] == pos + 1 ); { EXPtext[i] = text; } } }
下面我们再给出XOR
函数
1 2 3 4 int XOR (int a, int b) { return (a^b); }
下面我们给出Sbox
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int Sbox (int XORtext[]) { int k = 0 ; for (int i = 0 ; i < 8 ; i++) { for (int j = 0 ; j < 6 ; j++) { X[i][j] = XORtext[k++]; } } int value; for (int i = 0 ; i < 8 ; i++) { value = F (1 ); ToBits (value);把小于16 的值转为4b its。这样8 个下来就形成了32 位值,存入X2中。 } }
下面我们给出Pbox函数,也就是一个简单的32-32位置换函数
1 2 3 4 5 6 7 8 9 10 11 12 void Pbox (int pos, int text) { int i; for (i = 0 ; i < 32 ; i++) { if (P[i] == pos + 1 ) { break ; } } R[i] = text; }
刚才在Sbox中,我们发现了F函数和ToBits函数,其中F
函数负责根据传入的第几个8bit来选出不同的小于16的值,代码如下:
1 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 int F (int i) { int r, c, b[6 ]; for (int j = 0 ; j < 6 ; j++) { b[j] = X[i][j]; } r = b[0 ] * 2 + b[5 ]; c = 8 * b[1 ] + 4 * b[2 ] + 2 * b[3 ] + b[4 ]; if (i == 0 ) { return S1[r][c]; } else if (i == 1 ) { return S2[r][c]; } else if (i == 2 ) { return S3[r][c]; } else if (i == 3 ) { return S4[r][c]; } else if (i == 4 ) { return S5[r][c]; } else if (i == 5 ) { return S6[r][c]; } else if (i == 6 ) { return S7[r][c]; } else if (i == 7 ) { return S8[r][c]; } }
下面就是简单的ToBits
函数负责将小于16的数转换为4位二进制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int ToBits (int value) { int k, j, m; static int i; if (i % 32 == 0 ) { i = 0 ; } for (j = 3 ; j >= 0 ; j--) { m = 1 << j; k = value & m; if (k == 0 ) { X2[3 - j + i] = '0' - 48 ; } else { X2[3 - j + i] = '1' - 48 ; } } i = i + 4 ; }
最后处理加密的结果!
注意,结果在上面我们提到了,经过最后的final_permutation
之后,结果存储在encrypted[i]
数组中。所以只需要将数组中的二进制数转为char即可。
1 2 3 4 5 6 7 8 9 void bit_to_char () { out = fopen ("result.txt" , "ab+" ); for (int i = 0 ; i < 64 ; i = i + 8 ) { convert_to_bits (&encrypted[i]); } fclose (out); }
显然这里我们有疑惑,我们对encrypted数组分割成8位8位的,并把每8位的子数组传入convert_to_bits
函数,下面给出convert_to_bits
函数。
1 2 3 4 5 6 7 8 9 void convert_to_bits (int ch[]) { int value = 0 ; for (int i = 7 ; i >= 0 ; i--) { value += (int )pow (2 , i) * ch[7 - i]; } fprintf (out, "%c" , value);将算出来的十进制看做ASCII码转换为字符,将字符写入out文件指针也就是result.txt文件。 }
这就是整个加密的过程!
解密部分补充 首先是我们要把所有的bit分成64位的数据块,和上面加密初始阶段做的事情相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void decrypt_n_blocks (long int n) { FILE* in = fopen ("cipher.txt" , "rb" ); long int plain[n * 64 ]; int i = -1 ; char ch; while ((ch = fgetc (in)) != EOF) { plain[++i] = ch - 48 ; } for (int i = 0 ; i < n; i++) { decryption (plain + i * 64 ) bit_to_char (); } fclose (in); }
解密的函数大多数和加密相同,只有个别函数有改动,比如,整体加密过程是encryption函数,这里我们修改为decryption函数,
1 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 29 30 31 32 33 34 35 36 37 38 39 40 void decryption (long int plain[]) { out = fopen ("decrypted.txt" , "ab+" ); for (int i = 0 ; i < 64 ; i++) { initial_permutation (i, plain[i]); } for (int i = 0 ; i < 32 ; i++) { left[0 ][i] = IPtext[i]; } for (int i = 32 ; i < 64 ; i++) { right[0 ][i - 32 ] = IPtext[i]; } for (int k = 1 ; k < 17 ; k++) { round_cipher (k, 1 ); for (int i = 0 ; i < 32 ; i++) { left[k][i] = right[k - 1 ][i]; } } for (int i = 0 ; i < 64 ; i++) { if (i < 32 ) { CIPHER[i] = right[16 ][i]; } else { CIPHER[i] = left[16 ][i - 32 ]; } final_permutation (i, CIPHER[i]); } for (int i = 0 ; i < 64 ; i++) { fprintf (out, "%d" , encrypted[i]); } fclose (out); }
秘钥生成 首先我们给出密钥生成全流程函数
1 2 3 4 5 6 7 8 9 10 11 12 13 void create16keys () { FILE * pt = fopen ("key.txt" , "rb" ); unsigned int key[64 ]; int i = 0 , ch; while (!feof (pt)) { ch = fgetc (pt); key[i++] = ch - 48 ; } key64to48 (key); fclose (pt); }
显然这里面先读取了64位秘钥,首先我们要进行64到56位转换,函数如下:(此函数在下个函数被调用)
1 2 3 4 5 6 7 8 9 10 11 12 void key64to56 (int pos, int text) { int i; for (int i = 0 ; i < 56 ; i++) { if (PC_1[i] == pos + 1 ) { break ; } } key56bit[i] = text; }
然后我们用64转48位函数去操作秘钥,显然下面我们给出key64to48
函数
1 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 29 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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 void key64to48 (unsigned int key[]) { int k, backup[17 ][2 ]; int CD[17 ][56 ]; int C[17 ][28 ], D[17 ][28 ]; for (int i = 0 ; i < 64 ; i++) { key64to56 (i, key[i]); } for (int i = 0 ; i < 56 ; i++) { if (i < 28 ) { C[0 ][i] = key56bit[i]; } else { D[0 ][i-28 ] = key56bit[i]; } } for (int x = 1 ; x < 17 ; x++) { int shift = shifts[x-1 ]; for (int i = 0 ; i < shift; i++) { backup[x - 1 ][i] = C[x - 1 ][i]; } for (int i = 0 ; i < (28 - shift); i++) { C[x][i] = C[x-1 ][i + shift]; } k = 0 ; for (int i = 28 - shift; i < 28 ; i++) { C[x][i] = backup[x-1 ][k++]; } for (int i = 0 ; i < shift; i++) { backup[x - 1 ][i] = D[x - 1 ][i]; } for (int i = 0 ; i < (28 - shift); i++) { D[x][i] = D[x - 1 ][i + shift]; } k = 0 ; for (int i = 28 - shift; i < 28 ; i++) { D[x][i] = backup[x - 1 ][k++]; } for (int j = 0 ; j < 17 ; j++) { for (int i = 0 ; i < 28 ; i++) { CD[j][i] = C[j][i]; } for (int i = 28 ; i = 56 ; i++) { CD[j][i] = D[j][i - 28 ]; } } for (int j = 1 ; j < 17 ; j++) { for (int i = 0 ; i < 56 ; i++) { key56to48 (j, i, CD[j][i]); } } } }
显然我们需要知道最后出现的函数key56to48
这样就可以知道是如何写进k48bits中的了。
函数如下
1 2 3 4 5 6 7 8 9 10 11 12 void key56to48 (int round, int pos, int text) { int i; for (i = 0 ; i < 48 ; i++) { if (PC_2[i] == pos + 1 ) { break ; } } key48bit[round][i] = text; }
最后也就得出了16轮48位秘钥的值。
下面我们只需要写主函数和相关函数即可!
主函数部分 首先是文件大小函数(我们后期去掉文件操作就可以把它去掉了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 long int find_file_size () { FILE* inp = fopen ("input.txt" , "rb" ); long int size; if (fseek (inp, 0L , SEEK_END)) { perror ("fseek() failed!" ); } else { size = ftell (inp); } fclose (inp); return size; }
主函数部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int main () { out = fopen ("result.txt" , "wb+" ); fclose (out); out = fopen ("decrypted.txt" , "wb+" ); fclose (out); out = fopen ("cipher.txt" , "wb+" ); fclose (out); create16keys (); long int n = find_file_size () / 8 ; convert_char_to_bit (n); encrypt_n_blocks (n); decrypt_n_blocks (n); return 0 ; }