Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MACs & AEAD — HMAC, Poly1305, AES-GCM

🔐 Used in: TLS, APIs, cookies, tokens, VPNs, messaging apps

✅ Essential for integrity and authenticity, not optional.

Encryption hides data. But encryption alone does not stop attackers from modifying it.

Modern cryptography requires two guarantees:

  • Confidentiality → Nobody can read the data
  • Integrity & Authenticity → Nobody can tamper with it unnoticed

This chapter is about the second half — the part beginners forget, and attackers exploit.

Why Encryption Alone Is Not Enough

A common beginner mistake: “My data is encrypted, so it’s secure.” That’s false.

If an attacker can flip bits in your ciphertext and you don’t detect it, your system is broken.

Real consequences:

  • Modified database records
  • Forged API requests1
  • Token manipulation2
  • Padding-oracle exploits3
  • Silent data corruption4

Encryption without integrity is malleable by default.

This is why MACs and AEAD exist.

MACs (Message Authentication Codes)

A MAC is a cryptographic checksum computed using a secret key.

It answers one critical question:

“Was this message produced by someone who knows the secret key — and was it modified?”

MACs provide:

  • Integrity (detect modification)
  • Authenticity (prove origin)
  • No confidentiality (data remains readable)

A MAC is not encryption.

message + secret key → MAC

How to verify:

message + secret key → recomputed MAC → compare

If even one bit changes, verification fails.

HMAC (Hash-based Message Authentication Code) — The Standard MAC Construction

💡 Used in JWT5, APIs6, OAuth7, AWS signing8, TLS internals9

Stable, conservative, battle-tested

My Crate Logo Crate used: hmac

HMAC allows two parties sharing a secret key to authenticate a message and detect any tampering, without requiring encryption.

HMAC combines:

  • a cryptographic hash function (e.g. SHA-256)
  • a secret key
  • a hardened construction resistant to length-extension attacks10

Unlike naïve hash(key || message), HMAC is safe.

🧪 Code Example: HMAC-SHA256 (source code)

#![allow(unused)]
fn main() {
pub fn run_hmac_example() {
    use hkdf::hmac::Hmac;
    use hkdf::hmac::digest::Mac;
    use sha2::Sha256;

    type HmacSha256 = Hmac<Sha256>;

    let key = b"super-secret-key";
    let message = b"transfer=1000&to=alice";

    let mut mac = <HmacSha256 as Mac>::new_from_slice(key).unwrap();
    Mac::update(&mut mac, message);

    let tag = mac.finalize().into_bytes();

    // Verification
    let mut verify = <HmacSha256 as Mac>::new_from_slice(key).unwrap();
    Mac::update(&mut verify, message);
    Mac::verify_slice(verify, tag.as_slice()).unwrap();
}
}

If the message or tag is altered, verification fails immediately.

🚨 Critical rule
Never compare MACs with ==. Always use constant-time verification APIs.

🟢 Conclusion

HMAC is conservative, widely deployed, and extremely hard to misuse.

If you need integrity without encryption — HMAC is the right tool.

Poly1305 — One-Time MAC for Modern Crypto

💡 Used in ChaCha20-Poly1305, TLS 1.3, WireGuard

Extremely fast, simple, and timing-safe

My Crate Logo Crate used: poly1305

Poly1305 is a modern MAC designed by Daniel J. Bernstein.

Key properties:

  • One-time MAC (key must never be reused)
  • Constant-time11 by design
  • Very small and fast
  • Designed to pair with stream ciphers12

Poly1305 is almost never used alone. It is generated from a cipher keystream, usually ChaCha20.

🧪 Code Example: Poly1305 (source code)

#![allow(unused)]
fn main() {
pub fn run_poly1305_example() {
    use poly1305::{
        Poly1305,
        universal_hash::{KeyInit, UniversalHash},
    };

    let key = [0u8; 32]; // placeholder: must be a one-time key in real use
    let message = b"authenticated message";

    let mut mac = Poly1305::new(&key.into());
    mac.update_padded(message);

    let tag = mac.finalize();
    let _tag_bytes: [u8; 16] = tag.into(); // if plain array wished
}
}
🚨 Critical rule
Poly1305 keys must never be reused. Reuse = forgery.

🟢 Conclusion

Poly1305 is fast, elegant, and extremely secure when used correctly, but it must be paired with a cipher that guarantees fresh keys.

AEAD — Authenticated Encryption (The Right Way)

Modern cryptography does not ask: “Should I encrypt or authenticate?”.

The answer is: Both. Together. Always.

AEAD guarantees:

  • Confidentiality
  • Integrity
  • Authenticity
  • Optional authentication of unencrypted metadata

If authentication fails → decryption must not happen.

AES-GCM — The Enterprise Standard AEAD

💡 Used in TLS, HTTPS, databases, cloud storage, hardware security modules, hardware-accelerated and widely standardized

My Crate Logo Crates used: aes-gcm

AES-GCM13 combines:

  • AES block cipher14
  • CTR mode15 (for encryption)
  • GHASH16 (for authentication)

🧪 Code Example: AES-256-GCM (source code)

#![allow(unused)]
fn main() {
pub fn run_aes256gcm_example() {
    use aes_gcm::aead::{Aead, KeyInit};
    use aes_gcm::{Aes256Gcm, Key, Nonce};

    let key = Key::<Aes256Gcm>::from_slice(&[0u8; 32]);
    let cipher = Aes256Gcm::new(key);

    let nonce = Nonce::from_slice(&[0u8; 12]);
    let ciphertext = cipher.encrypt(nonce, b"secret data".as_ref()).unwrap();

    let plaintext = cipher.decrypt(nonce, ciphertext.as_ref()).unwrap();
    println!("AES256-GCM: {:#?}", plaintext);
}
}

Output:

AES256-GCM: [
    115,
    101,
    99,
    114,
    101,
    116,
    32,
    100,
    97,
    116,
    97,
]
🚨 Critical rule
Never reuse a nonce with the same key. Ever. GCM nonce reuse = total compromise.

🟢 Conclusion

AES-GCM is extremely fast on modern CPUs and ideal for servers, but nonce management must be flawless.

ChaCha20-Poly1305 — The Safer Default

💡 Used in WireGuard17, mobile apps, embedded systems, TLS fallback18

Designed for misuse resistance19

ChaCha20-Poly130520 combines:

  • ChaCha2021 (encryption)
  • Poly130522 (authentication)
  • A clean, unified AEAD API23

Advantages:

  • Constant-time11 by design
  • No cache-timing24 issues
  • Excellent performance everywhere
  • Fewer catastrophic mistakes

🧪 Code Example: ChaCha20-Poly1305 (source code)

#![allow(unused)]
fn main() {
pub fn run_chacha20poly1305_example() {
    use chacha20poly1305::aead::{Aead, KeyInit};
    use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce};

    let key = Key::from_slice(&[0u8; 32]);
    let cipher = ChaCha20Poly1305::new(key);

    let nonce = Nonce::from_slice(&[0u8; 12]);
    let ciphertext = cipher.encrypt(nonce, b"secret data".as_ref()).unwrap();

    let plaintext = cipher.decrypt(nonce, ciphertext.as_ref()).unwrap();
    println!("Chacha20-Poly1305: {:#?}", plaintext);
}
}

Output:

Chacha20-Poly1305: [
    115,
    101,
    99,
    114,
    101,
    116,
    32,
    100,
    97,
    116,
    97,
]

🟢 Conclusion

ChaCha20-Poly1305 is often the best default choice: safer APIs, portable performance, and strong resistance to side-channel attacks25.

MAC vs AEAD — What Should You Use?

SituationUse
Integrity onlyHMAC
Streaming cipherPoly1305 (with ChaCha20)
General encryptionAEAD
Enterprise systemsAES-GCM
Mobile / embeddedChaCha20-Poly1305
🚨 Critical rule
If encryption is involved → always use AEAD. Rolling your own MAC + encryption is a mistake.

🟢 Conclusion

Encryption alone only hides data — it doesn’t stop attackers from changing it.

A MAC teaches the integrity lesson: “was this message modified, and did it come from someone with the key?”

Poly1305 shows modern MAC design: fast and safe when used correctly (one-time keys).

AEAD (AES-GCM, ChaCha20-Poly1305) combines encryption + authentication so tampering is detected and decryption is refused.

Practical rule: if you encrypt data in real systems, use AEAD by default and treat nonce/key management as a first-class security requirement.


  1. Forged API Request — Attack where an adversary crafts or alters API requests to impersonate a legitimate client or bypass authentication and authorization controls. More

  2. Token Manipulation — Tampering with authentication or session tokens (JWT, cookies, API keys) to escalate privileges, extend validity, or impersonate another user. More

  3. Padding Oracle Attack — Cryptographic attack exploiting padding validation errors in block ciphers to progressively recover plaintext or forge valid ciphertexts. More

  4. Silent Data Corruption — Undetected modification of data caused by hardware faults, software bugs, or transmission errors, leading to integrity loss without immediate failure signals. More

  5. JWT (JSON Web Token) — Compact, URL-safe token format used to securely transmit signed or encrypted claims for authentication and authorization in distributed systems. More

  6. API (Application Programming Interface) — Contract defining how software components communicate via structured requests, responses, authentication, and versioned endpoints. More

  7. OAuth 2.0 — Industry-standard authorization framework enabling delegated access to protected resources without sharing user credentials with third-party applications. More

  8. AWS Request Signin — Cryptographic mechanism (SigV4) that authenticates and authorizes AWS API requests using HMAC-based signatures derived from secret credentials. More

  9. TLS Internals — Cryptographic protocols and handshake mechanisms that provide authentication, key exchange, confidentiality, and integrity for secure network communications. More

  10. Length-Extension Attack — Hash function vulnerability allowing attackers to append data to a hashed message and compute a valid hash without knowing the secret. More

  11. Constant-Time Algorithm — Implementation strategy where execution time is independent of secret data, preventing timing side-channel information leakage. More ↩2

  12. Stream Cipher — Symmetric encryption primitive that generates a pseudorandom keystream and encrypts data by XORing it with plaintext bytes. More

  13. AES-GCM — Standard AEAD using AES-CTR for encryption plus GHASH for authentication; fast but requires unique nonces per key. More

  14. AES Block Cipher — AES is a 128-bit block cipher primitive; you still need a mode/AEAD (CTR, GCM, etc.) to encrypt messages safely. More

  15. CTR Mode — Counter mode turns a block cipher into a keystream generator; it’s fast but malleable and must be paired with authentication. More

  16. GHASH — The polynomial-hash authenticator inside AES-GCM, computed over AAD and ciphertext to help produce the authentication tag. More

  17. WireGuard — Modern VPN protocol using a small, fixed set of strong primitives (notably ChaCha20-Poly1305) for high performance and simplicity. More

  18. TLS Fallback — Choosing an alternative cipher suite during TLS negotiation (often ChaCha20-Poly1305 on non-AES hardware) without sacrificing authenticated encryption. More

  19. Misuse Resistance — Design goal where common mistakes (especially nonce reuse) cause less catastrophic failure; still not “safe to misuse,” just safer. More

  20. ChaCha20-Poly1305 — Widely deployed AEAD combining ChaCha20 encryption with Poly1305 authentication; strong, fast in software, and common in TLS and WireGuard. More

  21. ChaCha20 — Fast ARX-based stream cipher; encrypts by XORing a generated keystream with plaintext; nonce reuse under the same key breaks confidentiality. More

  22. Poly1305 — One-time MAC producing a 16-byte tag; secure only if the Poly1305 key is never reused, typically derived fresh per message. More

  23. AEAD API — Standard encrypt/decrypt interface using (key, nonce, AAD, plaintext/ciphertext) and returning success or authentication failure; never release plaintext on failure. More

  24. Cache Timing — Timing leakage caused by secret-dependent CPU cache accesses (e.g., lookup tables), which can reveal keys; mitigated by constant-time code and AES-NI. More

  25. Side-Channel Attack — Attack that exploits leaked information from an implementation (timing, cache, power, EM, faults) rather than breaking the underlying cryptography. More