AES Encryption Algorithm Explained: From Principles to Implementation

AES Encryption Algorithm Explained: From Principles to Implementation

AES (Advanced Encryption Standard) is one of the most widely used symmetric encryption algorithms. It was officially adopted by the National Institute of Standards and Technology (NIST) in 2001, replacing the old DES algorithm. AES is a block cipher algorithm that supports three key lengths: 128-bit, 192-bit, and 256-bit.

I. Overview of AES Algorithm

Feature Description
Algorithm Type Symmetric Block Cipher
Block Size 128 bits (fixed)
Key Size 128/192/256 bits
Number of Rounds 10/12/14 rounds (corresponding to different key lengths)
Security Extremely high, no effective attack methods currently

II. AES Encryption Process

The AES encryption process mainly includes the following steps:

2.1 Initial Round (AddRoundKey)

Perform bitwise XOR operation between the plaintext block and the first round key:

state = state XOR roundKey[0]

2.2 Main Round Transformations (repeated N-1 times)

Each round contains four transformations:

  1. SubBytes: Byte substitution using S-box
  2. ShiftRows: Row shifting operation
  3. MixColumns: Column mixing operation
  4. AddRoundKey: XOR with round key

2.3 Final Round (without MixColumns)

The last round contains only three transformations:

  1. SubBytes
  2. ShiftRows
  3. AddRoundKey

III. Detailed Core Transformations

3.1 SubBytes: Byte Substitution

SubBytes uses a fixed lookup table (S-box) to perform non-linear substitution on each byte:

// AES S-Box example (partial)
const sBox = [
  [0x63, 0x7c, 0x77, 0x7b, ...],
  [0xf2, 0x6b, 0x6f, 0xc5, ...],
  // ... total 16x16 bytes
];

function subBytes(state) {
  for (let i = 0; i < 16; i++) {
    const row = (state[i] >> 4) & 0x0f;
    const col = state[i] & 0x0f;
    state[i] = sBox[row][col];
  }
  return state;
}

3.2 ShiftRows: Row Shifting

ShiftRows performs cyclic shifting on the rows of the matrix, with row i shifted left by i positions:

// Input state matrix (4x4)
// [ s0 s4 s8  s12 ]
// [ s1 s5 s9  s13 ]
// [ s2 s6 s10 s14 ]
// [ s3 s7 s11 s15 ]

function shiftRows(state) {
  // Row 0: no shift
  // Row 1: shift left by 1
  [state[1], state[5], state[9], state[13]] = 
    [state[5], state[9], state[13], state[1]];
  // Row 2: shift left by 2
  [state[2], state[6], state[10], state[14]] = 
    [state[10], state[14], state[2], state[6]];
  // Row 3: shift left by 3
  [state[3], state[7], state[11], state[15]] = 
    [state[15], state[3], state[7], state[11]];
  return state;
}

3.3 MixColumns: Column Mixing

MixColumns performs linear transformation on each column of the matrix using a fixed polynomial:

function mixColumns(state) {
  const matrix = [
    [2, 3, 1, 1],
    [1, 2, 3, 1],
    [1, 1, 2, 3],
    [3, 1, 1, 2]
  ];
  
  const newState = new Uint8Array(16);
  
  for (let col = 0; col < 4; col++) {
    for (let row = 0; row < 4; row++) {
      let sum = 0;
      for (let i = 0; i < 4; i++) {
        sum ^= gfMultiply(matrix[row][i], state[col * 4 + i]);
      }
      newState[col * 4 + row] = sum;
    }
  }
  
  return newState;
}

// Galois field multiplication
function gfMultiply(a, b) {
  let result = 0;
  for (let i = 0; i < 8; i++) {
    if (b & 1) result ^= a;
    const carry = a & 0x80;
    a = (a << 1) & 0xff;
    if (carry) a ^= 0x1b; // AES polynomial
    b >>= 1;
  }
  return result;
}

3.4 AddRoundKey: Round Key Addition

Perform bitwise XOR between the state and the round key:

function addRoundKey(state, roundKey) {
  for (let i = 0; i < 16; i++) {
    state[i] ^= roundKey[i];
  }
  return state;
}

IV. Key Expansion

AES uses a key expansion algorithm to expand the original key into multiple round keys:

function expandKey(key) {
  const keyLen = key.length;
  const rounds = keyLen === 16 ? 10 : (keyLen === 24 ? 12 : 14);
  const expanded = new Uint8Array(16 * (rounds + 1));
  
  // Copy original key
  expanded.set(key);
  
  for (let i = keyLen; i < expanded.length; i += 4) {
    let temp = expanded.slice(i - 4, i);
    
    // Transform every 4 bytes
    if (i % keyLen === 0) {
      temp = subWord(rotWord(temp));
      temp[0] ^= rcon[i / keyLen];
    } else if (keyLen > 24 && i % keyLen === 16) {
      temp = subWord(temp);
    }
    
    for (let j = 0; j < 4; j++) {
      expanded[i + j] = expanded[i + j - keyLen] ^ temp[j];
    }
  }
  
  return expanded;
}

function rotWord(word) {
  return [word[1], word[2], word[3], word[0]];
}

function subWord(word) {
  return word.map(byte => sBox[(byte >> 4) & 0x0f][byte & 0x0f]);
}

V. Complete AES Encryption Implementation

class AES {
  constructor(key) {
    this.key = key;
    this.rounds = key.length === 16 ? 10 : (key.length === 24 ? 12 : 14);
    this.expandedKey = this.expandKey(key);
  }
  
  encrypt(plaintext) {
    // Initial round
    let state = this.addRoundKey(plaintext, this.expandedKey.slice(0, 16));
    
    // Main rounds
    for (let round = 1; round < this.rounds; round++) {
      state = this.subBytes(state);
      state = this.shiftRows(state);
      state = this.mixColumns(state);
      state = this.addRoundKey(state, this.expandedKey.slice(round * 16, (round + 1) * 16));
    }
    
    // Final round
    state = this.subBytes(state);
    state = this.shiftRows(state);
    state = this.addRoundKey(state, this.expandedKey.slice(this.rounds * 16));
    
    return state;
  }
  
  // ... other methods (subBytes, shiftRows, mixColumns, addRoundKey, expandKey)
}

// Usage example
const key = new Uint8Array(16); // 128-bit key
const aes = new AES(key);
const plaintext = new Uint8Array(16); // 128-bit plaintext
const ciphertext = aes.encrypt(plaintext);

VI. AES Operating Modes

AES itself is a block cipher and needs to work with operating modes to handle data of arbitrary length:

Mode Features Security Applicable Scenario
ECB Same plaintext produces same ciphertext Low Not recommended
CBC Requires initialization vector (IV) Medium General scenarios
CFB Stream cipher mode Medium Streaming data
OFB Stream cipher mode, no error propagation Medium Network transmission
CTR Stream cipher mode, parallel encryption High High-performance scenarios
GCM Authenticated encryption mode High Scenarios requiring authentication

VII. Frontend Implementation Example

Modern browsers natively support AES using the Web Crypto API:

async function aesEncrypt(key, data, iv) {
  const cryptoKey = await window.crypto.subtle.importKey(
    'raw',
    key,
    { name: 'AES-GCM' },
    false,
    ['encrypt']
  );
  
  const ciphertext = await window.crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    cryptoKey,
    data
  );
  
  return ciphertext;
}

async function aesDecrypt(key, ciphertext, iv) {
  const cryptoKey = await window.crypto.subtle.importKey(
    'raw',
    key,
    { name: 'AES-GCM' },
    false,
    ['decrypt']
  );
  
  const plaintext = await window.crypto.subtle.decrypt(
    { name: 'AES-GCM', iv },
    cryptoKey,
    ciphertext
  );
  
  return plaintext;
}

// Usage example
const key = window.crypto.getRandomValues(new Uint8Array(16)); // 128-bit key
const iv = window.crypto.getRandomValues(new Uint8Array(12)); // GCM recommended 12-byte IV
const data = new TextEncoder().encode('Hello, AES!');

const ciphertext = await aesEncrypt(key, data, iv);
const decrypted = await aesDecrypt(key, ciphertext, iv);
console.log(new TextDecoder().decode(decrypted)); // "Hello, AES!"

πŸ’‘ Tip:It is recommended to use AES-GCM mode, which provides both encryption and authentication functions and can detect whether data has been tampered with.

VIII. Security Best Practices

  • Key Management: Keys must be kept secret, avoid hardcoding
  • IV Usage: Use a random IV for each encryption, IV can be public
  • Mode Selection: Prefer GCM or CTR mode, avoid ECB
  • Key Derivation: Use PBKDF2 or scrypt to derive keys from passwords
  • Authentication: Ensure data integrity, use authenticated encryption modes

IX. Summary

AES is the cornerstone of modern cryptography. Understanding its principles is crucial for secure development. The security of AES is based on its complex round function design and finite field operations, and there are currently no effective attack methods.

In actual development, the browser's native Web Crypto API or verified third-party libraries should be used first, and self-implemented encryption algorithms should be avoided. Proper key management and mode selection are equally important.