2

I'm trying to do a small implementation of a Feistel Cipher. This is what I've been trying:

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;
}

The round function, scramble is:

private static int scramble(int character, int key, int roundNumber) {
    return (int) Math.pow(2 * roundNumber * key, character) % 15;
}

I am attempting to first encrypt the left and right halves of the plaintext, and then run it through the decryption rounds - so by the end, the array's values should be [1,2,3] and [4,5,6] (back to the plaintext). Using a key input of 8, after decryption I'm getting values of [15, 13, 0] and [8, 12, 1]. Where am I going wrong with this?

For simplicity I'm just using a constant as the key right now as well as input of integers as opposed to reading from a file/using byte arrays.

edit:

counting for the loops was incorrect. Changed the "encryption loop" to:

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;
}

The loops now count rounds 1,2,3 (encryption) and 3,2,1 (decryption). However, the decryption still isn't resulting in the correct plaintext.

ChristianF
  • 1,735
  • 4
  • 28
  • 56

3 Answers3

3

Sometimes it is easier to see things if they are stripped down to the minimum. This pseudocode minimal Feistel cipher may help:

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

An even number of rounds is assumed, and the reversed closing '[' indicates an open ended interval.

rossum
  • 15,344
  • 1
  • 24
  • 38
2

Your round counters aren't symmetric.

for(int r = 0; r < 3; r++)

counts: 0, 1, 2.

for(int r = 3; r > 0; r--)

counts: 3, 2, 1.

2

Feistel works by applying a function of the right side TO the left side, i.e. left = left ^ F(right) then swap. This is equivalent to right2 = left1 ^ F(right1), left2 = right1 but that formulation works better in languages with parallel or destructuring assignment which Java doesn't have. See the picture at https://en.wikipedia.org/wiki/Feistel_cipher . In addition your code organization does one too many swap at the end of decrypt. Fixing both of those:

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));
    }
}

RESULTS:

=====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]

Also, it is usual for F to use the whole right half and produce a result that applies to the whole left half; by doing it separately on 32-bit int pieces you are actually running three independent 32-bit block ciphers in parallel, effectively in ECB mode. Both 32-bit block and ECB would be serious weaknesses if this were a real cipher.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70