Introduction

Every time you log into a website, buy something online, or send a message, your data travels through the internet wrapped in encryption. Most people assume that encryption is a single shield: it keeps secrets and prevents tampering. That assumption is wrong — and the gap it leaves has caused real disasters.

Confidentiality means an eavesdropper cannot read your data. Integrity means nobody can change it without you knowing. Plain encryption, such as AES in counter mode, gives you confidentiality alone. An attacker who intercepts the ciphertext cannot read it — but they can flip arbitrary bits, and those flips land in predictable places in the decrypted plaintext.

Authenticated Encryption with Associated Data (AEAD) is the modern answer. It bundles encryption and a cryptographic authentication tag into one operation. If even a single bit of the ciphertext (or its unencrypted metadata) is modified, the tag verification fails and the receiver learns nothing about the tampered plaintext — the message is simply rejected.

The most widely deployed AEAD scheme today is AES-GCM (Galois/Counter Mode), which became the standard after NIST's 2007 recommendation SP 800-38D. It powers TLS 1.3, SSH, QUIC, and most modern encrypted storage.

Try It: Flip a Bit

The demo below simulates AES-GCM authenticated encryption (using a simplified toy version safe for illustration). Type a message, encrypt it, then click any ciphertext byte to flip a bit and watch the authentication tag fail.

<!-- {{c_html_comment}} -->
<p class="hint">{{hint_para}}</p>
<div class="row">
  <input id="plaintext" type="text" maxlength="24" placeholder="{{input_placeholder}}" autocomplete="off" spellcheck="false">
  <button id="btn-encrypt" type="button">{{btn_encrypt}}</button>
</div>
<div id="cipher-area" class="hidden">
  <div class="label-row">
    <span class="label">{{label_nonce}}</span>
    <span class="label">{{label_ciphertext}}</span>
    <span class="label">{{label_tag}}</span>
  </div>
  <div class="bytes-row">
    <div id="nonce-bytes" class="byte-strip nonce-strip"></div>
    <div id="cipher-bytes" class="byte-strip cipher-strip"></div>
    <div id="tag-bytes" class="byte-strip tag-strip"></div>
  </div>
  <p class="hint small">{{hint_click_to_flip}}</p>
  <div class="row">
    <button id="btn-decrypt" type="button">{{btn_decrypt}}</button>
    <button id="btn-reset-bits" type="button" class="ghost">{{btn_restore}}</button>
  </div>
</div>
<div id="status" class="status"></div>
<div id="plaintext-out" class="plaintext-out hidden"></div>
/* {{c_css_comment}} */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; }
.hint { font-size: .88rem; color: #444; margin: 0 0 .7rem; line-height: 1.5; }
.hint.small { font-size: .8rem; color: #666; margin: .4rem 0; }
.row { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .8rem; align-items: center; }
input { flex: 1; min-width: 160px; font: 15px system-ui, sans-serif; padding: .42rem .7rem;
        border: 1px solid #adb1b8; border-radius: 8px; outline: none; }
input:focus { border-color: #1d3557; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
         background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; white-space: nowrap; }
button.ghost { background: #fff; color: #1d3557; }
.label-row { display: flex; gap: .5rem; margin-bottom: .25rem; }
.label { font-size: .75rem; font-weight: 600; color: #555; text-transform: uppercase; letter-spacing: .04em; }
.bytes-row { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: .25rem; }
.byte-strip { display: flex; flex-wrap: wrap; gap: 3px; }
.byte { width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
        font: 700 10px ui-monospace, monospace; border-radius: 5px; user-select: none; }
.nonce-strip .byte { background: #e8eef3; color: #1d3557; border: 1px solid #cdd9e3; }
.cipher-strip .byte { background: #c9ccd1; color: #1d3557; border: 1px solid #adb1b8; cursor: pointer; transition: background .12s; }
.cipher-strip .byte:hover { background: #b0b5bd; }
.cipher-strip .byte.flipped { background: #e63946; color: #fff; border-color: #c92f3c; }
.tag-strip .byte { background: #d0eedd; color: #0a5c33; border: 1px solid #9ed4b5; }
.tag-strip .byte.bad { background: #ffd6d6; color: #c92f3c; border-color: #f5a5a5; }
.hidden { display: none; }
.status { font-size: 1rem; font-weight: 600; min-height: 1.4em; margin: .4rem 0; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
.plaintext-out { font: 600 1rem ui-monospace, monospace; padding: .5rem .8rem;
                 background: #f0faf4; border: 1px solid #9ed4b5; border-radius: 8px;
                 margin-top: .4rem; letter-spacing: .05em; }
// Code not found

Notice two things. First, the ciphertext looks like random noise — confidentiality is working. Second, the moment you flip any bit the tag mismatch is detected immediately — integrity kicks in. The receiver never sees a corrupted plaintext; the decryption step is simply refused.

This is the critical insight: checking the tag takes O(n)O(n) time and is done before handing the decrypted bytes to the application. There is no way to sneak a tampered message past an honest AEAD implementation without knowing the secret key.

The Security Model

AEAD has a precise two-goal security model studied since Bellare and Namprempre's 2000 paper "Authenticated Encryption: Relations among notions and analysis of the generic composition paradigm":

  • IND-CPA (confidentiality): an adversary who can ask for encryptions of chosen plaintexts learns nothing about the plaintext from the ciphertext.
  • INT-CTXT (ciphertext integrity): an adversary cannot produce any ciphertext that the receiver will accept as valid unless it was produced by the sender — regardless of what it decrypts to.

Together these two properties imply IND-CCA2 security — the strongest standard notion of encryption security — meaning chosen-ciphertext attacks also fail.

The nonce is the fragile pin. AES-GCM is only secure if the nonce (a 96-bit initialisation vector) is never reused under the same key. If an attacker sees two ciphertexts encrypted with the same key and nonce, they can XOR them together, cancelling the keystream, and recover the XOR of the two plaintexts. Worse, they can forge arbitrary valid ciphertexts. The authentication tag in GCM is a polynomial evaluation over GF(2128)\text{GF}(2^{128}); nonce reuse makes that polynomial solvable.

This is not a theoretical concern. In 2012, Sony PlayStation 3's firmware signing used the same nonce for every signature, allowing researchers to extract the private key. TLS libraries have been caught with nonce-reuse bugs. The lesson: nonce uniqueness is a hard requirement, not a best practice.

Encrypt-then-MAC vs. MAC-then-Encrypt. Before AEAD primitives existed, engineers composed encryption and MAC manually. The order matters:

  • Encrypt-then-MAC (EtM): cipher the plaintext, then MAC the ciphertext. The receiver verifies the MAC before decrypting — safe.
  • MAC-then-Encrypt (MtE): MAC the plaintext, then encrypt both. The receiver must decrypt before verifying — exposes the decryption oracle to a padding-oracle attack (the 2011 BEAST attack on TLS exploited this).

AEAD schemes like AES-GCM implement EtM semantics internally, which is why the tag is always checked first.

See also: factoring for the asymmetric side of modern cryptography, and learning with errors for the post-quantum alternative.

Where It Matters

AEAD is not a niche primitive — it is the workhorse of modern secure communications:

  • TLS 1.3: the handshake establishes a shared key, then all records are protected with AES-128-GCM or ChaCha20-Poly1305. Earlier TLS versions that used MAC-then-Encrypt were repeatedly broken (BEAST, Lucky13, POODLE).
  • Disk encryption: Linux LUKS2, macOS FileVault 2, and Android Full Disk Encryption all use AES-GCM or XTS-AES (a closely related authenticated mode) so that offline bit-flipping attacks on the encrypted volume are detected.
  • Messaging apps: Signal, WhatsApp, and iMessage use the Double Ratchet algorithm, whose symmetric ratchet uses AES-GCM for each individual message.
  • QUIC / HTTP3: the new transport protocol builds AEAD into the packet header itself, so even packet numbers are authenticated.
  • Cloud storage SDKs: AWS, GCP, and Azure all ship envelope encryption libraries that wrap every stored object in AES-GCM with a fresh random nonce.

When it breaks. Real-world failures almost always involve one of three mistakes: nonce reuse (Sony PS3, Australian government e-passport); forgetting to authenticate associated data (metadata fields sent in the clear); or truncating the tag below 96 bits to save bandwidth, making forgery easier.

Conclusion

Encryption and authentication answer different questions. Encryption asks: can the attacker read this? Authentication asks: can the attacker modify this undetected? For decades these were treated as separate concerns, and the gap between them was exploited again and again.

AEAD closes that gap with a single primitive. Its security model is tight, its performance on modern hardware is one CPU cycle per byte, and its failure mode — a rejected message — is always safe. The only fragile point is the nonce: use it twice and the entire construction collapses.

So the next time your browser shows a padlock, remember it is not just hiding your data from eavesdroppers. The authentication tag is silently guaranteeing that every byte you receive is exactly the byte the server sent — and one flip would have torn the curtain.

Share this article

Pick a channel — or use your device's native share sheet.

Comments

Loading comments...

https://www.kipuhub.com/en/article/authenticated-encryption/Content licensed under CC BY-NC 4.0.