/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

/* AES Encryption */

package amcl

//import "fmt"

const AES_ECB int = 0
const AES_CBC int = 1
const AES_CFB1 int = 2
const AES_CFB2 int = 3
const AES_CFB4 int = 5
const AES_OFB1 int = 14
const AES_OFB2 int = 15
const AES_OFB4 int = 17
const AES_OFB8 int = 21
const AES_OFB16 int = 29
const AES_CTR1 int = 30
const AES_CTR2 int = 31
const AES_CTR4 int = 33
const AES_CTR8 int = 37
const AES_CTR16 int = 45

var aes_InCo = [...]byte{0xB, 0xD, 0x9, 0xE} /* Inverse Coefficients */

var aes_ptab = [...]byte{
	1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53,
	95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170,
	229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49,
	83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
	76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136,
	131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154,
	181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
	254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160,
	251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65,
	195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117,
	159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128,
	155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84,
	252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202,
	69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
	18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
	57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1}

var aes_ltab = [...]byte{
	0, 255, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3,
	100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193,
	125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120,
	101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142,
	150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56,
	102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16,
	126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186,
	43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87,
	175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232,
	44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160,
	127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183,
	204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157,
	151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209,
	83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171,
	68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165,
	103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7}

var aes_fbsub = [...]byte{
	99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118,
	202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192,
	183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21,
	4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117,
	9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132,
	83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207,
	208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
	81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210,
	205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115,
	96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219,
	224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
	231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8,
	186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138,
	112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,
	225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223,
	140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22}

var aes_rbsub = [...]byte{
	82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251,
	124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203,
	84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78,
	8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37,
	114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146,
	108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132,
	144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6,
	208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107,
	58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115,
	150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110,
	71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27,
	252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244,
	31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95,
	96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239,
	160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97,
	23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125}

var aes_rco = [...]byte{1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47}

var aes_ftable = [...]uint32{
	0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0xdf2f2ff, 0xbd6b6bd6,
	0xb16f6fde, 0x54c5c591, 0x50303060, 0x3010102, 0xa96767ce, 0x7d2b2b56,
	0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, 0x45caca8f, 0x9d82821f,
	0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0xbf0f0fb,
	0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453,
	0x967272e4, 0x5bc0c09b, 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c,
	0x5a36366c, 0x413f3f7e, 0x2f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
	0x34e5e5d1, 0x8f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a,
	0xc040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637,
	0xf05050a, 0xb59a9a2f, 0x907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
	0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d,
	0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b,
	0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
	0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x0, 0x2cededc1,
	0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d,
	0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
	0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a,
	0x55333366, 0x94858511, 0xcf45458a, 0x10f9f9e9, 0x6020204, 0x817f7ffe,
	0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
	0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x4f5f5f1,
	0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5,
	0xef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
	0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755,
	0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6,
	0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
	0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428,
	0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, 0x3be0e0db, 0x56323264,
	0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0xa06060c, 0x6c242448, 0xe45c5cb8,
	0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531,
	0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda,
	0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
	0x7f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810,
	0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657,
	0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
	0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c,
	0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x5030306, 0x1f6f6f7, 0x120e0e1c,
	0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
	0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122,
	0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, 0xb69b9b2d, 0x221e1e3c,
	0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
	0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7,
	0xc6424284, 0xb86868d0, 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e,
	0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c}

var aes_rtable = [...]uint32{
	0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f,
	0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5,
	0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, 0x495ab1de, 0x671bba25,
	0x980eea45, 0xe1c0fe5d, 0x2752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
	0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458,
	0x2969e049, 0x44c8c98e, 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927,
	0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5,
	0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9,
	0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, 0x23d373ab, 0xe2024b72,
	0x578f1fe3, 0x2aab5566, 0x728ebb2, 0x3c2b52f, 0x9a7bc586, 0xa50837d3,
	0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7,
	0xf0f207f3, 0xa1e2694e, 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4,
	0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040,
	0x69f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d,
	0xb58d5491, 0x55dc471, 0x6fd40604, 0xff155060, 0x24fb9819, 0x97e9bdd6,
	0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
	0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x0, 0x83868009, 0x48ed2b32,
	0xac70111e, 0x4e725a6c, 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36,
	0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0xfe75793,
	0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c,
	0xaba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, 0xb0d090e, 0xadc78bf2,
	0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
	0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb,
	0x68fcedb6, 0x63f1e4b8, 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684,
	0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
	0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947,
	0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, 0xc74e4987, 0xc1d138d9,
	0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
	0xe49d3a2c, 0xd927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890,
	0x5ef7392e, 0xf5afc382, 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf,
	0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x97826cd, 0xf418596e,
	0x1b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x8cfbc21, 0xe6e815ef,
	0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, 0xafb2a431, 0x31233f2a,
	0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
	0x4a9804f1, 0xf7daec41, 0xe50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43,
	0x544daacc, 0xdf0496e4, 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546,
	0x4ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92,
	0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb,
	0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, 0x59dfd29c, 0x3f73f255,
	0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
	0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0xc25e2bc,
	0x8b493c28, 0x41950dff, 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664,
	0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0}

type AES struct {
	Nk   int
	Nr   int
	mode int
	fkey [60]uint32
	rkey [60]uint32
	f    [16]byte
}

/* Rotates 32-bit word left by 1, 2 or 3 byte  */

func aes_ROTL8(x uint32) uint32 {
	return (((x) << 8) | ((x) >> 24))
}

func aes_ROTL16(x uint32) uint32 {
	return (((x) << 16) | ((x) >> 16))
}

func aes_ROTL24(x uint32) uint32 {
	return (((x) << 24) | ((x) >> 8))
}

func aes_pack(b [4]byte) uint32 { /* pack bytes into a 32-bit Word */
	return ((uint32(b[3]) & 0xff) << 24) | ((uint32(b[2]) & 0xff) << 16) | ((uint32(b[1]) & 0xff) << 8) | (uint32(b[0]) & 0xff)
}

func aes_unpack(a uint32) [4]byte { /* unpack bytes from a word */
	var b = [4]byte{byte(a & 0xff), byte((a >> 8) & 0xff), byte((a >> 16) & 0xff), byte((a >> 24) & 0xff)}
	return b
}

func aes_bmul(x byte, y byte) byte { /* x.y= AntiLog(Log(x) + Log(y)) */

	ix := int(x) & 0xff
	iy := int(y) & 0xff
	lx := int(aes_ltab[ix]) & 0xff
	ly := int(aes_ltab[iy]) & 0xff

	if x != 0 && y != 0 {
		return aes_ptab[(lx+ly)%255]
	} else {
		return byte(0)
	}
}

func aes_SubByte(a uint32) uint32 {
	b := aes_unpack(a)
	b[0] = aes_fbsub[int(b[0])]
	b[1] = aes_fbsub[int(b[1])]
	b[2] = aes_fbsub[int(b[2])]
	b[3] = aes_fbsub[int(b[3])]
	return aes_pack(b)
}

func aes_product(x uint32, y uint32) byte { /* dot product of two 4-byte arrays */
	xb := aes_unpack(x)
	yb := aes_unpack(y)

	return (aes_bmul(xb[0], yb[0]) ^ aes_bmul(xb[1], yb[1]) ^ aes_bmul(xb[2], yb[2]) ^ aes_bmul(xb[3], yb[3]))
}

func aes_InvMixCol(x uint32) uint32 { /* matrix Multiplication */
	var b [4]byte
	m := aes_pack(aes_InCo)
	b[3] = aes_product(m, x)
	m = aes_ROTL24(m)
	b[2] = aes_product(m, x)
	m = aes_ROTL24(m)
	b[1] = aes_product(m, x)
	m = aes_ROTL24(m)
	b[0] = aes_product(m, x)
	var y = aes_pack(b)
	return y
}

func aes_increment(f []byte) {
	for i := 0; i < 16; i++ {
		f[i]++
		if f[i] != 0 {
			break
		}
	}
}

/* reset cipher */
func (A *AES) Reset(m int, iv []byte) { /* reset mode, or reset iv */
	A.mode = m
	for i := 0; i < 16; i++ {
		A.f[i] = 0
	}
	if (A.mode != AES_ECB) && (iv != nil) {
		for i := 0; i < 16; i++ {
			A.f[i] = iv[i]
		}
	}
}

func (A *AES) Init(m int, nk int, key []byte, iv []byte) bool {
	/* Key Scheduler. Create expanded encryption key */
	var CipherKey [8]uint32
	var b [4]byte
	nk /= 4
	if nk != 4 && nk != 6 && nk != 8 {
		return false
	}
	nr := 6 + nk
	A.Nk = nk
	A.Nr = nr
	A.Reset(m, iv)
	N := 4 * (nr + 1)

	j := 0
	for i := 0; i < nk; i++ {
		for k := 0; k < 4; k++ {
			b[k] = key[j+k]
		}
		CipherKey[i] = aes_pack(b)
		j += 4
	}
	for i := 0; i < nk; i++ {
		A.fkey[i] = CipherKey[i]
	}

	j = nk
	for k := 0; j < N; k++ {
		A.fkey[j] = A.fkey[j-nk] ^ aes_SubByte(aes_ROTL24(A.fkey[j-1])) ^ uint32(aes_rco[k])
		if nk<=6 {
			for i := 1; i < nk && (i+j) < N; i++ {
				A.fkey[i+j] = A.fkey[i+j-nk] ^ A.fkey[i+j-1]
			}
		} else {
			i:=0
			for i = 1; i < 4 && (i + j) < N; i++ {
				A.fkey[i + j] = A.fkey[i + j - nk] ^ A.fkey[i + j - 1];
			}
			if (j + 4) < N {
				A.fkey[j + 4] = A.fkey[j + 4 - nk] ^ aes_SubByte(A.fkey[j + 3]);
			}
			for i = 5; i < nk && (i + j) < N; i++ {
				A.fkey[i + j] = A.fkey[i + j - nk] ^ A.fkey[i + j - 1];
			}
		}
		j += nk
		
	}

	/* now for the expanded decrypt key in reverse order */

	for j := 0; j < 4; j++ {
		A.rkey[j+N-4] = A.fkey[j]
	}
	for i := 4; i < N-4; i += 4 {
		k := N - 4 - i
		for j := 0; j < 4; j++ {
			A.rkey[k+j] = aes_InvMixCol(A.fkey[i+j])
		}
	}
	for j := N - 4; j < N; j++ {
		A.rkey[j-N+4] = A.fkey[j]
	}
	return true
}

func NewAES() *AES {
	var A = new(AES)
	return A
}

func (A *AES) Getreg() [16]byte {
	var ir [16]byte
	for i := 0; i < 16; i++ {
		ir[i] = A.f[i]
	}
	return ir
}

/* Encrypt a single block */
func (A *AES) ecb_encrypt(buff []byte) {
	var b [4]byte
	var p [4]uint32
	var q [4]uint32

	j := 0
	for i := 0; i < 4; i++ {
		for k := 0; k < 4; k++ {
			b[k] = buff[j+k]
		}
		p[i] = aes_pack(b)
		p[i] ^= A.fkey[i]
		j += 4
	}

	k := 4

	/* State alternates between p and q */
	for i := 1; i < A.Nr; i++ {
		q[0] = A.fkey[k] ^ aes_ftable[int(p[0]&0xff)] ^ aes_ROTL8(aes_ftable[int((p[1]>>8)&0xff)]) ^ aes_ROTL16(aes_ftable[int((p[2]>>16)&0xff)]) ^ aes_ROTL24(aes_ftable[int((p[3]>>24)&0xff)])

		q[1] = A.fkey[k+1] ^ aes_ftable[int(p[1]&0xff)] ^ aes_ROTL8(aes_ftable[int((p[2]>>8)&0xff)]) ^ aes_ROTL16(aes_ftable[int((p[3]>>16)&0xff)]) ^ aes_ROTL24(aes_ftable[int((p[0]>>24)&0xff)])

		q[2] = A.fkey[k+2] ^ aes_ftable[int(p[2]&0xff)] ^ aes_ROTL8(aes_ftable[int((p[3]>>8)&0xff)]) ^ aes_ROTL16(aes_ftable[int((p[0]>>16)&0xff)]) ^ aes_ROTL24(aes_ftable[int((p[1]>>24)&0xff)])

		q[3] = A.fkey[k+3] ^ aes_ftable[int(p[3]&0xff)] ^ aes_ROTL8(aes_ftable[int((p[0]>>8)&0xff)]) ^ aes_ROTL16(aes_ftable[int((p[1]>>16)&0xff)]) ^ aes_ROTL24(aes_ftable[int((p[2]>>24)&0xff)])

		k += 4
		for j = 0; j < 4; j++ {
			t := p[j]
			p[j] = q[j]
			q[j] = t
		}
	}

	/* Last Round */

	q[0] = A.fkey[k] ^ uint32(aes_fbsub[int(p[0]&0xff)]) ^ aes_ROTL8(uint32(aes_fbsub[int((p[1]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_fbsub[int((p[2]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_fbsub[int((p[3]>>24)&0xff)]))

	q[1] = A.fkey[k+1] ^ uint32(aes_fbsub[int(p[1]&0xff)]) ^ aes_ROTL8(uint32(aes_fbsub[int((p[2]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_fbsub[int((p[3]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_fbsub[int((p[0]>>24)&0xff)]))

	q[2] = A.fkey[k+2] ^ uint32(aes_fbsub[int(p[2]&0xff)]) ^ aes_ROTL8(uint32(aes_fbsub[int((p[3]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_fbsub[int((p[0]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_fbsub[int((p[1]>>24)&0xff)]))

	q[3] = A.fkey[k+3] ^ uint32(aes_fbsub[int(p[3]&0xff)]) ^ aes_ROTL8(uint32(aes_fbsub[int((p[0]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_fbsub[int((p[1]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_fbsub[int((p[2]>>24)&0xff)]))

	j = 0
	for i := 0; i < 4; i++ {
		b = aes_unpack(q[i])
		for k = 0; k < 4; k++ {
			buff[j+k] = b[k]
		}
		j += 4
	}
}

/* Decrypt a single block */
func (A *AES) ecb_decrypt(buff []byte) {
	var b [4]byte
	var p [4]uint32
	var q [4]uint32

	j := 0
	for i := 0; i < 4; i++ {
		for k := 0; k < 4; k++ {
			b[k] = buff[j+k]
		}
		p[i] = aes_pack(b)
		p[i] ^= A.rkey[i]
		j += 4
	}

	k := 4

	/* State alternates between p and q */
	for i := 1; i < A.Nr; i++ {

		q[0] = A.rkey[k] ^ aes_rtable[int(p[0]&0xff)] ^ aes_ROTL8(aes_rtable[int((p[3]>>8)&0xff)]) ^ aes_ROTL16(aes_rtable[int((p[2]>>16)&0xff)]) ^ aes_ROTL24(aes_rtable[int((p[1]>>24)&0xff)])

		q[1] = A.rkey[k+1] ^ aes_rtable[int(p[1]&0xff)] ^ aes_ROTL8(aes_rtable[int((p[0]>>8)&0xff)]) ^ aes_ROTL16(aes_rtable[int((p[3]>>16)&0xff)]) ^ aes_ROTL24(aes_rtable[int((p[2]>>24)&0xff)])

		q[2] = A.rkey[k+2] ^ aes_rtable[int(p[2]&0xff)] ^ aes_ROTL8(aes_rtable[int((p[1]>>8)&0xff)]) ^ aes_ROTL16(aes_rtable[int((p[0]>>16)&0xff)]) ^ aes_ROTL24(aes_rtable[int((p[3]>>24)&0xff)])

		q[3] = A.rkey[k+3] ^ aes_rtable[int(p[3]&0xff)] ^ aes_ROTL8(aes_rtable[int((p[2]>>8)&0xff)]) ^ aes_ROTL16(aes_rtable[int((p[1]>>16)&0xff)]) ^ aes_ROTL24(aes_rtable[int((p[0]>>24)&0xff)])

		k += 4
		for j := 0; j < 4; j++ {
			t := p[j]
			p[j] = q[j]
			q[j] = t
		}
	}

	/* Last Round */

	q[0] = A.rkey[k] ^ uint32(aes_rbsub[int(p[0]&0xff)]) ^ aes_ROTL8(uint32(aes_rbsub[int((p[3]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_rbsub[int((p[2]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_rbsub[int((p[1]>>24)&0xff)]))

	q[1] = A.rkey[k+1] ^ uint32(aes_rbsub[int(p[1]&0xff)]) ^ aes_ROTL8(uint32(aes_rbsub[int((p[0]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_rbsub[int((p[3]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_rbsub[int((p[2]>>24)&0xff)]))

	q[2] = A.rkey[k+2] ^ uint32(aes_rbsub[int(p[2]&0xff)]) ^ aes_ROTL8(uint32(aes_rbsub[int((p[1]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_rbsub[int((p[0]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_rbsub[int((p[3]>>24)&0xff)]))

	q[3] = A.rkey[k+3] ^ uint32(aes_rbsub[int((p[3])&0xff)]) ^ aes_ROTL8(uint32(aes_rbsub[int((p[2]>>8)&0xff)])) ^ aes_ROTL16(uint32(aes_rbsub[int((p[1]>>16)&0xff)])) ^ aes_ROTL24(uint32(aes_rbsub[int((p[0]>>24)&0xff)]))

	j = 0
	for i := 0; i < 4; i++ {
		b = aes_unpack(q[i])
		for k := 0; k < 4; k++ {
			buff[j+k] = b[k]
		}
		j += 4
	}
}

/* Encrypt using selected mode of operation */
func (A *AES) Encrypt(buff []byte) uint32 {
	var st [16]byte

	// Supported Modes of Operation

	var fell_off uint32 = 0
	switch A.mode {
	case AES_ECB:
		A.ecb_encrypt(buff)
		return 0
	case AES_CBC:
		for j := 0; j < 16; j++ {
			buff[j] ^= A.f[j]
		}
		A.ecb_encrypt(buff)
		for j := 0; j < 16; j++ {
			A.f[j] = buff[j]
		}
		return 0

	case AES_CFB1:
		fallthrough
	case AES_CFB2:
		fallthrough
	case AES_CFB4:
		bytes := A.mode - AES_CFB1 + 1
		for j := 0; j < bytes; j++ {
			fell_off = (fell_off << 8) | uint32(A.f[j])
		}
		for j := 0; j < 16; j++ {
			st[j] = A.f[j]
		}
		for j := bytes; j < 16; j++ {
			A.f[j-bytes] = A.f[j]
		}
		A.ecb_encrypt(st[:])
		for j := 0; j < bytes; j++ {
			buff[j] ^= st[j]
			A.f[16-bytes+j] = buff[j]
		}
		return fell_off

	case AES_OFB1:
		fallthrough
	case AES_OFB2:
		fallthrough
	case AES_OFB4:
		fallthrough
	case AES_OFB8:
		fallthrough
	case AES_OFB16:

		bytes := A.mode - AES_OFB1 + 1
		A.ecb_encrypt(A.f[:])
		for j := 0; j < bytes; j++ {
			buff[j] ^= A.f[j]
		}
		return 0

	case AES_CTR1:
		fallthrough
	case AES_CTR2:
		fallthrough
	case AES_CTR4:
		fallthrough
	case AES_CTR8:
		fallthrough
	case AES_CTR16:
		bytes := A.mode - AES_CTR1 + 1
		for j := 0; j < 16; j++ {
			st[j] = A.f[j]
		}
		A.ecb_encrypt(st[:])
		for j := 0; j < bytes; j++ {
			buff[j] ^= st[j]
		}
		aes_increment(A.f[:])
		return 0

	default:
		return 0
	}
}

/* Decrypt using selected mode of operation */
func (A *AES) Decrypt(buff []byte) uint32 {

	var st [16]byte

	// Supported Modes of Operation

	var fell_off uint32 = 0
	switch A.mode {
	case AES_ECB:
		A.ecb_decrypt(buff)
		return 0
	case AES_CBC:
		for j := 0; j < 16; j++ {
			st[j] = A.f[j]
			A.f[j] = buff[j]
		}
		A.ecb_decrypt(buff)
		for j := 0; j < 16; j++ {
			buff[j] ^= st[j]
			st[j] = 0
		}
		return 0
	case AES_CFB1:
		fallthrough
	case AES_CFB2:
		fallthrough
	case AES_CFB4:
		bytes := A.mode - AES_CFB1 + 1
		for j := 0; j < bytes; j++ {
			fell_off = (fell_off << 8) | uint32(A.f[j])
		}
		for j := 0; j < 16; j++ {
			st[j] = A.f[j]
		}
		for j := bytes; j < 16; j++ {
			A.f[j-bytes] = A.f[j]
		}
		A.ecb_encrypt(st[:])
		for j := 0; j < bytes; j++ {
			A.f[16-bytes+j] = buff[j]
			buff[j] ^= st[j]
		}
		return fell_off
	case AES_OFB1:
		fallthrough
	case AES_OFB2:
		fallthrough
	case AES_OFB4:
		fallthrough
	case AES_OFB8:
		fallthrough
	case AES_OFB16:
		bytes := A.mode - AES_OFB1 + 1
		A.ecb_encrypt(A.f[:])
		for j := 0; j < bytes; j++ {
			buff[j] ^= A.f[j]
		}
		return 0

	case AES_CTR1:
		fallthrough
	case AES_CTR2:
		fallthrough
	case AES_CTR4:
		fallthrough
	case AES_CTR8:
		fallthrough
	case AES_CTR16:
		bytes := A.mode - AES_CTR1 + 1
		for j := 0; j < 16; j++ {
			st[j] = A.f[j]
		}
		A.ecb_encrypt(st[:])
		for j := 0; j < bytes; j++ {
			buff[j] ^= st[j]
		}
		aes_increment(A.f[:])
		return 0

	default:
		return 0
	}
}

/* Clean up and delete left-overs */
func (A *AES) End() { // clean up
	for i := 0; i < 4*(A.Nr+1); i++ {
		A.fkey[i] = 0
		A.rkey[i] = 0
	}
	for i := 0; i < 16; i++ {
		A.f[i] = 0
	}
}

/*
func main() {
	var key [32]byte
	var block [16]byte
	var iv [16]byte

	for i:=0;i<32;i++ {key[i]=0}
	key[0]=1
	for i:=0;i<16;i++ {iv[i]=byte(i)}
	for i:=0;i<16;i++ {block[i]=byte(i)}

	a:=NewAES()

	a.Init(AES_CTR16,32,key[:],iv[:])
	fmt.Printf("Plain= \n")
	for i:=0;i<16;i++  {fmt.Printf("%02X ", block[i]&0xff)}
	fmt.Printf("\n")

	a.Encrypt(block[:])

	fmt.Printf("Encrypt= \n")
	for i:=0;i<16;i++  {fmt.Printf("%02X ", block[i]&0xff)}
	fmt.Printf("\n")

	a.Reset(AES_CTR16,iv[:])
	a.Decrypt(block[:])

	fmt.Printf("Decrypt= \n")
	for i:=0;i<16;i++  {fmt.Printf("%02X ", block[i]&0xff)}
	fmt.Printf("\n")

	a.End();
}
*/
