AES-128 Encryption Process: A Step-by-Step Breakdown

One of my favorite acronyms in the industry is CCMP-AES. It’s quite a mouthful: Counter Mode Cipher Block Chaining Message Authentication Code Protocol with Advanced Encryption Standard. Isn’t that cool?

For those looking for context, CCMP-AES is one of the security mechanisms defined in the 802.11i (RSN) standard. It provides a secure way for devices to send encrypted, authenticated traffic while ensuring data integrity.

Here are the key components:

  • AES (Advanced Encryption Standard)
  • Counter Mode
  • Cipher Block Chaining
  • Message Authentication Code

There’s plenty of information online, but my goal is to break it down into smaller, digestible chunks. Eventually, I aim to write code that interacts with real network devices, enabling encrypted communication both ways. Let’s start with AES, this post will talk about the encryption process, getting a little deeper in the steps involved.

AES

History

In 1997, the National Institute of Standards and Technology (NIST) in the United States announced its search for a successor to the Data Encryption Standard (DES). The requirements were clear: the new algorithm needed to be a symmetric block cipher, implementable in both hardware and software. Interestingly, the name “Advanced Encryption Standard” (AES) was already defined in the announcement document, titled “Announcing Development of a Federal Information Processing Standard for Advanced Encryption Standard” (NIST Announcement).

The algorithm Rijndael, developed by Belgian cryptographers Joan Daemen and Vincent Rijmen, won the competition. In 2001, it was formally adopted as the encryption standard AES (FIPS PUB 197) (FIPS PUB 197).

Basics

So, what is AES used for? AES is an encryption standard that converts blocks of plaintext into ciphertext. It uses a subkey the same length as the block of data to be encrypted (128 bits). AES supports key lengths of 128, 192, and 256 bits, but the block size is fixed at 128 bits.

To better understand AES encryption, let’s break down the steps of the AES-128 process. Keep in mind that the key schedule slightly differs for AES-192 and AES-256.

The following diagram summarizes the AES-128 encryption blocks:

Key Expansion

AES begins with a key expansion algorithm, which generates a series of 128-bit subkeys from the initial input key. These subkeys are used in different rounds of encryption.

Initial Transformation

The plaintext and the input key are each converted into a matrix format, known as the state matrix, which is a 4×4 grid. Before proceeding with further transformations, a bitwise XOR operation is performed between the plaintext matrix and the key matrix.

Here is the initial setup for this example:

  • Key: 1HundredwireKey
  • Plaintext: 1HundredwireWiFi

Matrix Conversion

Both the key and plaintext are represented as a 4×4 matrix:

4 x 4 Matrix with the Key

4 x 4 Matrix the Text

The format which is the AES algorithm works is transposed, so each column will represent a row.

Transposed key and input text

The AES algorithm uses a transposed format, where each column in the state matrix represents a row of the input data.

Key Matrix and input text represented in Hex

Before entering the round loop, an AddRoundKey operation is performed. This involves a XOR operation between the plaintext matrix and the key matrix.

XOR (exclusive-OR) is a bitwise operation that works as follows

For example, the element (6E) at position [0][3] in the Key Matrix will be XORed with the element at position [0][3] in the Text Matrix, which is also (6E).

  • Key Matrix [0][3]: 6E (HEX) → 1101110 (BIN)
  • Text Matrix [0][3]: 6E (HEX) → 1101110 (BIN)

The XOR result is 0x00, as XOR-ing identical binary values results in zero.

In this example, the key and text matrices share identical bytes except for the last four. This similarity results in a large number of zeros during the first AddRoundKey operation.

Now, let’s perform the XOR operation on the last column, where the bytes differ:

  • Key Matrix [3][0]: 4B (HEX) → 01001011 (BIN)
  • Text Matrix [3][0]: 57 (HEX) → 01010111 (BIN)

The XOR result for these differing bytes will showcase a non-zero output, highlighting the changes introduced by the key.

The XOR will be 1C.

The AddRoundKey will result in:

Now, this matrix (referred to as the State) will undergo several rounds, during which four transformations are applied.

The first transformation is SubBytes, which is straightforward: each byte in the State is replaced using a predefined lookup table called the S-box. This substitution is performed byte-by-byte, based on the values specified in the S-box.

Below is an image from the NIST AES standard that illustrates the S-box.

S-Box

After applying the SubBytes operation to our table, we end up with numerous 63s due to the presence of many 0s

State after Subbyte Round 1

At this point, our State is still not well mixed, and it even reveals that we’ve chosen a key that is somewhat similar to the text we want to encrypt.

Now, let’s move on to the next step of the round:

ShiftRows

This transformation cyclically shifts the values of the rows. The first row remains unchanged, while the other rows are shifted to the left by 1, 2, and 3 positions, respectively.

Since we have many identical values, it may be difficult to observe the effect of each individual byte. Let’s look at the example below:

State Matrix before ShiftRows
ShiftRow cyclical effect

The state Matrix will be:

State Matrix after ShiftRows

So far, I’ve left out an important detail: all of AES operates using elements from a finite field, where addition and multiplication follow different rules compared to regular numbers. I won’t dive into the details of finite fields here, but it’s important to note that AES uses the finite field GF(2^8).

In this field, both addition and subtraction are equivalent to XOR operations.

Hex 63 in binary is (01100011) in the polynomial notation is x6+x5+x+1.

Let’s do the multiplication of 63 with 02 (00000010)

2 in polynomial format is x

(x6+x5+x+1)*(x)

The result is (x7+x6+x2+x) which is good because it inside the finite field of GF(28) where the max element is

x7+ x6+x5+x4+x3+x2+x+1.

(x7+x6+x2+x) in binary is 11000110 in hex is C6

But, what if we multiple it by 2 again?

(x7+x6+x2+x) * x = (x8+x7+x6+x2)

Now we have a problem, this element is not part of the finite field GF(28). We have to perform the modulo operation using a irreducible polynomial for the AES algorithm the polynomial is x8+x4+x3+x+1.

(x8+x7+x6+x2) modulo x8+x4+x3+x+1 equals x7+x4+x2+x+1 in binary it is 10010111 and hex 97.

Returning to AES, the MixColumns operation is applied to the State matrix.

We take the first column of the State matrix and multiply each element by the corresponding element in the multiplication matrix. This procedure is repeated for every column of the State matrix.

As an example, let’s calculate the last item of the first column in the MixColumns operation. It’s calculated as:

(63 * 03) ⊕ (63 * 01) ⊕ (63 * 01) ⊕ (A0 * 02).

The polynomial for of each element is

01 is 1,

02 is x

03 is x+1

A0 is x7+x5

63 is x6+x5+x+1

63 * 03 = (x6+x5+x+1)(x+1) = x7+x5+x2+1

63 * 01 = (x6+x5+x+1)

A0 * 02 = (x7+x5)(x) = x6+x4+x3+x+1

Let’s XOR

The result of the XOR is 253 (FE)

After doing the whole mix column process we get:

Result of MixColumns round 1

The next step after finishing the MixColumn is the AddRoundKey which is a XOR operation between the state matrix and round key. On this post i will not explain how the keys are generated but for each round a different key is used.

The first Round key is:

Round 1 key

After performing the XOR operation with this, we will obtain:

Result of the first round

This process is repeated 9 times for AES-128.

Afterward, the final round involves SubBytes, ShiftRows, and AddRoundKey using the last round key.

The image below summarizes the steps used in the encryption process, as outlined in the NIST AES specification.

At the end of the process, the expected ciphertext is: 6d8b1121c2ba4cd539b11712bc39eb87.

In the next post, I will explain the Key Expansion algorithm and walk through some decryption steps.

I’ve developed a simple implementation of AES-128 in Python. You can check it out to learn more about the implementation details: https://github.com/dcapassi/python_AES-128

In summary, AES encryption operates in the finite field known as Galois Field GF(2^8). With a series of relatively simple operations, it is possible to create a robust cipher.

Thanks,

Diego Capassi

References
Advanced Encryption Standard (AES) FIPS-197 – https://doi.org/10.6028/NIST.FIPS.197-upd1