205 lines
6.0 KiB
Java
205 lines
6.0 KiB
Java
package ch.fhnw.kry;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import static ch.fhnw.kry.Main.BLOCK_LENGTH;
|
|
import static java.util.Map.entry;
|
|
|
|
public class SPN {
|
|
public static final Map<Integer, Integer> SBOX = Map.ofEntries(
|
|
entry(0, 0xE),
|
|
entry(1, 4),
|
|
entry(2, 0xD),
|
|
entry(3, 1),
|
|
entry(4, 2),
|
|
entry(5, 0xF),
|
|
entry(6, 0xB),
|
|
entry(7, 8),
|
|
entry(8, 3),
|
|
entry(9, 0xA),
|
|
entry(0xA, 6),
|
|
entry(0xB, 0xC),
|
|
entry(0xC, 5),
|
|
entry(0xD, 9),
|
|
entry(0xE, 0),
|
|
entry(0xF, 7)
|
|
);
|
|
public static final Map<Integer, Integer> SBOX_REVERSE = Map.ofEntries(
|
|
entry(0xE, 0),
|
|
entry(4, 1),
|
|
entry(0xD, 2),
|
|
entry(1, 3),
|
|
entry(2, 4),
|
|
entry(0xF, 5),
|
|
entry(0xB, 6),
|
|
entry(8, 7),
|
|
entry(3, 8),
|
|
entry(0xA, 9),
|
|
entry(6, 0xA),
|
|
entry(0xC, 0xB),
|
|
entry(5, 0xC),
|
|
entry(9, 0xD),
|
|
entry(0, 0xE),
|
|
entry(7, 0xF)
|
|
);
|
|
private final static int ROUND_KEY_LENGTH = 16;
|
|
private static final int ROUNDS = 4;
|
|
private final Map<Integer, Integer> PERMUTATION = Map.ofEntries(
|
|
entry(0, 0),
|
|
entry(1, 4),
|
|
entry(2, 8),
|
|
entry(3, 12),
|
|
entry(4, 1),
|
|
entry(5, 5),
|
|
entry(6, 9),
|
|
entry(7, 13),
|
|
entry(8, 2),
|
|
entry(9, 6),
|
|
entry(10, 10),
|
|
entry(11, 14),
|
|
entry(12, 3),
|
|
entry(13, 7),
|
|
entry(14, 11),
|
|
entry(15, 15)
|
|
);
|
|
|
|
/**
|
|
* Encrypt a block with the defined SPN.
|
|
*
|
|
* @param key Key for encryption (round keys get derived from this).
|
|
* @param x Input block (high 16 bits get ignored).
|
|
* @return Encrypted block.
|
|
*/
|
|
public int encryptBlock(int key, int x) {
|
|
x = init(key, x, 0); // initialer Weisschritt
|
|
|
|
for (int i = 1; i < ROUNDS; i++) { // for rounds > 0 && < r
|
|
x = substitution(x, SBOX); // run through s-box
|
|
x = permutation(x); // permutate
|
|
x ^= k(key, i); // xor with derived round key
|
|
}
|
|
|
|
x = substitution(x, SBOX); // run through s-box
|
|
x ^= k(key, ROUNDS); // xor with last round key
|
|
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Decrypt a block with the defined SPN.
|
|
*
|
|
* @param key Key for decryption (round keys get derived from this).
|
|
* @param x Encrypted block (high 16 bits get ignored).
|
|
* @return Decrypted block.
|
|
*/
|
|
public int decryptBlock(int key, int x) {
|
|
x = init(key, x, ROUNDS); // initialer Weisschritt
|
|
|
|
for (int i = ROUNDS - 1; i > 0; i--) { // for rounds > 0 && < r, going in reverse
|
|
x = substitution(x, SBOX_REVERSE); // run through reverse s-box
|
|
x = permutation(x); // permutate
|
|
x ^= permutation(k(key, i)); // xor with permutated derived round key
|
|
}
|
|
|
|
x = substitution(x, SBOX_REVERSE); // run through reverse s-box
|
|
x ^= k(key, 0); // xor with round key 0
|
|
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Derive a round key from a key by multiplying
|
|
* the round number with four and taking the next 16
|
|
* bits from that position in the key.
|
|
*
|
|
* @param key Key as input.
|
|
* @param i Round number (starts at 0).
|
|
* @return Derived key.
|
|
*/
|
|
public int k(int key, int i) {
|
|
i *= ROUNDS;
|
|
int mask = 0xFFFF_0000 >>> i;
|
|
|
|
return (key & mask) >>> ROUND_KEY_LENGTH - i;
|
|
}
|
|
|
|
/**
|
|
* Initialer Weisschritt.
|
|
* <p>
|
|
* XOR an input with the key of a specific round.
|
|
*
|
|
* @param key Key to derive the round key.
|
|
* @param x Input.
|
|
* @param r Round number (starts at 0).
|
|
* @return Input xored with the derived round key.
|
|
*/
|
|
public int init(int key, int x, int r) {
|
|
return x ^ k(key, r);
|
|
}
|
|
|
|
/**
|
|
* Permutate an input by swapping all its lower 16 bits as defined
|
|
* by a map.
|
|
*
|
|
* @param x Initial state (the high 16 bits of the integer are ignored).
|
|
* @return End state, after running all permutations.
|
|
*/
|
|
public int permutation(int x) {
|
|
Map<Integer, Boolean> blocked = new HashMap<>();
|
|
|
|
for (int i = 0; i < BLOCK_LENGTH; i++) {
|
|
int target = PERMUTATION.get(i);
|
|
if (!blocked.containsKey(target)) {
|
|
x = swapBits(x, i, target);
|
|
blocked.put(i, true);
|
|
}
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Substitute every four bits of an input by running them through an S-Box.
|
|
*
|
|
* @param x Initial state (the high 16 bits of the integer are ignored).
|
|
* @param sbox A mapping consisting of hex keys and values.
|
|
* @return End state, after running all substitutions.
|
|
*/
|
|
public int substitution(int x, Map<Integer, Integer> sbox) {
|
|
int mask = 0xF000;
|
|
for (int i = 0; i < 4; i++) {
|
|
int j = 4 * i;
|
|
int key = (x & mask) >>> 12 - j;
|
|
int val = sbox.get(key);
|
|
int replMask = ~(0xf << 12 - j); // Maske mit 4 0-Bits vorbereiten zur Vorbereitung vom Einfügen
|
|
x = x & replMask | (val << 12 - j); // x an der Stelle der 4 0-Bits mit val überschreiben
|
|
|
|
mask = mask >>> 4; // maske um 4 Bit für nächste Iteration verschieben
|
|
}
|
|
return x;
|
|
}
|
|
|
|
/**
|
|
* Swap two bits.
|
|
* <p>
|
|
* Because we work with blocks of 16 bits, the high bits of the int input x are ignored.
|
|
*
|
|
* @param x Value where the bits get swapped (high 16 bits get ignored).
|
|
* @param a Position of the first bit to swap.
|
|
* @param b Position of the second bit to swap.
|
|
* @return Input value with bits in position a and b swapped.
|
|
*/
|
|
public int swapBits(int x, int a, int b) {
|
|
// calculate the position respective to the 32 bits, hence ignoring the high 16 bits of x
|
|
a = 15 - a;
|
|
b = 15 - b;
|
|
|
|
if ((((x & 1 << a) >> a) ^ ((x & 1 << b) >> b)) == 1) { // only swap if the positions are not the same
|
|
x ^= (1 << a);
|
|
x ^= (1 << b);
|
|
}
|
|
|
|
return x;
|
|
}
|
|
}
|