/** diagnostic trace of static tables. */ public static void trace_static() { int i, j; System.out.print("AES Static Tablesn"); System.out.print("S[] = n"); for (i = 0; i < 16; i++) { for (j = 0; j < 16; j++) System.out.print(Util.toHEX1(S[i * 16 + j]) + ", "); System.out.println(); } System.out.print("Si[] = n"); for (i = 0; i < 16; i++) { for (j = 0; j < 16; j++) System.out.print(Util.toHEX1(Si[i * 16 + j]) + ", "); System.out.println(); } System.out.print("rcon[] = n"); for (i = 0; i < 5; i++) { for (j = 0; j < 6; j++) System.out.print(Util.toHEX1(rcon[i * 6 + j]) + ", "); System.out.println(); } System.out.print("log[] = n"); for (i = 0; i < 32; i++) { for (j = 0; j < 8; j++) System.out.print(Util.toHEX1(log[i * 8 + j]) + ", "); System.out.println(); } System.out.print("alog[] = n"); for (i = 0; i < 32; i++) { for (j = 0; j < 8; j++) System.out.print(Util.toHEX1(alog[i * 8 + j]) + ", "); System.out.println(); } }
/** * Expand a user-supplied key material into a session key. * * <p>See FIPS-197 Section 5.3 Fig 11 for details of the key expansion. * * <p>Session keys will be saved in Ke and Kd instance variables, along with numRounds being the * number of rounds for this sized key. * * @param key The 128/192/256-bit AES key to use. */ @SuppressWarnings("unused") public void setKey(byte[] key) { // assorted internal constants final int BC = BLOCK_SIZE / 4; final int Klen = key.length; final int Nk = Klen / 4; int i, j, r; traceInfo = ""; // reset trace info if (traceLevel > 0) traceInfo = "setKey(" + Util.toHEX1(key) + ")n"; // check for bad arguments if (key == null) throw new IllegalArgumentException("Empty key"); if (!(key.length == 16 || key.length == 24 || key.length == 32)) throw new IllegalArgumentException("Incorrect key length"); // set master number of rounds given size of this key numRounds = getRounds(Klen); final int ROUND_KEY_COUNT = (numRounds + 1) * BC; // allocate 4 arrays of bytes to hold the session key values // each array holds 1 of the 4 bytes [b0 b1 b2 b3] in each word w byte[] w0 = new byte[ROUND_KEY_COUNT]; byte[] w1 = new byte[ROUND_KEY_COUNT]; byte[] w2 = new byte[ROUND_KEY_COUNT]; byte[] w3 = new byte[ROUND_KEY_COUNT]; // allocate arrays to hold en/decrypt session keys (by byte rather than word) Ke = new byte[numRounds + 1][BLOCK_SIZE]; // encryption round keys Kd = new byte[numRounds + 1][BLOCK_SIZE]; // decryption round keys // copy key into start of session array (by word, each byte in own array) for (i = 0, j = 0; i < Nk; i++) { w0[i] = key[j++]; w1[i] = key[j++]; w2[i] = key[j++]; w3[i] = key[j++]; } // implement key expansion algorithm byte t0, t1, t2, t3, old0; // temp byte values for each word for (i = Nk; i < ROUND_KEY_COUNT; i++) { t0 = w0[i - 1]; t1 = w1[i - 1]; t2 = w2[i - 1]; t3 = w3[i - 1]; // temp = w[i-1] if (i % Nk == 0) { // temp = SubWord(RotWord(temp)) ^ Rcon[i/Nk] old0 = t0; // save old 1st byte value for t3 calc t0 = (byte) (S[t1 & 0xFF] ^ rcon[i / Nk]); // nb. constant XOR 1st byte only t1 = (byte) (S[t2 & 0xFF]); t2 = (byte) (S[t3 & 0xFF]); // nb. RotWord done by reordering bytes used t3 = (byte) (S[old0 & 0xFF]); } else if ((Nk > 6) && (i % Nk == 4)) { // temp = SubWord(temp) t0 = S[t0 & 0xFF]; t1 = S[t1 & 0xFF]; t2 = S[t2 & 0xFF]; t3 = S[t3 & 0xFF]; } // w[i] = w[i-Nk] ^ temp w0[i] = (byte) (w0[i - Nk] ^ t0); w1[i] = (byte) (w1[i - Nk] ^ t1); w2[i] = (byte) (w2[i - Nk] ^ t2); w3[i] = (byte) (w3[i - Nk] ^ t3); } // now copy values into en/decrypt session arrays by round & byte in round for (r = 0, i = 0; r < numRounds + 1; r++) { // for each round for (j = 0; j < BC; j++) { // for each word in round Ke[r][4 * j] = w0[i]; Ke[r][4 * j + 1] = w1[i]; Ke[r][4 * j + 2] = w2[i]; Ke[r][4 * j + 3] = w3[i]; Kd[numRounds - r][4 * j] = w0[i]; Kd[numRounds - r][4 * j + 1] = w1[i]; Kd[numRounds - r][4 * j + 2] = w2[i]; Kd[numRounds - r][4 * j + 3] = w3[i]; i++; } } // create trace info if needed if (traceLevel > 3) { traceInfo += " Encrypt Round keys:n"; for (r = 0; r < numRounds + 1; r++) traceInfo += " R" + r + "t = " + Util.toHEX1(Ke[r]) + "n"; traceInfo += " Decrypt Round keys:n"; for (r = 0; r < numRounds + 1; r++) traceInfo += " R" + r + "t = " + Util.toHEX1(Kd[r]) + "n"; } }
/** * AES decrypt 128-bit ciphertext using key previously set. * * <p>Follows cipher specification given in FIPS-197 section 5.3 See pseudo code in Fig 5, and * details in this section. * * @param cipher the 128-bit ciphertext value to decrypt. * @return the decrypted 128-bit plaintext value. */ public byte[] decrypt(byte[] cipher) { // define working variables byte[] a = new byte[BLOCK_SIZE]; // AES state variable byte[] ta = new byte[BLOCK_SIZE]; // AES temp state variable byte[] Kdr; // encrypt keys for current round int i, k, row, col; traceInfo = ""; // reset trace info if (traceLevel > 0) traceInfo = "decryptAES(" + Util.toHEX1(cipher) + ")"; // check for bad arguments if (cipher == null) throw new IllegalArgumentException("Empty ciphertext"); if (cipher.length != BLOCK_SIZE) throw new IllegalArgumentException("Incorrect ciphertext length"); // copy ciphertext bytes into state and do initial AddRoundKey(state) Kdr = Kd[0]; for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte) (cipher[i] ^ Kdr[i]); if (traceLevel > 2) traceInfo += "n R0 (Key = " + Util.toHEX1(Kdr) + ")nt AK = " + Util.toHEX1(a); else if (traceLevel > 1) traceInfo += "n R0 (Key = " + Util.toHEX1(Kdr) + ")t = " + Util.toHEX1(a); // for each round except last, apply round transforms for (int r = 1; r < numRounds; r++) { Kdr = Kd[r]; // get session keys for this round if (traceLevel > 1) traceInfo += "n R" + r + " (Key = " + Util.toHEX1(Kdr) + ")t"; // InvShiftRows(state) into ta (nb. same shift as encrypt but subtract) for (i = 0; i < BLOCK_SIZE; i++) { row = i % COL_SIZE; // get shifted byte index k = (i + BLOCK_SIZE - (row_shift[row] * COL_SIZE)) % BLOCK_SIZE; ta[i] = a[k]; } if (traceLevel > 2) traceInfo += "ntISR = " + Util.toHEX1(ta); // InvSubBytes(state) into a using inverse S-box Si for (i = 0; i < BLOCK_SIZE; i++) a[i] = Si[ta[i] & 0xFF]; if (traceLevel > 2) traceInfo += "ntISB = " + Util.toHEX1(a); // AddRoundKey(state) into ta for (i = 0; i < BLOCK_SIZE; i++) ta[i] = (byte) (a[i] ^ Kdr[i]); if (traceLevel > 2) traceInfo += "nt AK = " + Util.toHEX1(ta); // InvMixColumns(state) into a // implemented by expanding matrix mult for each column // see FIPS-197 section 5.3.3 for (col = 0; col < NUM_COLS; col++) { i = col * COL_SIZE; // start index for this col a[i] = (byte) (mul(0x0e, ta[i]) ^ mul(0x0b, ta[i + 1]) ^ mul(0x0d, ta[i + 2]) ^ mul(0x09, ta[i + 3])); a[i + 1] = (byte) (mul(0x09, ta[i]) ^ mul(0x0e, ta[i + 1]) ^ mul(0x0b, ta[i + 2]) ^ mul(0x0d, ta[i + 3])); a[i + 2] = (byte) (mul(0x0d, ta[i]) ^ mul(0x09, ta[i + 1]) ^ mul(0x0e, ta[i + 2]) ^ mul(0x0b, ta[i + 3])); a[i + 3] = (byte) (mul(0x0b, ta[i]) ^ mul(0x0d, ta[i + 1]) ^ mul(0x09, ta[i + 2]) ^ mul(0x0e, ta[i + 3])); } if (traceLevel > 2) traceInfo += "ntIMC"; if (traceLevel > 1) traceInfo += " = " + Util.toHEX1(a); } // last round is special - only has InvShiftRows, InvSubBytes and AddRoundKey Kdr = Kd[numRounds]; // get session keys for final round if (traceLevel > 1) traceInfo += "n R" + numRounds + " (Key = " + Util.toHEX1(Kdr) + ")t"; // InvShiftRows(state) into ta for (i = 0; i < BLOCK_SIZE; i++) { row = i % COL_SIZE; // get shifted byte index k = (i + BLOCK_SIZE - (row_shift[row] * COL_SIZE)) % BLOCK_SIZE; ta[i] = a[k]; } if (traceLevel > 2) traceInfo += "ntISR = " + Util.toHEX1(a); // InvSubBytes(state) into ta using inverse S-box Si for (i = 0; i < BLOCK_SIZE; i++) ta[i] = Si[ta[i] & 0xFF]; if (traceLevel > 2) traceInfo += "ntISB = " + Util.toHEX1(a); // AddRoundKey(state) into a for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte) (ta[i] ^ Kdr[i]); if (traceLevel > 2) traceInfo += "nt AK"; if (traceLevel > 1) traceInfo += " = " + Util.toHEX1(a) + "n"; if (traceLevel > 0) traceInfo += " = " + Util.toHEX1(a) + "n"; return (a); }
/** * AES encrypt 128-bit plaintext using key previously set. * * <p>Follows cipher specification given in FIPS-197 section 5.1 See pseudo code in Fig 5, and * details in this section. * * @param plain the 128-bit plaintext value to encrypt. * @return the encrypted 128-bit ciphertext value. */ public byte[] encrypt(byte[] plain) { // define working variables byte[] a = new byte[BLOCK_SIZE]; // AES state variable byte[] ta = new byte[BLOCK_SIZE]; // AES temp state variable byte[] Ker; // encrypt keys for current round int i, k, row, col; traceInfo = ""; // reset trace info if (traceLevel > 0) traceInfo = "encryptAES(" + Util.toHEX1(plain) + ")"; // check for bad arguments if (plain == null) throw new IllegalArgumentException("Empty plaintext"); if (plain.length != BLOCK_SIZE) throw new IllegalArgumentException("Incorrect plaintext length"); // copy plaintext bytes into state and do initial AddRoundKey(state) Ker = Ke[0]; for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte) (plain[i] ^ Ker[i]); if (traceLevel > 2) traceInfo += "n R0 (Key = " + Util.toHEX1(Ker) + ")ntAK = " + Util.toHEX1(a); else if (traceLevel > 1) traceInfo += "n R0 (Key = " + Util.toHEX1(Ker) + ")t = " + Util.toHEX1(a); // for each round except last, apply round transforms for (int r = 1; r < numRounds; r++) { Ker = Ke[r]; // get session keys for this round if (traceLevel > 1) traceInfo += "n R" + r + " (Key = " + Util.toHEX1(Ker) + ")t"; // SubBytes(state) into ta using S-Box S for (i = 0; i < BLOCK_SIZE; i++) ta[i] = S[a[i] & 0xFF]; if (traceLevel > 2) traceInfo += "ntSB = " + Util.toHEX1(ta); // ShiftRows(state) into a for (i = 0; i < BLOCK_SIZE; i++) { row = i % COL_SIZE; k = (i + (row_shift[row] * COL_SIZE)) % BLOCK_SIZE; // get shifted byte index a[i] = ta[k]; } if (traceLevel > 2) traceInfo += "ntSR = " + Util.toHEX1(a); // MixColumns(state) into ta // implemented by expanding matrix mult for each column // see FIPS-197 section 5.1.3 for (col = 0; col < NUM_COLS; col++) { i = col * COL_SIZE; // start index for this col ta[i] = (byte) (mul(2, a[i]) ^ mul(3, a[i + 1]) ^ a[i + 2] ^ a[i + 3]); ta[i + 1] = (byte) (a[i] ^ mul(2, a[i + 1]) ^ mul(3, a[i + 2]) ^ a[i + 3]); ta[i + 2] = (byte) (a[i] ^ a[i + 1] ^ mul(2, a[i + 2]) ^ mul(3, a[i + 3])); ta[i + 3] = (byte) (mul(3, a[i]) ^ a[i + 1] ^ a[i + 2] ^ mul(2, a[i + 3])); } if (traceLevel > 2) traceInfo += "ntMC = " + Util.toHEX1(ta); // AddRoundKey(state) into a for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte) (ta[i] ^ Ker[i]); if (traceLevel > 2) traceInfo += "ntAK"; if (traceLevel > 1) traceInfo += " = " + Util.toHEX1(a); } // last round is special - only has SubBytes, ShiftRows and AddRoundKey Ker = Ke[numRounds]; // get session keys for final round if (traceLevel > 1) traceInfo += "n R" + numRounds + " (Key = " + Util.toHEX1(Ker) + ")t"; // SubBytes(state) into a using S-Box S for (i = 0; i < BLOCK_SIZE; i++) a[i] = S[a[i] & 0xFF]; if (traceLevel > 2) traceInfo += "ntSB = " + Util.toHEX1(a); // ShiftRows(state) into ta for (i = 0; i < BLOCK_SIZE; i++) { row = i % COL_SIZE; k = (i + (row_shift[row] * COL_SIZE)) % BLOCK_SIZE; // get shifted byte index ta[i] = a[k]; } if (traceLevel > 2) traceInfo += "ntSR = " + Util.toHEX1(a); // AddRoundKey(state) into a for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte) (ta[i] ^ Ker[i]); if (traceLevel > 2) traceInfo += "ntAK"; if (traceLevel > 1) traceInfo += " = " + Util.toHEX1(a) + "n"; if (traceLevel > 0) traceInfo += " = " + Util.toHEX1(a) + "n"; return (a); }