注: 下述所有加法均為模二加, 即為異或運算
1 根據輸入的祕鑰得到16個子祕鑰
1.1 大致流程
1.2 利用PC-1從K_0中挑出K_1
接下來我們以輸入的祕鑰為"0x123456789abcdeff"
為例, 及二進位制編碼為:
"0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1111"
圖1.2.1 初始祕鑰K_0
接下來我們利用PC-1表
生成新祕鑰K_1
圖1.2.2 挑出的56位二進位制串
"1111 0000 1100 1100 1010 1010 1111"和"1101 0101 1110 0110 1111 1000 1111"
兩邊各是28位, 共56位
1.3 利用PC-2從K_1中挑出16個子祕鑰
對於從1.2得來的一串56位二進位制碼
, 我們將其拆分成兩串28位二進位制碼
, 即為:
左半部分的
"1111 0000 1100 1100 1010 1010 1111"
, 並將其命名為C_0右半部分的
"1101 0101 1110 0110 1111 1000 1111"
, 並將其命名為D_0
接下來, 我們利用下表, 進行迴圈左移, 規則如下:
之後我們按照上面的規則, 展示子祕鑰k_1
是如何生成的:
- 將C_0和D_0分別迴圈左移1位, 得到
C_1-"1110 0001 1001 1001 0101 0101 1111"
以及D_1-"1010 1011 1100 1101 1111 0001 1111"
(PS:迴圈左移1位就是將首位移動到最後末尾)- 將C_1即D_1拼接起來, 得到
"1110 0001 1001 1001 0101 0101 1111 1010 1011 1100 1101 1111 0001 1111"
- 利用PC-2表從拼接得到的字串中挑選出
子祕鑰k_1
, 類似於"利用PC-1表的過程"圖1.3.2 從C_0和D_0中生成的子祕鑰k_0
- 所以
子祕鑰k_1為"0001 1011 0000 0010 1110 1111 1111 1100 0111 1001 0111 0110"
按照同樣的邏輯, 在C_0和D_0已被迴圈左移的基礎上, 重複上述步驟, 我們得到如下16個子祕鑰:
PS: 上述輸出為自己寫的程式碼, 在文末會進行展示.
得到了這16個子祕鑰後, 我們就可以對明文進行加密了, 我們繼續!
2 利用16個子祕鑰對明文進行加密
2.1 大致流程
2.2 將R_0擴充套件成48位的二進位制串, 並與子祕鑰k模二加
我們以輸入的明文為"0x0123456789abcdef"
為例, 即二進位制編碼為:
"0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111"
經過變換後,得到:
"1100 1100 0000 0000 1100 1100 1111 1111 1111 0000 1010 1110 1111 0000 1010 1010"
將其拆分為L_0及R_0, 各32位
L_0:
"1100 1100 0000 0000 1100 1100 1111 1111"
R_0:
"1111 0000 1010 1110 1111 0000 1010 1010"
對R_0使用如下規則擴充套件成48位:
圖2.2.1 R_0擴充套件成48位規則 PS: 綠色的標出的位數即為擴充套件出的12位
因此, 得到RExtend(8個6位):
"011110 100001 010101 011101 011110 100001 010101 010101"
將其與子祕鑰
"0001 1011 0000 0010 1110 1111 1111 1100 0111 1001 0111 0110"
亦或得S盒
"0110 0001 0001 0111 1011 0010 1000 0110 0110 1100 0010 0011"
2.3 使用S1-S8表將48位變回32位
為什麼上面S盒要備註8個6位呢? 原因就是要以8個6位為輸入, 分別經過S1-S8盒的運算,得到8個4位輸出, 也就變成了32位.
圖2.3.1 S1-S8表 我們的S盒長這樣
"011000 010001 011110 110010 100001 100110 110000 100011"
以第一個6位
"011000"
為輸入, 代入到S1表變換以
"011000"
的首位和末尾拼起來的數字為行號, 中間4位為列號(當然都是二進位制的), 即第0(00b)行, 第15(1100b)列, 查詢S1表, 為S1(0, 12) = 5 = 0101b這樣就可以得到第一個4位的輸出, 按照這樣的方式可以得到8個4位的輸出, 合在一起就是一個32位的輸出, 經過運算是
"0101 1100 1000 0001 1011 0101 1010 0001"
2.4 使用P表對32位輸出進行位置變換, 並和L0相加
我們已經得到經過"擴位"和"縮位"
的變換, 現在要按照P表進行簡單的位置變換:
圖2.4.1 P表 圖2.4.2 32位輸出的每一位情況 照著P表生成新的32位輸出
"1010 0001 0000 1000 1010 1101 1001 1011"
再將輸出與L_0相加, L_0:
"1100 1100 0000 0000 1100 1100 1111 1111"
得到結果:
"0110 1101 0000 1000 0110 0001 0110 0100"
2.5 往復這樣的步驟16次, 將得到的R_16和L_16拼接並經IP^-1變換
如題, 得到最終結果:
"1110 1100 1010 1001 0011 1101 1001 1100 1110 0100 1101 1001 1001 1001 1100 1110"
3 程式碼實現
/*
* @Description:
* @Autor: Shmily
* @Date: 2021-03-01 23:40:00
* @LastEditTime: 2021-03-02 22:35:49
*/
#include <bits/stdc++.h>
using namespace std;
const int8_t PC_1[56] = {
57, 49, 41, 33, 25, 17, 9, 1,
58, 50, 42, 34, 26, 18, 10, 2,
59, 51, 43, 35, 28, 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
};
const int8_t PC_2[48] = {
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
};
const int8_t left_move[16] = {
1, 1, 2, 2, 2, 2, 2, 2,
1, 2, 2, 2, 2, 2, 2, 1
};
const int8_t S8[8][4][16] = {
// S1
{
{14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7},
{0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8},
{4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0},
{15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}
},
// S2
{
{15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10},
{3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5},
{0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15},
{13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}
},
// S3
{
{10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8},
{13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1},
{13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7},
{1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}
},
// S4
{
{7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15},
{13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9},
{10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4},
{3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}
},
// S5
{
{2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9},
{14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6},
{4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14},
{11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}
},
// S6
{
{12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11},
{10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8},
{9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6},
{4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13}
},
// S7
{
{4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1},
{13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6},
{1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2},
{6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12}
},
// S8
{
{13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7},
{1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2},
{7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8},
{2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11}
}
};
const int8_t PTable[32] = {
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, 4,
22, 11, 4, 25
};
const int8_t IP[2][64] = {
{
58, 50, 42, 34, 26, 18, 10, 2,
60, 52, 44, 36, 28, 20, 12, 4,
62, 54, 46, 38, 30, 22, 14, 6,
64, 56, 48, 40, 32, 24, 16, 8,
57, 49, 41, 33, 25, 17, 9, 1,
59, 51, 43, 35, 27, 16, 11, 3,
61, 53, 45, 37, 29, 21, 13, 5,
63, 55, 47, 39, 31, 23, 15, 7
},
{
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
}
};
void out_int8_t(int8_t* out, int len) {
for (int i = 0; i < len; i++) {
printf("%d", out[i]);
if ((i + 1) % 4 == 0) cout << " ";
}
cout << endl;
}
void fun_left_move(int8_t* C_0, int8_t* D_0, int times_left) {
for (int i = 0; i < times_left; i++) {
int8_t temp1 = C_0[0];
int8_t temp2 = D_0[0];
for (int j = 1; j < 28; j++) {
C_0[j - 1] = C_0[j];
D_0[j - 1] = D_0[j];
}
C_0[27] = temp1;
D_0[27] = temp2;
}
}
void Extend32To48(int8_t (*R) [32], int8_t (*RExtend) [48], int times) {
RExtend[times][0] = R[times][31];
RExtend[times][47] = R[times][0];
for (int i = 1; i < 47; i++) {
// 6個位一組, 即為一行
int LineNumber = (int)(i / 6);
RExtend[times][i] = R[times][i - 1 - LineNumber*2];
}
}
void XOR(int8_t (*RExtend) [48], int8_t (*k) [48], int8_t (*S) [48], int times) {
for (int i = 0; i < 48; i++) {
// 進行模二加運算
S[times][i] = (RExtend[times][i] + k[times][i]) % 2;
}
}
void XOR(int8_t (*L) [32], int8_t (*Pchanged) [32], int8_t (*R) [32], int times) {
for (int i = 0; i < 32; i++) {
R[times + 1][i] = (L[times][i] + Pchanged[times][i]) % 2;
}
}
void UseS48To32(int8_t (*S) [48], int8_t (*P) [32], int times) {
for (int i = 0; i < 8; i++) {
int line = 0, row = 0;
// 計算出行號及列號
line = line + S[times][i*6]*2 + S[times][5 + i*6];
row = row + S[times][1 + i*6]*8 + S[times][2 + i*6]*4 + S[times][3 + i*6]*2 + S[times][4 + i*6];
// 從S表中挑出答案
int8_t ans = S8[i][line][row];
for (int j = 0; j < 4; j++) {
int subtrahend = pow(2, (3 - j));
if (ans - subtrahend >= 0) {
P[times][j + i*4] = 1;
ans -= subtrahend;
}
else P[times][j + i*4] = 0;
}
}
}
void UsePChangeP(int8_t (*P) [32], int8_t (*PChanged) [32], int times) {
for (int i = 0; i < 32; i++) {
PChanged[times][i] = P[times][PTable[i] - 1];
}
}
void UseIPChange(int8_t* in, int8_t* out, int8_t choice) {
for (int i = 0; i < 64; i++) {
out[i] = in[IP[choice][i] - 1];
}
}
int main(void) {
// 對祕鑰進行處理
// 輸入為64位祕鑰, 輸出位16個32位子祕鑰
int8_t K_0[64], K_1[56], k[16][48];
// 祕鑰為"0x123456789abcdeff"
char in[] = "0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1111";
int len = strlen(in);
// 將輸入的祕鑰儲存到K_0中
for (int i = 0, j = 0; i < len; i++) {
if (in[i] == '0') K_0[j++] = 0;
else if (in[i] == '1') K_0[j++] = 1;
}
// 利用PC-0表, 從K_0中取出K_1
for (int i = 0; i < 56; i++) {
// 注意-1
K_1[i] = K_0[PC_1[i]-1];
}
// 上述完成了第一步利用PC-1表
// 接下來利用PC-2表
int8_t C_0[28], D_0[28];
// 將K_1拆分
for (int i = 0; i < 56; i++) {
if (i < 28) C_0[i] = K_1[i];
else D_0[i - 28] = K_1[i];
}
// 迴圈16次生成16個子祕鑰
for (int i = 0; i < 16; i++) {
int8_t times_left = left_move[i];
fun_left_move(C_0, D_0, times_left);
for (int j = 0; j < 48; j++) {
int8_t sub = PC_2[j] - 1;
if (sub < 28) k[i][j] = C_0[sub];
else k[i][j] = D_0[sub - 28];
}
// printf("子祕鑰k[%.2d]: ", i + 1);
// out_int8_t(k[i], 48);
}
// 對明文進行處理
// 明文為"0x0123456789abcdef"
char in_text[] = "0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111";
int8_t PlainText[64], IP_PlainText[64], EnchiperTextInitial[64], EnchiperText[64];
int8_t L[17][32], R[17][32], RExtend[16][48], S[16][48], P[16][32], PChanged[16][32];
int len_text = strlen(in_text);
// 64位明文儲存到PlainText中
for (int i = 0, j = 0; i < len; i++) {
if (in_text[i] == '0') PlainText[j++] = 0;
else if (in_text[i] == '1') PlainText[j++] = 1;
}
// 使用IP對PlainText進行變換
UseIPChange(PlainText, IP_PlainText, 0);
// 將PlainText分左右儲存到L_0及R_0中
for (int i = 0; i < 64; i++) {
if (i < 32) L[0][i] = IP_PlainText[i];
else R[0][i - 32] = IP_PlainText[i];
}
// 迴圈16次
for (int times = 0; times < 16; times++) {
// L_n = R_n-1
for (int i = 0; i < 32; i++) {
L[times + 1][i] = R[times][i];
}
// L_n = L_n-1 + f(R_n-1, k_n)
// 對R進行擴充套件
Extend32To48(R, RExtend, times);
// 將RExtend與k_n-1模二加
XOR(RExtend, k, S, times);
// 使用S表將S盒48位壓縮成32位
UseS48To32(S, P, times);
// 使用P表對P進行位置變換
UsePChangeP(P, PChanged, times);
// 生成的PChanged就是經過f之後的
// 模二加後放入R_n
XOR(L, PChanged, R, times);
}
for (int i = 0; i < 64; i++) {
if (i < 32) EnchiperTextInitial[i] = R[16][i];
else EnchiperTextInitial[i] = L[16][i - 32];
}
// 使用IP^-1對EnchiperTextInitial的進行變換得到Enchiper
UseIPChange(EnchiperTextInitial, EnchiperText, 1);
//最終結果輸出
out_int8_t(EnchiperText, 64);
return 0;
}