Skip to content
Blog

AES-GCM vs CBC vs CTR: Differences and Which to Use

Compare AES-GCM, AES-CBC, and AES-CTR security, authentication, nonce requirements, and performance. Learn why AES-GCM is the safest default for new apps.

Published Updated

For new applications, prefer AES-GCM: it encrypts and authenticates data in one operation. AES-CBC and AES-CTR provide confidentiality but must be paired with a correctly implemented message authentication code. Never reuse a nonce or IV with GCM or CTR under the same key.

This guide compares the modes and provides JavaScript examples using the Web Crypto API.


What Are AES Encryption Modes?

AES encryption modes define how the AES algorithm processes data blocks. AES encrypts data in fixed-size blocks (128 bits), and the mode determines how these blocks are handled, especially for data larger than one block. Let’s explore six common modes: AES-GCM, AES-CBC, AES-CTR, AES-ECB, AES-CFB, and AES-OFB.

AES Modes Overview


1. AES-GCM (Galois/Counter Mode)

What is AES-GCM?

AES-GCM is a mode that combines encryption with authentication. It ensures data confidentiality and verifies that the data hasn’t been tampered with. It’s widely used in secure communication protocols like TLS.

AES-GCM Characteristics

Aspect Details
Key Features Authentication with built-in tag, uses a 12-byte nonce, parallelizable, used in HTTPS and disk encryption.
Pros Provides encryption and authentication, resistant to padding oracle attacks, efficient for modern applications.
Cons Requires unique nonce to avoid security risks, slightly complex to implement correctly.

Code Example (AES-GCM in JavaScript):

async function encryptAESGCM(plaintext, key) {
  const encoder = new TextEncoder();
  const data = encoder.encode(plaintext);
  const iv = crypto.getRandomValues(new Uint8Array(12)); // 12-byte nonce
  const cryptoKey = await crypto.subtle.importKey("raw", key, { name: "AES-GCM" }, false, ["encrypt"]);
  const encrypted = await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, cryptoKey, data);
  return { iv, ciphertext: new Uint8Array(encrypted) };
}

async function decryptAESGCM(ciphertext, key, iv) {
  const cryptoKey = await crypto.subtle.importKey( "raw", key, { name: "AES-GCM" }, false, ["decrypt"]);
  const decrypted = await crypto.subtle.decrypt({ name: "AES-GCM", iv: iv }, cryptoKey, ciphertext);
  return new TextDecoder().decode(decrypted);
}

// Usage
(async () => {
  const key = crypto.getRandomValues(new Uint8Array(16)); // 128-bit key
  const plaintext = "Hello, AES-GCM!";
  const { iv, ciphertext } = await encryptAESGCM(plaintext, key);
  console.log("Encrypted:", ciphertext);
  const decrypted = await decryptAESGCM(ciphertext, key, iv);
  console.log("Decrypted:", decrypted);
})();

2. AES-CBC (Cipher Block Chaining)

What is AES-CBC?

AES-CBC is one of the oldest AES modes. It chains blocks together, where each block’s encryption depends on the previous block’s ciphertext. It requires an initialization vector (IV) to start the chain.

AES-CBC Characteristics

Aspect Details
Key Features Uses a 16-byte random IV, block dependency, used in legacy systems and file encryption.
Pros Simple and widely supported, secure with a random IV.
Cons No built-in authentication, not parallelizable, susceptible to padding oracle attacks.

Code Example (AES-CBC in JavaScript):

async function encryptAESCBC(plaintext, key) {
  const encoder = new TextEncoder();
  const data = encoder.encode(plaintext);
  const iv = crypto.getRandomValues(new Uint8Array(16)); // 16-byte IV
  const cryptoKey = await crypto.subtle.importKey("raw", key, { name: "AES-CBC" }, false, ["encrypt"]);
  const encrypted = await crypto.subtle.encrypt({ name: "AES-CBC", iv: iv }, cryptoKey, data);
  return { iv, ciphertext: new Uint8Array(encrypted) };
}

async function decryptAESCBC(ciphertext, key, iv) {
  const cryptoKey = await crypto.subtle.importKey("raw", key, { name: "AES-CBC" }, false, ["decrypt"]);
  const decrypted = await crypto.subtle.decrypt({ name: "AES-CBC", iv: iv }, cryptoKey, ciphertext);
  return new TextDecoder().decode(decrypted);
}

// Usage
(async () => {
  const key = crypto.getRandomValues(new Uint8Array(16)); // 128-bit key
  const plaintext = "Hello, AES-CBC!";
  const { iv, ciphertext } = await encryptAESCBC(plaintext, key);
  console.log("Encrypted:", ciphertext);
  const decrypted = await decryptAESCBC(ciphertext, key, iv);
  console.log("Decrypted:", decrypted);
})();

3. AES-CTR (Counter Mode)

What is AES-CTR?

AES-CTR turns AES into a stream cipher. It uses a counter combined with a nonce to encrypt data, making it suitable for streaming applications.

AES-CTR Characteristics

Aspect Details
Key Features Uses a 16-byte nonce with a counter, stream cipher, used in streaming and disk encryption.
Pros Fast and parallelizable, no padding required, suitable for streaming data.
Cons No built-in authentication, nonce reuse is catastrophic for security.

Code Example (AES-CTR in JavaScript):

async function encryptAESCTR(plaintext, key) {
  const encoder = new TextEncoder();
  const data = encoder.encode(plaintext);
  const iv = crypto.getRandomValues(new Uint8Array(16)); // 16-byte nonce
  const cryptoKey = await crypto.subtle.importKey("raw", key, { name: "AES-CTR" }, false, ["encrypt"]);
  const encrypted = await crypto.subtle.encrypt({ name: "AES-CTR", counter: iv, length: 128 }, cryptoKey,data);
  return { iv, ciphertext: new Uint8Array(encrypted) };
}

async function decryptAESCTR(ciphertext, key, iv) {
  const cryptoKey = await crypto.subtle.importKey("raw", key, { name: "AES-CTR" }, false, ["decrypt"]);
  const decrypted = await crypto.subtle.decrypt({ name: "AES-CTR", counter: iv, length: 128 }, cryptoKey, ciphertext);
  return new TextDecoder().decode(decrypted);
}

// Usage
(async () => {
  const key = crypto.getRandomValues(new Uint8Array(16)); // 128-bit key
  const plaintext = "Hello, AES-CTR!";
  const { iv, ciphertext } = await encryptAESCTR(plaintext, key);
  console.log("Encrypted:", ciphertext);
  const decrypted = await decryptAESCTR(ciphertext, key, iv);
  console.log("Decrypted:", decrypted);
})();

4. AES-ECB (Electronic Codebook)

What is AES-ECB?

AES-ECB is the simplest AES mode, where each block is encrypted independently using the same key. It does not use an IV or nonce, making it predictable for identical plaintext blocks.

AES-ECB Characteristics

Aspect Details
Key Features No IV/nonce, block independence, used in legacy systems (rarely).
Pros Extremely simple to implement, fast due to independent block processing.
Cons Insecure due to pattern leakage, no authentication, not recommended.

Code Example (AES-ECB in JavaScript):

Note: The Web Crypto API does not support AES-ECB due to its insecurity. Below is a conceptual example using a library like crypto-js for demonstration purposes only. Do not use ECB in production.

const CryptoJS = require("crypto-js");

function encryptAESECB(plaintext, key) {
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  const encrypted = CryptoJS.AES.encrypt(plaintext, keyWordArray, {
    mode: CryptoJS.mode.ECB,
    padding: CryptoJS.pad.Pkcs7
  });
  return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}

function decryptAESECB(ciphertext, key) {
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  const decrypted = CryptoJS.AES.decrypt(
    { ciphertext: CryptoJS.enc.Base64.parse(ciphertext) },
    keyWordArray,
    { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
  );
  return decrypted.toString(CryptoJS.enc.Utf8);
}

// Usage
const key = "1234567890123456"; // 16-byte key
const plaintext = "Hello, AES-ECB!";
const ciphertext = encryptAESECB(plaintext, key);
console.log("Encrypted:", ciphertext);
const decrypted = decryptAESECB(ciphertext, key);
console.log("Decrypted:", decrypted);

5. AES-CFB (Cipher Feedback)

What is AES-CFB?

AES-CFB turns AES into a stream cipher by using the previous ciphertext to encrypt the next block. It requires an IV to start the process.

AES-CFB Characteristics

Aspect Details
Key Features Uses a 16-byte random IV, stream cipher, used in streaming and communication.
Pros No padding required, suitable for streaming, more secure than ECB.
Cons No built-in authentication, slower than CTR, error propagation in blocks.

Code Example (AES-CFB in JavaScript):

Note: The Web Crypto API does not support AES-CFB. Below is a conceptual example using crypto-js.

const CryptoJS = require("crypto-js");

function encryptAESCFB(plaintext, key) {
  const iv = CryptoJS.lib.WordArray.random(16); // 16-byte IV
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  const encrypted = CryptoJS.AES.encrypt(plaintext, keyWordArray, {
    iv: iv,
    mode: CryptoJS.mode.CFB,
    padding: CryptoJS.pad.NoPadding
  });
  return { iv: iv.toString(CryptoJS.enc.Base64), ciphertext: encrypted.ciphertext.toString(CryptoJS.enc.Base64) };
}

function decryptAESCFB(ciphertext, key, iv) {
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  const ivWordArray = CryptoJS.enc.Base64.parse(iv);
  const decrypted = CryptoJS.AES.decrypt(
    { ciphertext: CryptoJS.enc.Base64.parse(ciphertext) },
    keyWordArray,
    { iv: ivWordArray, mode: CryptoJS.mode.CFB, padding: CryptoJS.pad.NoPadding }
  );
  return decrypted.toString(CryptoJS.enc.Utf8);
}

// Usage
const key = "1234567890123456"; // 16-byte key
const plaintext = "Hello, AES-CFB!";
const { iv, ciphertext } = encryptAESCFB(plaintext, key);
console.log("Encrypted:", ciphertext);
const decrypted = decryptAESCFB(ciphertext, key, iv);
console.log("Decrypted:", decrypted);

6. AES-OFB (Output Feedback)

What is AES-OFB?

AES-OFB also turns AES into a stream cipher. It generates a keystream by repeatedly encrypting the IV, which is then XORed with the plaintext.

AES-OFB Characteristics

Aspect Details
Key Features Uses a 16-byte random IV, stream cipher, used in low-error environments.
Pros No padding required, suitable for streaming, errors affect only corresponding bits.
Cons No built-in authentication, nonce reuse is dangerous, less common.

Code Example (AES-OFB in JavaScript):

Note: The Web Crypto API does not support AES-OFB. Below is a conceptual example using crypto-js.

const CryptoJS = require("crypto-js");

function encryptAESOFB(plaintext, key) {
  const iv = CryptoJS.lib.WordArray.random(16); // 16-byte IV
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  const encrypted = CryptoJS.AES.encrypt(plaintext, keyWordArray, {
    iv: iv,
    mode: CryptoJS.mode.OFB,
    padding: CryptoJS.pad.NoPadding
  });
  return { iv: iv.toString(CryptoJS.enc.Base64), ciphertext: encrypted.ciphertext.toString(CryptoJS.enc.Base64) };
}

function decryptAESOFB(ciphertext, key, iv) {
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  const ivWordArray = CryptoJS.enc.Base64.parse(iv);
  const decrypted = CryptoJS.AES.decrypt(
    { ciphertext: CryptoJS.enc.Base64.parse(ciphertext) },
    keyWordArray,
    { iv: ivWordArray, mode: CryptoJS.mode.OFB, padding: CryptoJS.pad.NoPadding }
  );
  return decrypted.toString(CryptoJS.enc.Utf8);
}

// Usage
const key = "1234567890123456"; // 16-byte key
const plaintext = "Hello, AES-OFB!";
const { iv, ciphertext } = encryptAESOFB(plaintext, key);
console.log("Encrypted:", ciphertext);
const decrypted = decryptAESOFB(ciphertext, key, iv);
console.log("Decrypted:", decrypted);

Comparison of AES-GCM, AES-CBC, AES-CTR, AES-ECB, AES-CFB, AES-OFB

Feature AES-GCM AES-CBC AES-CTR AES-ECB AES-CFB AES-OFB
Type Authenticated Encryption Block Cipher Stream Cipher Block Cipher Stream Cipher Stream Cipher
Authentication Yes (built-in tag) No No No No No
IV/Nonce Nonce (12 bytes) IV (16 bytes) Nonce (16 bytes) None IV (16 bytes) IV (16 bytes)
Parallelizable Yes No Yes Yes No No
Padding Required No Yes No Yes No No
Delegate TLS, disk encryption Legacy systems, files Streaming, disk encryption Rarely used (insecure) Streaming, communication Low-error environments
Security Concerns Nonce reuse Padding oracle attacks Nonce reuse Pattern leakage Error propagation Nonce reuse

Which Mode Should You Use?

Always use a unique IV/nonce for each encryption and ensure proper key management to maintain security.


Conclusion

AES-GCM, AES-CBC, AES-CTR, AES-ECB, AES-CFB, and AES-OFB offer different trade-offs. AES-GCM is preferred for modern secure applications due to its authentication. AES-CBC is reliable for legacy use, while AES-CTR, AES-CFB, and AES-OFB suit streaming scenarios. AES-ECB should be avoided due to its security flaws. Understanding these modes helps you choose the right one for your project.


References


Web Crypto APIaes encryption modesaes encryptionAESAES-GCMAES-CBCAES-CTRGALOIS COUNTER MODECIPHER BLOCK CHAININGCOUNTER MODE