Last active
December 23, 2015 09:39
-
-
Save jonathanedgecombe/6616264 to your computer and use it in GitHub Desktop.
EBC (Block Cipher) Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.jonathanedgecombe.security; | |
import java.security.InvalidKeyException; | |
import java.security.InvalidParameterException; | |
import javax.crypto.IllegalBlockSizeException; | |
/** | |
* EBC (Edgecombe Block Cipher) | |
* A block cipher making use of substitution, permutation and modular arithmetic. | |
* @author Jonathan Edgecombe | |
*/ | |
public final class EBC { | |
/** | |
* Constant hard-coded 1-to-1 substitution table for encrypting. | |
*/ | |
private final static byte[] SUBSTITUTION_TABLE_ENCRYPT = new byte[] { | |
0x76, 0x79, 0x70, -0x3A, -0x13, 0x5B, -0x1E, -0x6A, 0x54, -0x31, -0x34, -0x5D, -0x18, 0x29, -0x04, -0x48, | |
0x6B, 0x3E, -0x40, 0x36, 0x23, 0x1A, 0x19, -0x44, 0x78, 0x5A, -0x6B, 0x60, -0x6F, 0x32, -0x60, -0x4E, | |
-0x4F, 0x46, 0x48, 0x33, -0x0E, 0x14, -0x05, -0x65, 0x62, 0x7F, -0x5F, 0x5D, -0x02, 0x45, -0x57, 0x2D, | |
0x4B, -0x3B, -0x01, 0x5E, 0x17, 0x69, 0x44, -0x27, 0x1C, 0x55, -0x80, -0x62, 0x74, -0x50, 0x4C, -0x72, | |
0x00, -0x06, 0x1B, 0x24, -0x2F, 0x6A, -0x7B, 0x35, 0x07, -0x66, -0x0F, 0x03, 0x2F, -0x55, 0x4A, 0x3A, | |
0x15, 0x16, -0x32, 0x59, 0x34, 0x0D, 0x30, 0x75, -0x20, -0x47, -0x42, 0x37, 0x77, -0x52, 0x58, 0x51, | |
0x1D, -0x4A, -0x74, 0x49, 0x26, -0x1F, 0x3B, 0x38, -0x37, -0x1C, -0x69, -0x0A, -0x21, 0x08, -0x0B, -0x38, | |
-0x10, -0x5E, 0x61, 0x28, -0x56, -0x79, 0x1E, -0x15, 0x64, 0x71, -0x17, -0x70, 0x20, 0x50, 0x6E, 0x02, | |
-0x16, 0x27, -0x1A, -0x54, -0x7A, 0x53, -0x19, -0x7F, -0x71, 0x3C, -0x2C, -0x2B, 0x31, -0x11, 0x22, -0x3F, | |
-0x77, 0x09, 0x6F, -0x63, 0x0A, 0x18, 0x6C, 0x4E, -0x7D, -0x1B, -0x39, 0x6D, -0x6D, -0x6C, -0x2E, -0x67, | |
0x05, 0x7D, -0x75, -0x78, 0x3D, -0x12, -0x6E, 0x2E, -0x53, -0x36, 0x13, 0x7C, -0x45, -0x24, 0x01, -0x5B, | |
0x41, -0x76, 0x42, 0x52, 0x10, 0x66, -0x35, -0x1D, -0x30, -0x26, -0x4B, -0x49, -0x7E, -0x25, 0x06, -0x09, | |
0x73, 0x7A, -0x29, 0x7B, -0x22, 0x0B, -0x46, 0x40, -0x4D, -0x33, 0x25, -0x3D, 0x12, -0x28, 0x1F, -0x43, | |
0x67, -0x3C, 0x43, -0x2A, -0x3E, -0x59, -0x7C, -0x68, -0x2D, -0x5C, 0x3F, -0x0D, 0x7E, -0x51, 0x2C, 0x0F, | |
-0x4C, -0x41, -0x61, 0x68, -0x03, -0x73, 0x5C, -0x0C, 0x2B, -0x64, -0x23, 0x0C, 0x5F, 0x57, -0x08, -0x58, | |
0x04, 0x4D, 0x4F, -0x07, 0x63, -0x14, 0x56, 0x65, 0x72, 0x21, 0x47, 0x0E, 0x11, 0x39, 0x2A, -0x5A | |
}; | |
/** | |
* Constant hard-coded 1-to-1 substitution table for decrypting. | |
*/ | |
private final static byte[] SUBSTITUTION_TABLE_DECRYPT = new byte[] { | |
0x40, -0x52, 0x7F, 0x4B, -0x10, -0x60, -0x42, 0x48, 0x6D, -0x6F, -0x6C, -0x3B, -0x15, 0x55, -0x05, -0x21, | |
-0x4C, -0x04, -0x34, -0x56, 0x25, 0x50, 0x51, 0x34, -0x6B, 0x16, 0x15, 0x42, 0x38, 0x60, 0x76, -0x32, | |
0x7C, -0x07, -0x72, 0x14, 0x43, -0x36, 0x64, -0x7F, 0x73, 0x0D, -0x02, -0x18, -0x22, 0x2F, -0x59, 0x4C, | |
0x56, -0x74, 0x1D, 0x23, 0x54, 0x47, 0x13, 0x5B, 0x67, -0x03, 0x4F, 0x66, -0x77, -0x5C, 0x11, -0x26, | |
-0x39, -0x50, -0x4E, -0x2E, 0x36, 0x2D, 0x21, -0x06, 0x22, 0x63, 0x4E, 0x30, 0x3E, -0x0F, -0x69, -0x0E, | |
0x7D, 0x5F, -0x4D, -0x7B, 0x08, 0x39, -0x0A, -0x13, 0x5E, 0x53, 0x19, 0x05, -0x1A, 0x2B, 0x33, -0x14, | |
0x1B, 0x72, 0x28, -0x0C, 0x78, -0x09, -0x4B, -0x30, -0x1D, 0x35, 0x45, 0x10, -0x6A, -0x65, 0x7E, -0x6E, | |
0x02, 0x79, -0x08, -0x40, 0x3C, 0x57, 0x00, 0x5C, 0x18, 0x01, -0x3F, -0x3D, -0x55, -0x5F, -0x24, 0x29, | |
0x3A, -0x79, -0x44, -0x68, -0x2A, 0x46, -0x7C, 0x75, -0x5D, -0x70, -0x4F, -0x5E, 0x62, -0x1B, 0x3F, -0x78, | |
0x7B, 0x1C, -0x5A, -0x64, -0x63, 0x1A, 0x07, 0x6A, -0x29, -0x61, 0x49, 0x27, -0x17, -0x6D, 0x3B, -0x1E, | |
0x1E, 0x2A, 0x71, 0x0B, -0x27, -0x51, -0x01, -0x2B, -0x11, 0x2E, 0x74, 0x4D, -0x7D, -0x58, 0x5D, -0x23, | |
0x3D, 0x20, 0x1F, -0x38, -0x20, -0x46, 0x61, -0x45, 0x0F, 0x59, -0x3A, -0x54, 0x17, -0x31, 0x5A, -0x1F, | |
0x12, -0x71, -0x2C, -0x35, -0x2F, 0x31, 0x03, -0x66, 0x6F, 0x68, -0x57, -0x4A, 0x0A, -0x37, 0x52, 0x09, | |
-0x48, 0x44, -0x62, -0x28, -0x76, -0x75, -0x2D, -0x3E, -0x33, 0x37, -0x47, -0x43, -0x53, -0x16, -0x3C, 0x6C, | |
0x58, 0x65, 0x06, -0x49, 0x69, -0x67, -0x7E, -0x7A, 0x0C, 0x7A, -0x80, 0x77, -0x0B, 0x04, -0x5B, -0x73, | |
0x70, 0x4A, 0x24, -0x25, -0x19, 0x6E, 0x6B, -0x41, -0x12, -0x0D, 0x41, 0x26, 0x0E, -0x1C, 0x2C, 0x32 | |
}; | |
/** | |
* The number of rounds to apply when encrypting or decrypting a block. | |
*/ | |
private final int rounds; | |
/** | |
* The key to use when encrypting or decrypting a block. | |
*/ | |
private final byte[] key; | |
/** | |
* Create a new block cipher with the specified key and number of rounds. | |
* @param key The key for the cipher to use. | |
* @param rounds The rounds of encryption to use (minimum of 4). | |
* @throws InvalidKeyException If the key is not valid (non-zero length and multiple of 16 bytes [128 bits] length). | |
*/ | |
public EBC(byte[] key, int rounds) throws InvalidKeyException { | |
if (!checkKey(key)) throw new InvalidKeyException("Key must be non-zero and a multiple of 16 bytes (128 bits) in length"); | |
if (rounds < 4) throw new InvalidParameterException("Rounds must be greater than or equal to 4."); | |
this.rounds = rounds; | |
this.key = key; | |
} | |
/** | |
* Create a new block cipher with the specified key and default (8) number of rounds. | |
* @param key The key for the cipher to use. | |
* @throws InvalidKeyException If the key is not valid (non-zero length and multiple of 16 bytes [128 bits] length). | |
*/ | |
public EBC(byte[] key) throws InvalidKeyException { | |
this(key, 8); | |
} | |
/** | |
* Encrypt a block of data. | |
* @param block The block to encrypt. | |
* @return The encrypted data. | |
* @throws IllegalBlockSizeException If the block size is not 16 bytes (128 bits). | |
*/ | |
public byte[] encrypt(byte[] block) throws IllegalBlockSizeException { | |
if (block.length != 16) throw new IllegalBlockSizeException("Only block sizes of 16 bytes (128 bits) are permitted"); | |
byte[] data1 = new byte[8]; | |
byte[] data2 = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
data1[i] = block[i]; | |
data2[i] = block[8 + i]; | |
} | |
for (int round = 0; round < rounds; round++) { | |
data1 = xor(encryptRound(data1, key), data2); | |
data2 = xor(encryptRound(data2, key), data1); | |
} | |
byte[] out = new byte[16]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = data1[i]; | |
out[8 + i] = data2[i]; | |
} | |
return out; | |
} | |
/** | |
* Decrypt a block of data. | |
* @param block The block to decrypt. | |
* @return The decrypted data. | |
* @throws IllegalBlockSizeException If the block size is not 16 bytes (128 bits). | |
*/ | |
public byte[] decrypt(byte[] block) throws IllegalBlockSizeException { | |
if (block.length != 16) throw new IllegalBlockSizeException("Only block sizes of 16 bytes (128 bits) are permitted"); | |
byte[] data1 = new byte[8]; | |
byte[] data2 = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
data1[i] = block[i]; | |
data2[i] = block[8 + i]; | |
} | |
for (int round = 0; round < rounds; round++) { | |
data2 = decryptRound(xor(data2, data1), key); | |
data1 = decryptRound(xor(data1, data2), key); | |
} | |
byte[] out = new byte[16]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = data1[i]; | |
out[8 + i] = data2[i]; | |
} | |
return out; | |
} | |
/** | |
* XOR two byte arrays together sequentially. | |
* @param a Byte array a. | |
* @param b Byte array b. | |
* @return XOR of a and b. | |
*/ | |
private static byte[] xor(byte[] a, byte[] b) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = (byte) (a[i] ^ b[i]); | |
} | |
return out; | |
} | |
/** | |
* Checks if a key is valid (non-zero length and multiple of 128 bits length). | |
* @param key The key to check. | |
* @return If the key is valid or not. | |
*/ | |
private static boolean checkKey(byte[] key) { | |
if (key.length == 0) return false; | |
if (key.length%16 != 0) return false; | |
return true; | |
} | |
/** | |
* Key sheduler. Makes use of the substitution and permutation ciphers. | |
* @param key The key to use. | |
* @param keyRound The round of the sub key. | |
* @return The sub key generated. | |
*/ | |
private static byte[] getSubKey(byte[] key, int keyRound) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = key[keyRound*8 + i]; | |
} | |
out = substituteEncrypt(out); | |
out = permutateEncrypt(out); | |
return out; | |
} | |
/** | |
* A method for one round of encryption on data. | |
* @param data The data to encrypt. | |
* @param key The key to use. | |
* @return The encrypted data. | |
*/ | |
private static byte[] encryptRound(byte[] data, byte[] key) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < key.length/16; i++) { | |
// Substitution | |
out = substituteEncrypt(data); | |
// Permutation | |
out = permutateEncrypt(out); | |
// Key arithmetic | |
out = addKeyEncrypt(out, getSubKey(key, i*2)); | |
out = xorKeyEncrypt(out, getSubKey(key, i*2 + 1)); | |
} | |
return out; | |
} | |
/** | |
* A method for one round of decryption on data. | |
* @param data The data to decrypt. | |
* @param key The key to use. | |
* @return The decrypted data. | |
*/ | |
private static byte[] decryptRound(byte[] data, byte[] key) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = data[i]; | |
} | |
for (int i = 0; i < key.length/16; i++) { | |
// Key arithmetic | |
out = xorKeyEncrypt(out, getSubKey(key, i*2 + 1)); | |
out = subKeyEncrypt(out, getSubKey(key, i*2)); | |
// Permutation | |
out = permutateDecrypt(out); | |
// Substitution | |
out = substituteDecrypt(out); | |
} | |
return out; | |
} | |
/** | |
* Applies the sub key to the block using a bitwise XOR. | |
* @param data The data to encrypt. | |
* @param subKey The sub key to use. | |
* @return The encrypted data. | |
*/ | |
private static byte[] xorKeyEncrypt(byte[] data, byte[] subKey) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = (byte) (data[i] ^ subKey[i]); | |
} | |
return out; | |
} | |
/** | |
* Applies the sub key to the block using a bytewise add. | |
* @param data The data to encrypt. | |
* @param subKey The sub key to use. | |
* @return The encrypted data. | |
*/ | |
private static byte[] addKeyEncrypt(byte[] data, byte[] subKey) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = (byte) (data[i] + subKey[i]); | |
} | |
return out; | |
} | |
/** | |
* Applies the sub key to the block using a bytewise subtract. | |
* @param data The data to encrypt. | |
* @param subKey The sub key to use. | |
* @return The encrypted data. | |
*/ | |
private static byte[] subKeyEncrypt(byte[] data, byte[] subKey) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = (byte) (data[i] - subKey[i]); | |
} | |
return out; | |
} | |
/** | |
* Applies a substitution encryption to the block. | |
* @param data The data to substitute. | |
* @return The substituted data. | |
*/ | |
private static byte[] substituteEncrypt(byte[] data) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = SUBSTITUTION_TABLE_ENCRYPT[data[i] & 0xFF]; | |
} | |
return out; | |
} | |
/** | |
* Applies a substitution decryption to the block. | |
* @param data The data to decrypt. | |
* @return The decrypted data. | |
*/ | |
private static byte[] substituteDecrypt(byte[] data) { | |
byte[] out = new byte[8]; | |
for (int i = 0; i < 8; i++) { | |
out[i] = SUBSTITUTION_TABLE_DECRYPT[data[i] & 0xFF]; | |
} | |
return out; | |
} | |
/** | |
* Applies a permutation encryption to the block. | |
* @param data The data to permutate. | |
* @return The permutated data. | |
*/ | |
private static byte[] permutateEncrypt(byte[] data) { | |
byte[] out = new byte[8]; | |
out[0] = (byte) (data[1] ^ data[2] ^ data[3] ^ data[4] ^ data[5] ^ data[6] ^ data[7]); | |
out[1] = (byte) (data[0] ^ data[2] ^ data[3] ^ data[4] ^ data[5] ^ data[6] ^ data[7]); | |
out[2] = (byte) (data[0] ^ data[1] ^ data[3] ^ data[4] ^ data[5] ^ data[6] ^ data[7]); | |
out[3] = (byte) (data[0] ^ data[1] ^ data[2] ^ data[4] ^ data[5] ^ data[6] ^ data[7]); | |
out[4] = (byte) (data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[5] ^ data[6] ^ data[7]); | |
out[5] = (byte) (data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4] ^ data[6] ^ data[7]); | |
out[6] = (byte) (data[0] ^ data[1] ^ data[2] ^ data[3] ^ data[4] ^ data[5] ^ data[7]); | |
out[7] = (byte) (data[0] + out[0] + out[1] + out[2] + out[3] + out[4] + out[5] + out[6]); | |
return out; | |
} | |
/** | |
* Applies a permutation decryption to the block. | |
* @param data The data to decrypt. | |
* @return The decrypted data. | |
*/ | |
private static byte[] permutateDecrypt(byte[] data) { | |
byte[] out = new byte[8]; | |
out[0] = (byte) (data[7] - data[0] - data[1] - data[2] - data[3] - data[4] - data[5] - data[6]); | |
out[1] = (byte) (data[0] ^ data[1] ^ out[0]); | |
out[2] = (byte) (data[1] ^ data[2] ^ out[1]); | |
out[3] = (byte) (data[2] ^ data[3] ^ out[2]); | |
out[4] = (byte) (data[3] ^ data[4] ^ out[3]); | |
out[5] = (byte) (data[4] ^ data[5] ^ out[4]); | |
out[6] = (byte) (data[5] ^ data[6] ^ out[5]); | |
out[7] = (byte) (out[1] ^ out[2] ^ out[3] ^ out[4] ^ out[5] ^ out[6] ^ data[0]); | |
return out; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment