package com.thealgorithms.ciphers;
import java.math.BigInteger;
import java.util.Scanner;
/**
* This class is build to demonstrate the application of the AES-algorithm on a
* single 128-Bit block of data.
*/
public class AES {
/**
* Precalculated values for x to the power of 2 in Rijndaels galois field.
* Used as 'RCON' during the key expansion.
*/
private static final int[] RCON = {
0x8d,
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0x1b,
0x36,
0x6c,
0xd8,
0xab,
0x4d,
0x9a,
0x2f,
0x5e,
0xbc,
0x63,
0xc6,
0x97,
0x35,
0x6a,
0xd4,
0xb3,
0x7d,
0xfa,
0xef,
0xc5,
0x91,
0x39,
0x72,
0xe4,
0xd3,
0xbd,
0x61,
0xc2,
0x9f,
0x25,
0x4a,
0x94,
0x33,
0x66,
0xcc,
0x83,
0x1d,
0x3a,
0x74,
0xe8,
0xcb,
0x8d,
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0x1b,
0x36,
0x6c,
0xd8,
0xab,
0x4d,
0x9a,
0x2f,
0x5e,
0xbc,
0x63,
0xc6,
0x97,
0x35,
0x6a,
0xd4,
0xb3,
0x7d,
0xfa,
0xef,
0xc5,
0x91,
0x39,
0x72,
0xe4,
0xd3,
0xbd,
0x61,
0xc2,
0x9f,
0x25,
0x4a,
0x94,
0x33,
0x66,
0xcc,
0x83,
0x1d,
0x3a,
0x74,
0xe8,
0xcb,
0x8d,
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0x1b,
0x36,
0x6c,
0xd8,
0xab,
0x4d,
0x9a,
0x2f,
0x5e,
0xbc,
0x63,
0xc6,
0x97,
0x35,
0x6a,
0xd4,
0xb3,
0x7d,
0xfa,
0xef,
0xc5,
0x91,
0x39,
0x72,
0xe4,
0xd3,
0xbd,
0x61,
0xc2,
0x9f,
0x25,
0x4a,
0x94,
0x33,
0x66,
0xcc,
0x83,
0x1d,
0x3a,
0x74,
0xe8,
0xcb,
0x8d,
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0x1b,
0x36,
0x6c,
0xd8,
0xab,
0x4d,
0x9a,
0x2f,
0x5e,
0xbc,
0x63,
0xc6,
0x97,
0x35,
0x6a,
0xd4,
0xb3,
0x7d,
0xfa,
0xef,
0xc5,
0x91,
0x39,
0x72,
0xe4,
0xd3,
0xbd,
0x61,
0xc2,
0x9f,
0x25,
0x4a,
0x94,
0x33,
0x66,
0xcc,
0x83,
0x1d,
0x3a,
0x74,
0xe8,
0xcb,
0x8d,
0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80,
0x1b,
0x36,
0x6c,
0xd8,
0xab,
0x4d,
0x9a,
0x2f,
0x5e,
0xbc,
0x63,
0xc6,
0x97,
0x35,
0x6a,
0xd4,
0xb3,
0x7d,
0xfa,
0xef,
0xc5,
0x91,
0x39,
0x72,
0xe4,
0xd3,
0xbd,
0x61,
0xc2,
0x9f,
0x25,
0x4a,
0x94,
0x33,
0x66,
0xcc,
0x83,
0x1d,
0x3a,
0x74,
0xe8,
0xcb,
0x8d,
};
/**
* Rijndael S-box Substitution table used for encryption in the subBytes
* step, as well as the key expansion.
*/
private static final int[] SBOX = {
0x63,
0x7C,
0x77,
0x7B,
0xF2,
0x6B,
0x6F,
0xC5,
0x30,
0x01,
0x67,
0x2B,
0xFE,
0xD7,
0xAB,
0x76,
0xCA,
0x82,
0xC9,
0x7D,
0xFA,
0x59,
0x47,
0xF0,
0xAD,
0xD4,
0xA2,
0xAF,
0x9C,
0xA4,
0x72,
0xC0,
0xB7,
0xFD,
0x93,
0x26,
0x36,
0x3F,
0xF7,
0xCC,
0x34,
0xA5,
0xE5,
0xF1,
0x71,
0xD8,
0x31,
0x15,
0x04,
0xC7,
0x23,
0xC3,
0x18,
0x96,
0x05,
0x9A,
0x07,
0x12,
0x80,
0xE2,
0xEB,
0x27,
0xB2,
0x75,
0x09,
0x83,
0x2C,
0x1A,
0x1B,
0x6E,
0x5A,
0xA0,
0x52,
0x3B,
0xD6,
0xB3,
0x29,
0xE3,
0x2F,
0x84,
0x53,
0xD1,
0x00,
0xED,
0x20,
0xFC,
0xB1,
0x5B,
0x6A,
0xCB,
0xBE,
0x39,
0x4A,
0x4C,
0x58,
0xCF,
0xD0,
0xEF,
0xAA,
0xFB,
0x43,
0x4D,
0x33,
0x85,
0x45,
0xF9,
0x02,
0x7F,
0x50,
0x3C,
0x9F,
0xA8,
0x51,
0xA3,
0x40,
0x8F,
0x92,
0x9D,
0x38,
0xF5,
0xBC,
0xB6,
0xDA,
0x21,
0x10,
0xFF,
0xF3,
0xD2,
0xCD,
0x0C,
0x13,
0xEC,
0x5F,
0x97,
0x44,
0x17,
0xC4,
0xA7,
0x7E,
0x3D,
0x64,
0x5D,
0x19,
0x73,
0x60,
0x81,
0x4F,
0xDC,
0x22,
0x2A,
0x90,
0x88,
0x46,
0xEE,
0xB8,
0x14,
0xDE,
0x5E,
0x0B,
0xDB,
0xE0,
0x32,
0x3A,
0x0A,
0x49,
0x06,
0x24,
0x5C,
0xC2,
0xD3,
0xAC,
0x62,
0x91,
0x95,
0xE4,
0x79,
0xE7,
0xC8,
0x37,
0x6D,
0x8D,
0xD5,
0x4E,
0xA9,
0x6C,
0x56,
0xF4,
0xEA,
0x65,
0x7A,
0xAE,
0x08,
0xBA,
0x78,
0x25,
0x2E,
0x1C,
0xA6,
0xB4,
0xC6,
0xE8,
0xDD,
0x74,
0x1F,
0x4B,
0xBD,
0x8B,
0x8A,
0x70,
0x3E,
0xB5,
0x66,
0x48,
0x03,
0xF6,
0x0E,
0x61,
0x35,
0x57,
0xB9,
0x86,
0xC1,
0x1D,
0x9E,
0xE1,
0xF8,
0x98,
0x11,
0x69,
0xD9,
0x8E,
0x94,
0x9B,
0x1E,
0x87,
0xE9,
0xCE,
0x55,
0x28,
0xDF,
0x8C,
0xA1,
0x89,
0x0D,
0xBF,
0xE6,
0x42,
0x68,
0x41,
0x99,
0x2D,
0x0F,
0xB0,
0x54,
0xBB,
0x16,
};
/**
* Inverse Rijndael S-box Substitution table used for decryption in the
* subBytesDec step.
*/
private static final int[] INVERSE_SBOX = {
0x52,
0x09,
0x6A,
0xD5,
0x30,
0x36,
0xA5,
0x38,
0xBF,
0x40,
0xA3,
0x9E,
0x81,
0xF3,
0xD7,
0xFB,
0x7C,
0xE3,
0x39,
0x82,
0x9B,
0x2F,
0xFF,
0x87,
0x34,
0x8E,
0x43,
0x44,
0xC4,
0xDE,
0xE9,
0xCB,
0x54,
0x7B,
0x94,
0x32,
0xA6,
0xC2,
0x23,
0x3D,
0xEE,
0x4C,
0x95,
0x0B,
0x42,
0xFA,
0xC3,
0x4E,
0x08,
0x2E,
0xA1,
0x66,
0x28,
0xD9,
0x24,
0xB2,
0x76,
0x5B,
0xA2,
0x49,
0x6D,
0x8B,
0xD1,
0x25,
0x72,
0xF8,
0xF6,
0x64,
0x86,
0x68,
0x98,
0x16,
0xD4,
0xA4,
0x5C,
0xCC,
0x5D,
0x65,
0xB6,
0x92,
0x6C,
0x70,
0x48,
0x50,
0xFD,
0xED,
0xB9,
0xDA,
0x5E,
0x15,
0x46,
0x57,
0xA7,
0x8D,
0x9D,
0x84,
0x90,
0xD8,
0xAB,
0x00,
0x8C,
0xBC,
0xD3,
0x0A,
0xF7,
0xE4,
0x58,
0x05,
0xB8,
0xB3,
0x45,
0x06,
0xD0,
0x2C,
0x1E,
0x8F,
0xCA,
0x3F,
0x0F,
0x02,
0xC1,
0xAF,
0xBD,
0x03,
0x01,
0x13,
0x8A,
0x6B,
0x3A,
0x91,
0x11,
0x41,
0x4F,
0x67,
0xDC,
0xEA,
0x97,
0xF2,
0xCF,
0xCE,
0xF0,
0xB4,
0xE6,
0x73,
0x96,
0xAC,
0x74,
0x22,
0xE7,
0xAD,
0x35,
0x85,
0xE2,
0xF9,
0x37,
0xE8,
0x1C,
0x75,
0xDF,
0x6E,
0x47,
0xF1,
0x1A,
0x71,
0x1D,
0x29,
0xC5,
0x89,
0x6F,
0xB7,
0x62,
0x0E,
0xAA,
0x18,
0xBE,
0x1B,
0xFC,
0x56,
0x3E,
0x4B,
0xC6,
0xD2,
0x79,
0x20,
0x9A,
0xDB,
0xC0,
0xFE,
0x78,
0xCD,
0x5A,
0xF4,
0x1F,
0xDD,
0xA8,
0x33,
0x88,
0x07,
0xC7,
0x31,
0xB1,
0x12,
0x10,
0x59,
0x27,
0x80,
0xEC,
0x5F,
0x60,
0x51,
0x7F,
0xA9,
0x19,
0xB5,
0x4A,
0x0D,
0x2D,
0xE5,
0x7A,
0x9F,
0x93,
0xC9,
0x9C,
0xEF,
0xA0,
0xE0,
0x3B,
0x4D,
0xAE,
0x2A,
0xF5,
0xB0,
0xC8,
0xEB,
0xBB,
0x3C,
0x83,
0x53,
0x99,
0x61,
0x17,
0x2B,
0x04,
0x7E,
0xBA,
0x77,
0xD6,
0x26,
0xE1,
0x69,
0x14,
0x63,
0x55,
0x21,
0x0C,
0x7D,
};
/**
* Precalculated lookup table for galois field multiplication by 2 used in
* the MixColums step during encryption.
*/
private static final int[] MULT2 = {
0x00,
0x02,
0x04,
0x06,
0x08,
0x0a,
0x0c,
0x0e,
0x10,
0x12,
0x14,
0x16,
0x18,
0x1a,
0x1c,
0x1e,
0x20,
0x22,
0x24,
0x26,
0x28,
0x2a,
0x2c,
0x2e,
0x30,
0x32,
0x34,
0x36,
0x38,
0x3a,
0x3c,
0x3e,
0x40,
0x42,
0x44,
0x46,
0x48,
0x4a,
0x4c,
0x4e,
0x50,
0x52,
0x54,
0x56,
0x58,
0x5a,
0x5c,
0x5e,
0x60,
0x62,
0x64,
0x66,
0x68,
0x6a,
0x6c,
0x6e,
0x70,
0x72,
0x74,
0x76,
0x78,
0x7a,
0x7c,
0x7e,
0x80,
0x82,
0x84,
0x86,
0x88,
0x8a,
0x8c,
0x8e,
0x90,
0x92,
0x94,
0x96,
0x98,
0x9a,
0x9c,
0x9e,
0xa0,
0xa2,
0xa4,
0xa6,
0xa8,
0xaa,
0xac,
0xae,
0xb0,
0xb2,
0xb4,
0xb6,
0xb8,
0xba,
0xbc,
0xbe,
0xc0,
0xc2,
0xc4,
0xc6,
0xc8,
0xca,
0xcc,
0xce,
0xd0,
0xd2,
0xd4,
0xd6,
0xd8,
0xda,
0xdc,
0xde,
0xe0,
0xe2,
0xe4,
0xe6,
0xe8,
0xea,
0xec,
0xee,
0xf0,
0xf2,
0xf4,
0xf6,
0xf8,
0xfa,
0xfc,
0xfe,
0x1b,
0x19,
0x1f,
0x1d,
0x13,
0x11,
0x17,
0x15,
0x0b,
0x09,
0x0f,
0x0d,
0x03,
0x01,
0x07,
0x05,
0x3b,
0x39,
0x3f,
0x3d,
0x33,
0x31,
0x37,
0x35,
0x2b,
0x29,
0x2f,
0x2d,
0x23,
0x21,
0x27,
0x25,
0x5b,
0x59,
0x5f,
0x5d,
0x53,
0x51,
0x57,
0x55,
0x4b,
0x49,
0x4f,
0x4d,
0x43,
0x41,
0x47,
0x45,
0x7b,
0x79,
0x7f,
0x7d,
0x73,
0x71,
0x77,
0x75,
0x6b,
0x69,
0x6f,
0x6d,
0x63,
0x61,
0x67,
0x65,
0x9b,
0x99,
0x9f,
0x9d,
0x93,
0x91,
0x97,
0x95,
0x8b,
0x89,
0x8f,
0x8d,
0x83,
0x81,
0x87,
0x85,
0xbb,
0xb9,
0xbf,
0xbd,
0xb3,
0xb1,
0xb7,
0xb5,
0xab,
0xa9,
0xaf,
0xad,
0xa3,
0xa1,
0xa7,
0xa5,
0xdb,
0xd9,
0xdf,
0xdd,
0xd3,
0xd1,
0xd7,
0xd5,
0xcb,
0xc9,
0xcf,
0xcd,
0xc3,
0xc1,
0xc7,
0xc5,
0xfb,
0xf9,
0xff,
0xfd,
0xf3,
0xf1,
0xf7,
0xf5,
0xeb,
0xe9,
0xef,
0xed,
0xe3,
0xe1,
0xe7,
0xe5,
};
/**
* Precalculated lookup table for galois field multiplication by 3 used in
* the MixColums step during encryption.
*/
private static final int[] MULT3 = {
0x00,
0x03,
0x06,
0x05,
0x0c,
0x0f,
0x0a,
0x09,
0x18,
0x1b,
0x1e,
0x1d,
0x14,
0x17,
0x12,
0x11,
0x30,
0x33,
0x36,
0x35,
0x3c,
0x3f,
0x3a,
0x39,
0x28,
0x2b,
0x2e,
0x2d,
0x24,
0x27,
0x22,
0x21,
0x60,
0x63,
0x66,
0x65,
0x6c,
0x6f,
0x6a,
0x69,
0x78,
0x7b,
0x7e,
0x7d,
0x74,
0x77,
0x72,
0x71,
0x50,
0x53,
0x56,
0x55,
0x5c,
0x5f,
0x5a,
0x59,
0x48,
0x4b,
0x4e,
0x4d,
0x44,
0x47,
0x42,
0x41,
0xc0,
0xc3,
0xc6,
0xc5,
0xcc,
0xcf,
0xca,
0xc9,
0xd8,
0xdb,
0xde,
0xdd,
0xd4,
0xd7,
0xd2,
0xd1,
0xf0,
0xf3,
0xf6,
0xf5,
0xfc,
0xff,
0xfa,
0xf9,
0xe8,
0xeb,
0xee,
0xed,
0xe4,
0xe7,
0xe2,
0xe1,
0xa0,
0xa3,
0xa6,
0xa5,
0xac,
0xaf,
0xaa,
0xa9,
0xb8,
0xbb,
0xbe,
0xbd,
0xb4,
0xb7,
0xb2,
0xb1,
0x90,
0x93,
0x96,
0x95,
0x9c,
0x9f,
0x9a,
0x99,
0x88,
0x8b,
0x8e,
0x8d,
0x84,
0x87,
0x82,
0x81,
0x9b,
0x98,
0x9d,
0x9e,
0x97,
0x94,
0x91,
0x92,
0x83,
0x80,
0x85,
0x86,
0x8f,
0x8c,
0x89,
0x8a,
0xab,
0xa8,
0xad,
0xae,
0xa7,
0xa4,
0xa1,
0xa2,
0xb3,
0xb0,
0xb5,
0xb6,
0xbf,
0xbc,
0xb9,
0xba,
0xfb,
0xf8,
0xfd,
0xfe,
0xf7,
0xf4,
0xf1,
0xf2,
0xe3,
0xe0,
0xe5,
0xe6,
0xef,
0xec,
0xe9,
0xea,
0xcb,
0xc8,
0xcd,
0xce,
0xc7,
0xc4,
0xc1,
0xc2,
0xd3,
0xd0,
0xd5,
0xd6,
0xdf,
0xdc,
0xd9,
0xda,
0x5b,
0x58,
0x5d,
0x5e,
0x57,
0x54,
0x51,
0x52,
0x43,
0x40,
0x45,
0x46,
0x4f,
0x4c,
0x49,
0x4a,
0x6b,
0x68,
0x6d,
0x6e,
0x67,
0x64,
0x61,
0x62,
0x73,
0x70,
0x75,
0x76,
0x7f,
0x7c,
0x79,
0x7a,
0x3b,
0x38,
0x3d,
0x3e,
0x37,
0x34,
0x31,
0x32,
0x23,
0x20,
0x25,
0x26,
0x2f,
0x2c,
0x29,
0x2a,
0x0b,
0x08,
0x0d,
0x0e,
0x07,
0x04,
0x01,
0x02,
0x13,
0x10,
0x15,
0x16,
0x1f,
0x1c,
0x19,
0x1a,
};
/**
* Precalculated lookup table for galois field multiplication by 9 used in
* the MixColums step during decryption.
*/
private static final int[] MULT9 = {
0x00,
0x09,
0x12,
0x1b,
0x24,
0x2d,
0x36,
0x3f,
0x48,
0x41,
0x5a,
0x53,
0x6c,
0x65,
0x7e,
0x77,
0x90,
0x99,
0x82,
0x8b,
0xb4,
0xbd,
0xa6,
0xaf,
0xd8,
0xd1,
0xca,
0xc3,
0xfc,
0xf5,
0xee,
0xe7,
0x3b,
0x32,
0x29,
0x20,
0x1f,
0x16,
0x0d,
0x04,
0x73,
0x7a,
0x61,
0x68,
0x57,
0x5e,
0x45,
0x4c,
0xab,
0xa2,
0xb9,
0xb0,
0x8f,
0x86,
0x9d,
0x94,
0xe3,
0xea,
0xf1,
0xf8,
0xc7,
0xce,
0xd5,
0xdc,
0x76,
0x7f,
0x64,
0x6d,
0x52,
0x5b,
0x40,
0x49,
0x3e,
0x37,
0x2c,
0x25,
0x1a,
0x13,
0x08,
0x01,
0xe6,
0xef,
0xf4,
0xfd,
0xc2,
0xcb,
0xd0,
0xd9,
0xae,
0xa7,
0xbc,
0xb5,
0x8a,
0x83,
0x98,
0x91,
0x4d,
0x44,
0x5f,
0x56,
0x69,
0x60,
0x7b,
0x72,
0x05,
0x0c,
0x17,
0x1e,
0x21,
0x28,
0x33,
0x3a,
0xdd,
0xd4,
0xcf,
0xc6,
0xf9,
0xf0,
0xeb,
0xe2,
0x95,
0x9c,
0x87,
0x8e,
0xb1,
0xb8,
0xa3,
0xaa,
0xec,
0xe5,
0xfe,
0xf7,
0xc8,
0xc1,
0xda,
0xd3,
0xa4,
0xad,
0xb6,
0xbf,
0x80,
0x89,
0x92,
0x9b,
0x7c,
0x75,
0x6e,
0x67,
0x58,
0x51,
0x4a,
0x43,
0x34,
0x3d,
0x26,
0x2f,
0x10,
0x19,
0x02,
0x0b,
0xd7,
0xde,
0xc5,
0xcc,
0xf3,
0xfa,
0xe1,
0xe8,
0x9f,
0x96,
0x8d,
0x84,
0xbb,
0xb2,
0xa9,
0xa0,
0x47,
0x4e,
0x55,
0x5c,
0x63,
0x6a,
0x71,
0x78,
0x0f,
0x06,
0x1d,
0x14,
0x2b,
0x22,
0x39,
0x30,
0x9a,
0x93,
0x88,
0x81,
0xbe,
0xb7,
0xac,
0xa5,
0xd2,
0xdb,
0xc0,
0xc9,
0xf6,
0xff,
0xe4,
0xed,
0x0a,
0x03,
0x18,
0x11,
0x2e,
0x27,
0x3c,
0x35,
0x42,
0x4b,
0x50,
0x59,
0x66,
0x6f,
0x74,
0x7d,
0xa1,
0xa8,
0xb3,
0xba,
0x85,
0x8c,
0x97,
0x9e,
0xe9,
0xe0,
0xfb,
0xf2,
0xcd,
0xc4,
0xdf,
0xd6,
0x31,
0x38,
0x23,
0x2a,
0x15,
0x1c,
0x07,
0x0e,
0x79,
0x70,
0x6b,
0x62,
0x5d,
0x54,
0x4f,
0x46,
};
/**
* Precalculated lookup table for galois field multiplication by 11 used in
* the MixColums step during decryption.
*/
private static final int[] MULT11 = {
0x00,
0x0b,
0x16,
0x1d,
0x2c,
0x27,
0x3a,
0x31,
0x58,
0x53,
0x4e,
0x45,
0x74,
0x7f,
0x62,
0x69,
0xb0,
0xbb,
0xa6,
0xad,
0x9c,
0x97,
0x8a,
0x81,
0xe8,
0xe3,
0xfe,
0xf5,
0xc4,
0xcf,
0xd2,
0xd9,
0x7b,
0x70,
0x6d,
0x66,
0x57,
0x5c,
0x41,
0x4a,
0x23,
0x28,
0x35,
0x3e,
0x0f,
0x04,
0x19,
0x12,
0xcb,
0xc0,
0xdd,
0xd6,
0xe7,
0xec,
0xf1,
0xfa,
0x93,
0x98,
0x85,
0x8e,
0xbf,
0xb4,
0xa9,
0xa2,
0xf6,
0xfd,
0xe0,
0xeb,
0xda,
0xd1,
0xcc,
0xc7,
0xae,
0xa5,
0xb8,
0xb3,
0x82,
0x89,
0x94,
0x9f,
0x46,
0x4d,
0x50,
0x5b,
0x6a,
0x61,
0x7c,
0x77,
0x1e,
0x15,
0x08,
0x03,
0x32,
0x39,
0x24,
0x2f,
0x8d,
0x86,
0x9b,
0x90,
0xa1,
0xaa,
0xb7,
0xbc,
0xd5,
0xde,
0xc3,
0xc8,
0xf9,
0xf2,
0xef,
0xe4,
0x3d,
0x36,
0x2b,
0x20,
0x11,
0x1a,
0x07,
0x0c,
0x65,
0x6e,
0x73,
0x78,
0x49,
0x42,
0x5f,
0x54,
0xf7,
0xfc,
0xe1,
0xea,
0xdb,
0xd0,
0xcd,
0xc6,
0xaf,
0xa4,
0xb9,
0xb2,
0x83,
0x88,
0x95,
0x9e,
0x47,
0x4c,
0x51,
0x5a,
0x6b,
0x60,
0x7d,
0x76,
0x1f,
0x14,
0x09,
0x02,
0x33,
0x38,
0x25,
0x2e,
0x8c,
0x87,
0x9a,
0x91,
0xa0,
0xab,
0xb6,
0xbd,
0xd4,
0xdf,
0xc2,
0xc9,
0xf8,
0xf3,
0xee,
0xe5,
0x3c,
0x37,
0x2a,
0x21,
0x10,
0x1b,
0x06,
0x0d,
0x64,
0x6f,
0x72,
0x79,
0x48,
0x43,
0x5e,
0x55,
0x01,
0x0a,
0x17,
0x1c,
0x2d,
0x26,
0x3b,
0x30,
0x59,
0x52,
0x4f,
0x44,
0x75,
0x7e,
0x63,
0x68,
0xb1,
0xba,
0xa7,
0xac,
0x9d,
0x96,
0x8b,
0x80,
0xe9,
0xe2,
0xff,
0xf4,
0xc5,
0xce,
0xd3,
0xd8,
0x7a,
0x71,
0x6c,
0x67,
0x56,
0x5d,
0x40,
0x4b,
0x22,
0x29,
0x34,
0x3f,
0x0e,
0x05,
0x18,
0x13,
0xca,
0xc1,
0xdc,
0xd7,
0xe6,
0xed,
0xf0,
0xfb,
0x92,
0x99,
0x84,
0x8f,
0xbe,
0xb5,
0xa8,
0xa3,
};
/**
* Precalculated lookup table for galois field multiplication by 13 used in
* the MixColums step during decryption.
*/
private static final int[] MULT13 = {
0x00,
0x0d,
0x1a,
0x17,
0x34,
0x39,
0x2e,
0x23,
0x68,
0x65,
0x72,
0x7f,
0x5c,
0x51,
0x46,
0x4b,
0xd0,
0xdd,
0xca,
0xc7,
0xe4,
0xe9,
0xfe,
0xf3,
0xb8,
0xb5,
0xa2,
0xaf,
0x8c,
0x81,
0x96,
0x9b,
0xbb,
0xb6,
0xa1,
0xac,
0x8f,
0x82,
0x95,
0x98,
0xd3,
0xde,
0xc9,
0xc4,
0xe7,
0xea,
0xfd,
0xf0,
0x6b,
0x66,
0x71,
0x7c,
0x5f,
0x52,
0x45,
0x48,
0x03,
0x0e,
0x19,
0x14,
0x37,
0x3a,
0x2d,
0x20,
0x6d,
0x60,
0x77,
0x7a,
0x59,
0x54,
0x43,
0x4e,
0x05,
0x08,
0x1f,
0x12,
0x31,
0x3c,
0x2b,
0x26,
0xbd,
0xb0,
0xa7,
0xaa,
0x89,
0x84,
0x93,
0x9e,
0xd5,
0xd8,
0xcf,
0xc2,
0xe1,
0xec,
0xfb,
0xf6,
0xd6,
0xdb,
0xcc,
0xc1,
0xe2,
0xef,
0xf8,
0xf5,
0xbe,
0xb3,
0xa4,
0xa9,
0x8a,
0x87,
0x90,
0x9d,
0x06,
0x0b,
0x1c,
0x11,
0x32,
0x3f,
0x28,
0x25,
0x6e,
0x63,
0x74,
0x79,
0x5a,
0x57,
0x40,
0x4d,
0xda,
0xd7,
0xc0,
0xcd,
0xee,
0xe3,
0xf4,
0xf9,
0xb2,
0xbf,
0xa8,
0xa5,
0x86,
0x8b,
0x9c,
0x91,
0x0a,
0x07,
0x10,
0x1d,
0x3e,
0x33,
0x24,
0x29,
0x62,
0x6f,
0x78,
0x75,
0x56,
0x5b,
0x4c,
0x41,
0x61,
0x6c,
0x7b,
0x76,
0x55,
0x58,
0x4f,
0x42,
0x09,
0x04,
0x13,
0x1e,
0x3d,
0x30,
0x27,
0x2a,
0xb1,
0xbc,
0xab,
0xa6,
0x85,
0x88,
0x9f,
0x92,
0xd9,
0xd4,
0xc3,
0xce,
0xed,
0xe0,
0xf7,
0xfa,
0xb7,
0xba,
0xad,
0xa0,
0x83,
0x8e,
0x99,
0x94,
0xdf,
0xd2,
0xc5,
0xc8,
0xeb,
0xe6,
0xf1,
0xfc,
0x67,
0x6a,
0x7d,
0x70,
0x53,
0x5e,
0x49,
0x44,
0x0f,
0x02,
0x15,
0x18,
0x3b,
0x36,
0x21,
0x2c,
0x0c,
0x01,
0x16,
0x1b,
0x38,
0x35,
0x22,
0x2f,
0x64,
0x69,
0x7e,
0x73,
0x50,
0x5d,
0x4a,
0x47,
0xdc,
0xd1,
0xc6,
0xcb,
0xe8,
0xe5,
0xf2,
0xff,
0xb4,
0xb9,
0xae,
0xa3,
0x80,
0x8d,
0x9a,
0x97,
};
/**
* Precalculated lookup table for galois field multiplication by 14 used in
* the MixColums step during decryption.
*/
private static final int[] MULT14 = {
0x00,
0x0e,
0x1c,
0x12,
0x38,
0x36,
0x24,
0x2a,
0x70,
0x7e,
0x6c,
0x62,
0x48,
0x46,
0x54,
0x5a,
0xe0,
0xee,
0xfc,
0xf2,
0xd8,
0xd6,
0xc4,
0xca,
0x90,
0x9e,
0x8c,
0x82,
0xa8,
0xa6,
0xb4,
0xba,
0xdb,
0xd5,
0xc7,
0xc9,
0xe3,
0xed,
0xff,
0xf1,
0xab,
0xa5,
0xb7,
0xb9,
0x93,
0x9d,
0x8f,
0x81,
0x3b,
0x35,
0x27,
0x29,
0x03,
0x0d,
0x1f,
0x11,
0x4b,
0x45,
0x57,
0x59,
0x73,
0x7d,
0x6f,
0x61,
0xad,
0xa3,
0xb1,
0xbf,
0x95,
0x9b,
0x89,
0x87,
0xdd,
0xd3,
0xc1,
0xcf,
0xe5,
0xeb,
0xf9,
0xf7,
0x4d,
0x43,
0x51,
0x5f,
0x75,
0x7b,
0x69,
0x67,
0x3d,
0x33,
0x21,
0x2f,
0x05,
0x0b,
0x19,
0x17,
0x76,
0x78,
0x6a,
0x64,
0x4e,
0x40,
0x52,
0x5c,
0x06,
0x08,
0x1a,
0x14,
0x3e,
0x30,
0x22,
0x2c,
0x96,
0x98,
0x8a,
0x84,
0xae,
0xa0,
0xb2,
0xbc,
0xe6,
0xe8,
0xfa,
0xf4,
0xde,
0xd0,
0xc2,
0xcc,
0x41,
0x4f,
0x5d,
0x53,
0x79,
0x77,
0x65,
0x6b,
0x31,
0x3f,
0x2d,
0x23,
0x09,
0x07,
0x15,
0x1b,
0xa1,
0xaf,
0xbd,
0xb3,
0x99,
0x97,
0x85,
0x8b,
0xd1,
0xdf,
0xcd,
0xc3,
0xe9,
0xe7,
0xf5,
0xfb,
0x9a,
0x94,
0x86,
0x88,
0xa2,
0xac,
0xbe,
0xb0,
0xea,
0xe4,
0xf6,
0xf8,
0xd2,
0xdc,
0xce,
0xc0,
0x7a,
0x74,
0x66,
0x68,
0x42,
0x4c,
0x5e,
0x50,
0x0a,
0x04,
0x16,
0x18,
0x32,
0x3c,
0x2e,
0x20,
0xec,
0xe2,
0xf0,
0xfe,
0xd4,
0xda,
0xc8,
0xc6,
0x9c,
0x92,
0x80,
0x8e,
0xa4,
0xaa,
0xb8,
0xb6,
0x0c,
0x02,
0x10,
0x1e,
0x34,
0x3a,
0x28,
0x26,
0x7c,
0x72,
0x60,
0x6e,
0x44,
0x4a,
0x58,
0x56,
0x37,
0x39,
0x2b,
0x25,
0x0f,
0x01,
0x13,
0x1d,
0x47,
0x49,
0x5b,
0x55,
0x7f,
0x71,
0x63,
0x6d,
0xd7,
0xd9,
0xcb,
0xc5,
0xef,
0xe1,
0xf3,
0xfd,
0xa7,
0xa9,
0xbb,
0xb5,
0x9f,
0x91,
0x83,
0x8d,
};
/**
* Subroutine of the Rijndael key expansion.
*/
public static BigInteger scheduleCore(BigInteger t, int rconCounter) {
StringBuilder rBytes = new StringBuilder(t.toString(16));
// Add zero padding
while (rBytes.length() < 8) {
rBytes.insert(0, "0");
}
// rotate the first 16 bits to the back
String rotatingBytes = rBytes.substring(0, 2);
String fixedBytes = rBytes.substring(2);
rBytes = new StringBuilder(fixedBytes + rotatingBytes);
// apply S-Box to all 8-Bit Substrings
for (int i = 0; i < 4; i++) {
StringBuilder currentByteBits = new StringBuilder(
rBytes.substring(i * 2, (i + 1) * 2)
);
int currentByte = Integer.parseInt(currentByteBits.toString(), 16);
currentByte = SBOX[currentByte];
// add the current RCON value to the first byte
if (i == 0) {
currentByte = currentByte ^ RCON[rconCounter];
}
currentByteBits =
new StringBuilder(Integer.toHexString(currentByte));
// Add zero padding
while (currentByteBits.length() < 2) {
currentByteBits.insert(0, '0');
}
// replace bytes in original string
rBytes =
new StringBuilder(
rBytes.substring(0, i * 2) +
currentByteBits +
rBytes.substring((i + 1) * 2)
);
}
// t = new BigInteger(rBytes, 16);
// return t;
return new BigInteger(rBytes.toString(), 16);
}
/**
* Returns an array of 10 + 1 round keys that are calculated by using
* Rijndael key schedule
*
* @return array of 10 + 1 round keys
*/
public static BigInteger[] keyExpansion(BigInteger initialKey) {
BigInteger[] roundKeys = {
initialKey,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
BigInteger.ZERO,
};
// initialize rcon iteration
int rconCounter = 1;
for (int i = 1; i < 11; i++) {
// get the previous 32 bits the key
BigInteger t =
roundKeys[i - 1].remainder(new BigInteger("100000000", 16));
// split previous key into 8-bit segments
BigInteger[] prevKey = {
roundKeys[i - 1].remainder(new BigInteger("100000000", 16)),
roundKeys[i - 1].remainder(
new BigInteger("10000000000000000", 16)
)
.divide(new BigInteger("100000000", 16)),
roundKeys[i - 1].remainder(
new BigInteger("1000000000000000000000000", 16)
)
.divide(new BigInteger("10000000000000000", 16)),
roundKeys[i - 1].divide(
new BigInteger("1000000000000000000000000", 16)
),
};
// run schedule core
t = scheduleCore(t, rconCounter);
rconCounter += 1;
// Calculate partial round key
BigInteger t0 = t.xor(prevKey[3]);
BigInteger t1 = t0.xor(prevKey[2]);
BigInteger t2 = t1.xor(prevKey[1]);
BigInteger t3 = t2.xor(prevKey[0]);
// Join round key segments
t2 = t2.multiply(new BigInteger("100000000", 16));
t1 = t1.multiply(new BigInteger("10000000000000000", 16));
t0 = t0.multiply(new BigInteger("1000000000000000000000000", 16));
roundKeys[i] = t0.add(t1).add(t2).add(t3);
}
return roundKeys;
}
/**
* representation of the input 128-bit block as an array of 8-bit integers.
*
* @param block of 128-bit integers
* @return array of 8-bit integers
*/
public static int[] splitBlockIntoCells(BigInteger block) {
int[] cells = new int[16];
StringBuilder blockBits = new StringBuilder(block.toString(2));
// Append leading 0 for full "128-bit" string
while (blockBits.length() < 128) {
blockBits.insert(0, '0');
}
// split 128 to 8 bit cells
for (int i = 0; i < cells.length; i++) {
String cellBits = blockBits.substring(8 * i, 8 * (i + 1));
cells[i] = Integer.parseInt(cellBits, 2);
}
return cells;
}
/**
* Returns the 128-bit BigInteger representation of the input of an array of
* 8-bit integers.
*
* @param cells that we need to merge
* @return block of merged cells
*/
public static BigInteger mergeCellsIntoBlock(int[] cells) {
StringBuilder blockBits = new StringBuilder();
for (int i = 0; i < 16; i++) {
StringBuilder cellBits = new StringBuilder(
Integer.toBinaryString(cells[i])
);
// Append leading 0 for full "8-bit" strings
while (cellBits.length() < 8) {
cellBits.insert(0, '0');
}
blockBits.append(cellBits);
}
return new BigInteger(blockBits.toString(), 2);
}
/**
* @return ciphertext XOR key
*/
public static BigInteger addRoundKey(
BigInteger ciphertext,
BigInteger key
) {
return ciphertext.xor(key);
}
/**
* substitutes 8-Bit long substrings of the input using the S-Box and
* returns the result.
*
* @return subtraction Output
*/
public static BigInteger subBytes(BigInteger ciphertext) {
int[] cells = splitBlockIntoCells(ciphertext);
for (int i = 0; i < 16; i++) {
cells[i] = SBOX[cells[i]];
}
return mergeCellsIntoBlock(cells);
}
/**
* substitutes 8-Bit long substrings of the input using the inverse S-Box
* for decryption and returns the result.
*
* @return subtraction Output
*/
public static BigInteger subBytesDec(BigInteger ciphertext) {
int[] cells = splitBlockIntoCells(ciphertext);
for (int i = 0; i < 16; i++) {
cells[i] = INVERSE_SBOX[cells[i]];
}
return mergeCellsIntoBlock(cells);
}
/**
* Cell permutation step. Shifts cells within the rows of the input and
* returns the result.
*/
public static BigInteger shiftRows(BigInteger ciphertext) {
int[] cells = splitBlockIntoCells(ciphertext);
int[] output = new int[16];
// do nothing in the first row
output[0] = cells[0];
output[4] = cells[4];
output[8] = cells[8];
output[12] = cells[12];
// shift the second row backwards by one cell
output[1] = cells[5];
output[5] = cells[9];
output[9] = cells[13];
output[13] = cells[1];
// shift the third row backwards by two cell
output[2] = cells[10];
output[6] = cells[14];
output[10] = cells[2];
output[14] = cells[6];
// shift the forth row backwards by tree cell
output[3] = cells[15];
output[7] = cells[3];
output[11] = cells[7];
output[15] = cells[11];
return mergeCellsIntoBlock(output);
}
/**
* Cell permutation step for decryption . Shifts cells within the rows of
* the input and returns the result.
*/
public static BigInteger shiftRowsDec(BigInteger ciphertext) {
int[] cells = splitBlockIntoCells(ciphertext);
int[] output = new int[16];
// do nothing in the first row
output[0] = cells[0];
output[4] = cells[4];
output[8] = cells[8];
output[12] = cells[12];
// shift the second row forwards by one cell
output[1] = cells[13];
output[5] = cells[1];
output[9] = cells[5];
output[13] = cells[9];
// shift the third row forwards by two cell
output[2] = cells[10];
output[6] = cells[14];
output[10] = cells[2];
output[14] = cells[6];
// shift the forth row forwards by tree cell
output[3] = cells[7];
output[7] = cells[11];
output[11] = cells[15];
output[15] = cells[3];
return mergeCellsIntoBlock(output);
}
/**
* Applies the Rijndael MixColumns to the input and returns the result.
*/
public static BigInteger mixColumns(BigInteger ciphertext) {
int[] cells = splitBlockIntoCells(ciphertext);
int[] outputCells = new int[16];
for (int i = 0; i < 4; i++) {
int[] row = {
cells[i * 4],
cells[i * 4 + 1],
cells[i * 4 + 2],
cells[i * 4 + 3],
};
outputCells[i * 4] =
MULT2[row[0]] ^ MULT3[row[1]] ^ row[2] ^ row[3];
outputCells[i * 4 + 1] =
row[0] ^ MULT2[row[1]] ^ MULT3[row[2]] ^ row[3];
outputCells[i * 4 + 2] =
row[0] ^ row[1] ^ MULT2[row[2]] ^ MULT3[row[3]];
outputCells[i * 4 + 3] =
MULT3[row[0]] ^ row[1] ^ row[2] ^ MULT2[row[3]];
}
return mergeCellsIntoBlock(outputCells);
}
/**
* Applies the inverse Rijndael MixColumns for decryption to the input and
* returns the result.
*/
public static BigInteger mixColumnsDec(BigInteger ciphertext) {
int[] cells = splitBlockIntoCells(ciphertext);
int[] outputCells = new int[16];
for (int i = 0; i < 4; i++) {
int[] row = {
cells[i * 4],
cells[i * 4 + 1],
cells[i * 4 + 2],
cells[i * 4 + 3],
};
outputCells[i * 4] =
MULT14[row[0]] ^
MULT11[row[1]] ^
MULT13[row[2]] ^
MULT9[row[3]];
outputCells[i * 4 + 1] =
MULT9[row[0]] ^
MULT14[row[1]] ^
MULT11[row[2]] ^
MULT13[row[3]];
outputCells[i * 4 + 2] =
MULT13[row[0]] ^
MULT9[row[1]] ^
MULT14[row[2]] ^
MULT11[row[3]];
outputCells[i * 4 + 3] =
MULT11[row[0]] ^
MULT13[row[1]] ^
MULT9[row[2]] ^
MULT14[row[3]];
}
return mergeCellsIntoBlock(outputCells);
}
/**
* Encrypts the plaintext with the key and returns the result
*
* @param plainText which we want to encrypt
* @param key the key for encrypt
* @return EncryptedText
*/
public static BigInteger encrypt(BigInteger plainText, BigInteger key) {
BigInteger[] roundKeys = keyExpansion(key);
// Initial round
plainText = addRoundKey(plainText, roundKeys[0]);
// Main rounds
for (int i = 1; i < 10; i++) {
plainText = subBytes(plainText);
plainText = shiftRows(plainText);
plainText = mixColumns(plainText);
plainText = addRoundKey(plainText, roundKeys[i]);
}
// Final round
plainText = subBytes(plainText);
plainText = shiftRows(plainText);
plainText = addRoundKey(plainText, roundKeys[10]);
return plainText;
}
/**
* Decrypts the ciphertext with the key and returns the result
*
* @param cipherText The Encrypted text which we want to decrypt
* @return decryptedText
*/
public static BigInteger decrypt(BigInteger cipherText, BigInteger key) {
BigInteger[] roundKeys = keyExpansion(key);
// Invert final round
cipherText = addRoundKey(cipherText, roundKeys[10]);
cipherText = shiftRowsDec(cipherText);
cipherText = subBytesDec(cipherText);
// Invert main rounds
for (int i = 9; i > 0; i--) {
cipherText = addRoundKey(cipherText, roundKeys[i]);
cipherText = mixColumnsDec(cipherText);
cipherText = shiftRowsDec(cipherText);
cipherText = subBytesDec(cipherText);
}
// Invert initial round
cipherText = addRoundKey(cipherText, roundKeys[0]);
return cipherText;
}
public static void main(String[] args) {
try (Scanner input = new Scanner(System.in)) {
System.out.println(
"Enter (e) letter for encrpyt or (d) letter for decrypt :"
);
char choice = input.nextLine().charAt(0);
String in;
switch (choice) {
case 'E', 'e' -> {
System.out.println(
"Choose a plaintext block (128-Bit Integer in base 16):"
);
in = input.nextLine();
BigInteger plaintext = new BigInteger(in, 16);
System.out.println(
"Choose a Key (128-Bit Integer in base 16):"
);
in = input.nextLine();
BigInteger encryptionKey = new BigInteger(in, 16);
System.out.println(
"The encrypted message is: \n" +
encrypt(plaintext, encryptionKey).toString(16)
);
}
case 'D', 'd' -> {
System.out.println(
"Enter your ciphertext block (128-Bit Integer in base 16):"
);
in = input.nextLine();
BigInteger ciphertext = new BigInteger(in, 16);
System.out.println(
"Choose a Key (128-Bit Integer in base 16):"
);
in = input.nextLine();
BigInteger decryptionKey = new BigInteger(in, 16);
System.out.println(
"The deciphered message is:\n" +
decrypt(ciphertext, decryptionKey).toString(16)
);
}
default -> System.out.println("** End **");
}
}
}
}