Skip to main content
ZombieMutex

30 results · ↑ ↓ navigate · Enter open · Esc close

All writing
November 7, 20262 min read

AEAD design review: ciphertext still needs an identity and context

An encrypted database value can be perfectly unreadable to an attacker and still end up in the wrong record. If a service moves ciphertext between tenants without binding the record's context, the system may de

By

Share

Send this article to someone or copy its link.

Suggest a correction

Reader tools

2 min remaining · 0% of article

Select a passage and choose Highlight selection to save it. Local browser storage only. Shift+F toggles focus.

Case file / Retro schematic

Reported sequence and proposed controls

Explore a simplified sequence from the cited research, then switch to defensive checkpoints. These are explanatory diagrams, not live samples, executed code, or original incident telemetry.

Reported sequence

  1. Select record context
  2. Encrypt with AEAD
  3. Store ciphertext + key ID
  4. Verify context on decrypt

Proposed defensive checks

  1. Canonicalize record context
  2. Keep unique nonces
  3. Bind associated data
  4. Authorize decrypt access

Simplified sequence based on cited research; proposed defenses are not evidence that the incident was prevented.

In the AEAD design review I focused on a failure mode that encryption alone does not prevent: moving otherwise valid ciphertext into the wrong tenant or record context. A tag can authenticate bytes while still leaving an application-level identity boundary unclear.

Authenticated encryption with associated data (AEAD) is a useful building block for that problem.

The context gets authenticated, not hidden

Google Tink's AEAD guidance explains that associated data is authenticated alongside the ciphertext, but not encrypted. It can include a tenant identifier, resource identifier, schema version or purpose label. Decryption succeeds only when the associated data supplied at that point matches what was used during encryption.

Here is a contract sketch, not an implementation of AES-GCM:

text
encrypt(
  plaintext = record,
  associatedData = canonical(tenantId, recordId, schemaVersion)
)
→ keyIdentifier + nonce + ciphertext + authenticationTag

decrypt(
  ciphertext,
  associatedData = canonical(requestTenantId, requestedRecordId, schemaVersion)
)
→ plaintext OR authentication failure

The canonical encoding must be versioned. Concatenating arbitrary strings without separators can be ambiguous; silently changing a schema or identifier encoding can make historical records undecryptable.

Three separate mistakes to avoid

  1. Nonce reuse with AES-GCM. Nonce uniqueness under one key is critical; rely on a well-maintained encryption library instead of custom IV handling.
  2. Treating associated data as secret. Tenant IDs and metadata remain visible. Use it for binding, not confidentiality.
  3. Equating successful decryption with authorization. A caller must still be authenticated and permitted to read the record. AEAD detects context tampering; it doesn't enforce your business roles.

Key rotation introduces another state transition: a record needs the identifier of the key that can decrypt it, and a migration policy must preserve compatibility without allowing unsafe downgrades.

How I'd test the contract

Use a vetted AEAD library in a test environment. Encrypt a fixture for tenant A, then attempt decryption under tenant B or a different record ID. Both should fail. Verify legitimate decryption, key-ID handling and tampering behavior separately. Do not paste production key material into a browser demo.

Sources

Scope: My architecture and threat-boundary review. The article analyzes the failure mode; it does not claim a live key-service measurement or a customer system audit.

Related technical notes with overlapping topics.

Discuss a passage

Select a paragraph above, copy it with its article link, then paste into the discussion. Don't include private data.

Jump to discussion ↓

Public conversation / GitHub Discussions

Discuss this article

Read and reply using GitHub Discussions. A GitHub account is needed to post. Comments are public and subject to GitHub moderation.

For privacy, the external giscus widget does not load until you choose to open it. Opening it connects your browser to giscus and GitHub. Do not post credentials or private project data.