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 final class AES {
    private 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));
        }

        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 encrypt 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 **");
            }
        }
    }
}
