Java 中 Feistel 密码的小型实现
Small implementation of a Feistel Cipher in Java
我正在尝试执行 Feistel 密码的小型实现。这就是我一直在尝试的:
int[] left = {1,2,3};//left half of plaintext
int[] right = {4,5,6};//right half of plaintext
int temp[];//temp for swapping values
//encrypt the plaintext (left and right arrays)
for(int r = 0; r < 3; r++) {//the number of rounds
for(int i = 0; i < right.length; i++){
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
temp = left;
left = right;
right = temp;
}
//swap left and right array before decryption
temp = left;
left = right;
right = temp;
for(int r = 3; r > 0; r--) {//start from the last round
for(int i = 0; i < right.length; i++) {
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
//again, swap arrays to do the next round
temp = left;
left = right;
right = temp;
}
圆函数,scramble
为:
private static int scramble(int character, int key, int roundNumber) {
return (int) Math.pow(2 * roundNumber * key, character) % 15;
}
我试图首先加密明文的左右两半,然后 运行 通过解密轮 - 所以到最后,数组的值应该是 [1,2,3]和 [4,5,6](回到明文)。使用 8 的密钥输入,解密后我得到 [15, 13, 0] 和 [8, 12, 1] 的值。我哪里出错了?
为简单起见,我现在只是使用常量作为键以及整数输入,而不是从 file/using 字节数组中读取。
编辑:
循环计数不正确。将 "encryption loop" 更改为:
for(int r = 1; r < 4; r++) {//the number of rounds
for(int i = 0; i < right.length; i++){
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
temp = left;
left = right;
right = temp;
}
循环现在计算第 1、2、3 轮(加密)和第 3、2、1 轮(解密)。但是,解密仍然没有得到正确的明文。
您的圆形计数器不对称。
for(int r = 0; r < 3; r++)
计数:0、1、2。
for(int r = 3; r > 0; r--)
计数:3、2、1。
Feistel 的工作原理是将右侧的函数应用到左侧,即 left = left ^ F(right) 然后交换。这相当于 right2 = left1 ^ F(right1), left2 = right1 但该公式在 Java 没有的具有并行或解构赋值的语言中效果更好。请参阅 https://en.wikipedia.org/wiki/Feistel_cipher 处的图片。此外,您的代码组织在解密结束时进行了过多的交换。修复这两个问题:
static void SO40331050Feistel (){
final int KEY = 8;
int[] left = {1,2,3}, right = {4,5,6}, temp;
System.out.println ("=====WRONG=====");
for(int r = 1; r <= 3; r++) {
for(int i = 0; i < right.length; i++){
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
System.out.println ("ENC"+r +" "+Arrays.toString(left) +" "+Arrays.toString(right));
temp = left; left = right; right = temp;
}
temp = left; left = right; right = temp; // swap before decrypt
for(int r = 3; r >= 1; r--) {
for(int i = 0; i < right.length; i++) {
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
System.out.println ("DEC"+r + " "+Arrays.toString(left) +" "+Arrays.toString(right));
temp = left; left = right; right = temp;
}
left = new int[]{1,2,3}; right = new int[]{4,5,6}; // reset
System.out.println ("=====RIGHT=====");
for(int r = 1; r <= 3; r++) {
for(int i = 0; i < right.length; i++){
left[i] ^= (scramble(right[i], KEY, r));
}
System.out.println ("ENC"+r +" "+Arrays.toString(left) +" "+Arrays.toString(right));
temp = left; left = right; right = temp; // swap after
}
for(int r = 3; r >= 1; r--) {
temp = left; left = right; right = temp; // swap before on decrypt
for(int i = 0; i < right.length; i++) {
left[i] ^= (scramble(right[i], KEY, r));
}
System.out.println ("DEC"+r + " "+Arrays.toString(left) +" "+Arrays.toString(right));
}
}
结果:
=====WRONG=====
ENC1 [1, 2, 3] [0, 3, 2]
ENC2 [0, 3, 2] [2, 7, 10]
ENC3 [2, 7, 10] [3, 11, 3]
DEC3 [2, 7, 10] [14, 0, 6]
DEC2 [14, 0, 6] [10, 7, 1]
DEC1 [10, 7, 1] [13, 6, 0]
=====RIGHT=====
ENC1 [0, 3, 2] [4, 5, 6]
ENC2 [5, 13, 2] [0, 3, 2]
ENC3 [3, 4, 11] [5, 13, 2]
DEC3 [0, 3, 2] [5, 13, 2]
DEC2 [4, 5, 6] [0, 3, 2]
DEC1 [1, 2, 3] [4, 5, 6]
此外,F通常使用整个右半部分并产生适用于整个左半部分的结果;通过在 32 位 int 片段上单独执行,您实际上是 运行 三个独立的 32 位块密码并行,在 ECB 模式下有效。如果这是一个真正的密码,32 位块和 ECB 都是严重的弱点。
有时,如果将事物精简到最低限度,会更容易看清事物。这个伪代码最小 Feistel 密码可能有帮助:
function FeistelEncipher(plaintextBlock)
left <- left hand half of plaintextBlock
right <- right hand half of plaintextBlock
// Note the half-open interval.
for (roundNumber in [0 .. number of rounds[)
if (roundNumber != 0)
swap(left, right)
end if
right <- right XOR F(left, roundNumber)
end for
// Return ciphertext block.
return join(left, right)
end function
function F(data, roundNumber)
return some combination of the data and the round key for this round
end function
假定轮数为偶数,反向闭合“[”表示开放式区间。
我正在尝试执行 Feistel 密码的小型实现。这就是我一直在尝试的:
int[] left = {1,2,3};//left half of plaintext
int[] right = {4,5,6};//right half of plaintext
int temp[];//temp for swapping values
//encrypt the plaintext (left and right arrays)
for(int r = 0; r < 3; r++) {//the number of rounds
for(int i = 0; i < right.length; i++){
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
temp = left;
left = right;
right = temp;
}
//swap left and right array before decryption
temp = left;
left = right;
right = temp;
for(int r = 3; r > 0; r--) {//start from the last round
for(int i = 0; i < right.length; i++) {
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
//again, swap arrays to do the next round
temp = left;
left = right;
right = temp;
}
圆函数,scramble
为:
private static int scramble(int character, int key, int roundNumber) {
return (int) Math.pow(2 * roundNumber * key, character) % 15;
}
我试图首先加密明文的左右两半,然后 运行 通过解密轮 - 所以到最后,数组的值应该是 [1,2,3]和 [4,5,6](回到明文)。使用 8 的密钥输入,解密后我得到 [15, 13, 0] 和 [8, 12, 1] 的值。我哪里出错了?
为简单起见,我现在只是使用常量作为键以及整数输入,而不是从 file/using 字节数组中读取。
编辑:
循环计数不正确。将 "encryption loop" 更改为:
for(int r = 1; r < 4; r++) {//the number of rounds
for(int i = 0; i < right.length; i++){
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
temp = left;
left = right;
right = temp;
}
循环现在计算第 1、2、3 轮(加密)和第 3、2、1 轮(解密)。但是,解密仍然没有得到正确的明文。
您的圆形计数器不对称。
for(int r = 0; r < 3; r++)
计数:0、1、2。
for(int r = 3; r > 0; r--)
计数:3、2、1。
Feistel 的工作原理是将右侧的函数应用到左侧,即 left = left ^ F(right) 然后交换。这相当于 right2 = left1 ^ F(right1), left2 = right1 但该公式在 Java 没有的具有并行或解构赋值的语言中效果更好。请参阅 https://en.wikipedia.org/wiki/Feistel_cipher 处的图片。此外,您的代码组织在解密结束时进行了过多的交换。修复这两个问题:
static void SO40331050Feistel (){
final int KEY = 8;
int[] left = {1,2,3}, right = {4,5,6}, temp;
System.out.println ("=====WRONG=====");
for(int r = 1; r <= 3; r++) {
for(int i = 0; i < right.length; i++){
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
System.out.println ("ENC"+r +" "+Arrays.toString(left) +" "+Arrays.toString(right));
temp = left; left = right; right = temp;
}
temp = left; left = right; right = temp; // swap before decrypt
for(int r = 3; r >= 1; r--) {
for(int i = 0; i < right.length; i++) {
right[i] = left[i] ^ (scramble(right[i], KEY, r));
}
System.out.println ("DEC"+r + " "+Arrays.toString(left) +" "+Arrays.toString(right));
temp = left; left = right; right = temp;
}
left = new int[]{1,2,3}; right = new int[]{4,5,6}; // reset
System.out.println ("=====RIGHT=====");
for(int r = 1; r <= 3; r++) {
for(int i = 0; i < right.length; i++){
left[i] ^= (scramble(right[i], KEY, r));
}
System.out.println ("ENC"+r +" "+Arrays.toString(left) +" "+Arrays.toString(right));
temp = left; left = right; right = temp; // swap after
}
for(int r = 3; r >= 1; r--) {
temp = left; left = right; right = temp; // swap before on decrypt
for(int i = 0; i < right.length; i++) {
left[i] ^= (scramble(right[i], KEY, r));
}
System.out.println ("DEC"+r + " "+Arrays.toString(left) +" "+Arrays.toString(right));
}
}
结果:
=====WRONG=====
ENC1 [1, 2, 3] [0, 3, 2]
ENC2 [0, 3, 2] [2, 7, 10]
ENC3 [2, 7, 10] [3, 11, 3]
DEC3 [2, 7, 10] [14, 0, 6]
DEC2 [14, 0, 6] [10, 7, 1]
DEC1 [10, 7, 1] [13, 6, 0]
=====RIGHT=====
ENC1 [0, 3, 2] [4, 5, 6]
ENC2 [5, 13, 2] [0, 3, 2]
ENC3 [3, 4, 11] [5, 13, 2]
DEC3 [0, 3, 2] [5, 13, 2]
DEC2 [4, 5, 6] [0, 3, 2]
DEC1 [1, 2, 3] [4, 5, 6]
此外,F通常使用整个右半部分并产生适用于整个左半部分的结果;通过在 32 位 int 片段上单独执行,您实际上是 运行 三个独立的 32 位块密码并行,在 ECB 模式下有效。如果这是一个真正的密码,32 位块和 ECB 都是严重的弱点。
有时,如果将事物精简到最低限度,会更容易看清事物。这个伪代码最小 Feistel 密码可能有帮助:
function FeistelEncipher(plaintextBlock)
left <- left hand half of plaintextBlock
right <- right hand half of plaintextBlock
// Note the half-open interval.
for (roundNumber in [0 .. number of rounds[)
if (roundNumber != 0)
swap(left, right)
end if
right <- right XOR F(left, roundNumber)
end for
// Return ciphertext block.
return join(left, right)
end function
function F(data, roundNumber)
return some combination of the data and the round key for this round
end function
假定轮数为偶数,反向闭合“[”表示开放式区间。