kry-ctr-spn/src/main/java/ch/fhnw/kry/SPN.java
Sebastian Hugentobler e1c2d7df7d
Fix CTR decryption.
Off by one error in respect to the y blocks.
2022-03-22 21:07:04 +01:00

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