by Neal R. Wagner
Copyright © 2001 by Neal R. Wagner. All rights reserved.
NOTE: This site is obsolete. See book draft (in PDF):
The previous 4 subsections have covered most of the details needed to complete the AES algorithm. One still needs a description and code for the following routines:
In the first two parts, the AES is moving around and ``stirring up'' data in the 4-by-4 array of bytes named state.
The action of shifting rows is particularly simple, just performing left circular shifts of rows 1, 2, and 3, by amounts of 1, 2, and 3 bytes. Row 0 is not changed. The actual Java code below does this.
void ShiftRows(byte[][] state) {
byte[] t = new byte[4];
for (int r = 1; r < 4; r++) {
for (int c = 0; c < Nb; c++)
t[c] = state[r][(c + r)%Nb];
for (int c = 0; c < Nb; c++)
state[r][c] = t[c];
}
}
The action of mixing columns works on the columns of the state array, but it is much more complicated that the shift columns action. As described in the AES specification, it treats each column as a four-term polynomial with coefficients in the field GF(28). All this is similar to the description of the field itself, except with an extra layer of complexity. These polynomials are added and multiplied just using the operations of the field GF(28) on the coefficients, except that the result of a multiplication, which is a polynomial of degree up to 6, must be reduced by dividing by the polynomial x4 + 1 and taking the remainder.
The columns are each multiplied by the fixed polynomial:
a(x) = (03)x3 + (01)x2 +(01)x + (02),where (03) stands for the field element 0x03. This sounds horrible, but mathematical manipulations can reduce everything to the following simple algorithm, where multiplication in the field is represented below by #. The principle change needed to convert this to actual Java is to replace # with a call to FFMul(). (Gladman gives a shorter but more obscure version of this code.)
void MixColumns(byte[][] s) {
byte[] sp = new byte[4];
for (int c = 0; c < 4; c++) {
sp[0] = (0x02 # s[0][c]) ^ (0x03 # s[1][c]) ^ s[2][c] ^ s[3][c];
sp[1] = s[0][c] ^ (0x02 # s[1][c]) ^ (0x03 # s[2][c]) ^ s[3][c];
sp[2] = s[0][c] ^ s[1][c] ^ (0x02 # s[2][c]) ^ (0x03 # s[3][c]);
sp[3] = (0x03 # s[0][c]) ^ s[1][c] ^ s[2][c] ^ (0x02 # s[3][c]);
for (int i = 0; i < 4; i++) s[i][c] = sp[i];
}
}
As described before, portions of the expanded key w are exclusive-ored onto the state matrix Nr+1 times (once for each round plus one more time). There are 4*Nb bytes of state, and since each byte of the expanded key is used exactly once, the expanded key size of 4*Nb*(Nr+1) bytes is just right. The expanded key is used, byte by byte, from lowest to highest index, so there is no need to count the bytes as they are used from w, but just use them up and move on, as the following near-Java code shows. This code assumes the key has already been expanded into the array w, and it assumes a global counter wCount initialized to 0. The function AddRoundKey uses up 4*Nb = 16 bytes of expanded key every time it is called.
void AddRoundKey(byte[][] state) {
for (int c = 0; c < Nb; c++)
for (int r = 0; r < 4; r++)
state[r][c] = state[r][c] ^ w[wCount++];
}
My Java implementation of AES encryption uses four classes:
Here is a combined listing of all the ecryption classes: Java Implementation of AES Encryption.
See the next section for test runs of the program, where encryption is followed by decryption.