package ch.fhnw.kry; import java.nio.charset.StandardCharsets; import static ch.fhnw.kry.Main.BLOCK_LENGTH; /** * Decrypt cipher text with CTR and SPN. *

* Some implemented functions are not as flexible as could be, * as everything basically assumes a block length of 16 bits. * For this learning exercise, this is satisfactory though. */ public class Decrypt { /** * Decrypt a cipher text with a key, using CTR and an SPN. *

* The decrypted data is interpreted as ASCII code. * * @param keyString Key as a bit string. * @param cipher Cipher text as a bit string. * @return Decrypted data, interpreted as an ASCII string. */ public String decrypt(String keyString, String cipher) { int key = Integer.parseInt(keyString, 2); // get the key int[] data = strToArray(cipher); byte[] decryptedData = new byte[data.length * 2]; // as the block size is 16 bits, the byte array needs double the capacity int iv = data[0]; // first block is the iv var ctr = new CTR(iv, key); for (int i = 0; i < data.length - 1; i++) { int block = data[i]; int decryptedBlock = ctr.decrypt(block, i, data[i + 1]); // y is i + 1 because i = 0 is the iv decryptedData[i * 2] = (byte) (decryptedBlock >>> 8); // get the upper half of the decrypted block decryptedData[i * 2 + 1] = (byte) (decryptedBlock & 0xFF); // and the lower half } decryptedData = removePadding(decryptedData); return new String(decryptedData, StandardCharsets.US_ASCII); } /** * Remove the padding of data by traversing it * backwards until a byte != 0 s found and lop it off behind * the found position. * * @param data Data with padding. * @return Input data without the padding. */ public byte[] removePadding(byte[] data) { int idx = data.length - 1; while (idx > 0 && data[idx] == 0) { idx--; } byte[] unpaddedData = new byte[idx]; System.arraycopy(data, 0, unpaddedData, 0, idx); return unpaddedData; } /** * Convert a bit string into an integer array. *

* Because later we only ever look at the lower 16 bits, * we stuff every block of 16 bits into the lower half of an int * (meaning the igh 16 bits of the ints in the array are always 0). * * @param bits Bit string. Its length must be a multiple of 16. * @return int array, with every int's lower 16 bits set to 16 bits of the input string. */ public int[] strToArray(String bits) { int[] data = new int[bits.length() / BLOCK_LENGTH]; for (int i = 0; i < data.length; i++) { int startIdx = i * BLOCK_LENGTH; String wordBits = bits.substring(startIdx, startIdx + BLOCK_LENGTH); int word = Integer.parseInt(wordBits, 2); data[i] = word; } return data; } }