Like in AES encryption, we separate key expansion away from decryption. We must call updateKey() before use a new cipher key to decrypt message.
Based on similar consideration in AES encryption implementation, we also can merge inverse SubBytes and Inverse MixColumns into one look-up table as long as operation flow is re-ordered appropriately. Therefore, we adopt that Inverse MixColumns and AddRoundKey are exchanged with each other in one operation round. However, generated key at KeyExpansion stage must be followed by one extra Inverse MixColumns operation for correct decryption. It’s worth to do because of hardware overhead of matrix multiplication in GF(2^8) will be reduced obviously. Furthermore, this addition operation will be executed only once for most case which plenty of blocks are decrypted with sharing the same cipher key. The optimzed flow is shown as below.
Since each round of process needs one round key in reverse order, and that indicateds the dependency between KeyExpansion and decryption process. KeyExpansion is separated from the whole decryption loop.
In addition, in order to eliminate unnecessary inverse SubBytes operation in the common loop-up table within decryption process, one same SubBytes process should be operated on round keys before they come into Inverse MixColumns step. For the last round in one block decryption, Inverse MixColumns will be skipped.