iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Symmetric Ciphers

Symmetric crypto uses one shared key for encrypt + decrypt. Modern choices: AES-256-GCM (hardware-accelerated, FIPS) or ChaCha20-Poly1305 (faster on mobile). Both are authenticated — tampering breaks decryption.

AES-GCM, ChaCha20, key derivation

EXAMPLE
// 1) AES-256-GCM in Node — the daily-driver
import crypto from 'node:crypto';

function aesGcmEncrypt(plaintext, key, aad) {
    const iv = crypto.randomBytes(12);           // 96-bit nonce — MUST be unique per encryption
    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
    if (aad) cipher.setAAD(aad);                 // authenticated additional data
    const ct = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
    const tag = cipher.getAuthTag();             // 16 bytes
    // Pack: iv || ct || tag
    return Buffer.concat([iv, ct, tag]).toString('base64');
}

function aesGcmDecrypt(packed, key, aad) {
    const buf = Buffer.from(packed, 'base64');
    const iv  = buf.subarray(0, 12);
    const tag = buf.subarray(buf.length - 16);
    const ct  = buf.subarray(12, buf.length - 16);
    const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
    if (aad) decipher.setAAD(aad);
    decipher.setAuthTag(tag);
    return Buffer.concat([decipher.update(ct), decipher.final()]).toString('utf8');
}

const key = crypto.randomBytes(32);              // 256-bit key — STORE THIS SAFELY
const packed = aesGcmEncrypt('hello world', key);
console.log(aesGcmDecrypt(packed, key));

// 2) ChaCha20-Poly1305 — preferred when AES hw is uncertain (mobile, IoT)
import sodium from 'libsodium-wrappers';
await sodium.ready;

const key2 = sodium.crypto_aead_chacha20poly1305_ietf_keygen();
const nonce = sodium.randombytes_buf(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES);
const ct2 = sodium.crypto_aead_chacha20poly1305_ietf_encrypt('hello', null, null, nonce, key2);
const pt2 = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(null, ct2, null, nonce, key2);

// 3) Python — cryptography (AESGCM)
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

key = AESGCM.generate_key(bit_length=256)
aes = AESGCM(key)
nonce = os.urandom(12)
ct = aes.encrypt(nonce, b'hello', associated_data=b'ctx')
pt = aes.decrypt(nonce, ct, associated_data=b'ctx')

// 4) PHP — libsodium (recommended)
$key   = sodium_crypto_aead_chacha20poly1305_ietf_keygen();
$nonce = random_bytes(SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);
$ct    = sodium_crypto_aead_chacha20poly1305_ietf_encrypt('hello', '', $nonce, $key);
$pt    = sodium_crypto_aead_chacha20poly1305_ietf_decrypt($ct, '', $nonce, $key);

// 5) Java — javax.crypto with AES/GCM/NoPadding
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

byte[] iv = new byte[12]; new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
byte[] ct = cipher.doFinal(plaintext);

// === Key derivation — when the key comes from a password / shared secret ===

// 6) Argon2id — derive a strong key from a low-entropy password
import argon2 from 'argon2';
const derived = await argon2.hash(password, {
    type:        argon2.argon2id,
    raw:         true,
    salt:        crypto.randomBytes(16),
    memoryCost:  19 * 1024,
    timeCost:    2,
    hashLength:  32,
});
// derived = 32 bytes — use as a symmetric key

// 7) HKDF — derive a key from a high-entropy shared secret (e.g. ECDH output)
const sharedSecret = crypto.diffieHellman({ privateKey: myPriv, publicKey: theirPub });
const aesKey = crypto.hkdfSync('sha256', sharedSecret, salt, info, 32);

// === Operational essentials ===

// 8) Nonce rules — non-negotiable
//   • Never REUSE a (key, nonce) pair. Same pair + GCM = leaks plaintext XOR. Catastrophic.
//   • Random 96-bit nonces are safe for ~2^32 messages per key. After that, ROTATE.
//   • For more messages on one key: deterministic counter nonces, or XChaCha20 (192-bit).

// 9) Authenticated encryption (AEAD) — ALWAYS
//   AES-GCM and ChaCha20-Poly1305 verify integrity automatically.
//   AES-CBC + HMAC by hand: easy to mess up. Don't.
//   AES-ECB: leaks structure (the famous penguin meme). NEVER.

// 10) Key storage
//   • KMS / Secrets Manager / Vault — not env vars, not in code
//   • Envelope encryption: master key wraps data keys; rotate keys without re-encrypting data
//   • Hardware: HSM / TPM / Apple Secure Enclave / Android Keystore for highest assurance

// 11) Common API patterns
//   • Encrypt-only: data in DB columns (per-tenant key in KMS, ciphertext in column)
//   • Encrypt-then-MAC for legacy (AES-CBC + HMAC-SHA256, constant-time compare)
//   • Hybrid: RSA / X25519 wraps an AES key; AES encrypts the payload

// 12) Cryptographic agility
//   • Prefix the ciphertext with an algorithm + version byte: '01' = AES-GCM-256
//   • Rotation path: bump the version; decrypt old, re-encrypt new
//   • Don't hard-code algorithm choice into the API — wrap it

// 13) Things to AVOID
//   • Rolling your own crypto (build on libsodium / cryptography / WebCrypto / @noble)
//   • Using Math.random / Date.now for nonces (use CSPRNG)
//   • Reusing keys across services without rotation
//   • Storing both key and ciphertext in the same place without separating access
//   • Sending the IV / nonce out of band — pack it with the ciphertext
//   • Reinventing AES-GCM — there's always a subtle nonce bug waiting

// 14) When you need PERFECT FORWARD SECRECY
//   • Don't reuse a long-term key for messages — derive an ephemeral session key (ECDH + HKDF + AES)
//   • This is how Signal / TLS 1.3 / Noise protocol work

// 15) Quick decision
//   Encrypt small data (< few KB): AES-256-GCM
//   Encrypt streaming / large data: AES-GCM in chunks (one nonce per chunk) OR XChaCha20-Poly1305
//   Mobile / embedded: ChaCha20-Poly1305 — no AES hardware dependency
//   FIPS / regulated: AES-256-GCM (compliant)

Why it matters

AES-256-GCM + 96-bit random nonces, key from a CSPRNG, keys in a KMS — that’s the modern symmetric stack. Nonce reuse breaks everything, so never invent your own scheme.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Same key encrypts and decrypts. Use AEAD modes only.
//   AES-GCM       — fastest with hardware AES (most servers)
//   ChaCha20-Poly1305 — best for mobile / no AES-NI
// Avoid bare CBC / ECB / CTR — they don't authenticate.
Try it Yourself »

Discussion

Loading…