Skip to content

AEAD architecture: FAuthObj + multi-call GCM (from PR #90 concern A) - #102

Merged
MHumm merged 3 commits into
MHumm:developmentfrom
omonien:package/aead-architecture
Sep 12, 2026
Merged

AEAD architecture: FAuthObj + multi-call GCM (from PR #90 concern A)#102
MHumm merged 3 commits into
MHumm:developmentfrom
omonien:package/aead-architecture

Conversation

@omonien

@omonien omonien commented Jul 26, 2026

Copy link
Copy Markdown

For Markus — AEAD architecture (no ChaCha)

This is package A only: a careful architecture / GCM fix extracted from donor PR #90.
ChaCha / XChaCha / Poly1305 / AES-NI are not in this PR. Those stay separate packages on the fork for later.

Why bother (simple words)

Today, authenticated modes (especially GCM) are a bit of a special case:

  • GCM and CCM each have their own fields and switch statements.
  • GCM did not correctly support encrypting/decrypting a message in several chunks (streaming). Tags could look fine for one-shot use and still be wrong for multi-call use.
  • That is a real correctness problem for anyone who streams AES-GCM (network buffers, files, etc.).

This PR does three practical things:

  1. One place for “authenticated mode” work — a single FAuthObj instead of dual FGCM / FCCM wiring.
  2. Correct multi-call GCM — partial blocks and keystream are carried across Encode/Decode calls; the tag is finalized in Done.
  3. A clear lifecycle everyone can follow:
    Init → (AAD / tag length) → Encode/Decode* → Done → read / check tag.

Benefits for existing code (already, without ChaCha)

You get value even if we never merge ChaCha:

Benefit Why it matters now
Correct AES-GCM streaming Multi-chunk encrypt/decrypt produces the same ciphertext and tag as one-shot
Safer “after Done” behaviour Further Encode/Decode after finalize raises until the next Init
Simpler mode wiring Less case FMode of duplication for auth properties / Done
Solid foundation for future AEAD New modes can plug in as another FAuthObj implementation without rewriting GCM again
Public API kept IDECAuthenticatedCipher names stay; protected EncodeGCM / DecodeGCM kept as wrappers

Call site note: for multi-call GCM, call Done before reading CalculatedAuthenticationResult. Paths that already call Done (most of the suite / normal demo style) keep working. Single-shot “encode then Done then tag” remains the normal pattern.

CCM stays one-shot as before (no fake multi-chunk for CCM).

Relation to the donor PR (#90)

PR #90 mixed architecture, GCM rewrite, ChaCha, Poly1305, SIMD, AES-NI, … in one large change.

For this package we:

  • Cut out only the AEAD architecture + GCM multi-call concern
  • Reworked thoroughly — did not take the donor’s GCM streaming model as-is (it treated a partial call as end-of-message and broke unaligned multi-chunk streams)
  • Kept the good idea (shared auth object + deferred finalize) and replaced the weak streaming engine with a correct multi-call design (same spirit as the GCM multi-chunk work discussed in the cleanup / Option A direction)
  • Left ChaCha / Poly1305 / CPU / AES-NI for later packages so this review stays focused

So: donor as inspiration and reference, not a bulk merge.

What changed (files)

File Role
Source/DECAuthenticatedCipherModesBase.pas Shared base + virtual Done
Source/DECCipherModes.pas FAuthObj wiring, leak-safe InitMode, unified finalize + ExpectedTag check
Source/DECCipherModesGCM.pas Multi-call GCM (GHASH partial + CTR keystream remainder + FFinalized)
Unit Tests/Tests/TestDECCipherModesGCM.pas Multi-chunk + Done lifecycle tests
Unit Tests/Data/gcmEncryptExtIV256_large.rsp Corrected large-vector tag (CT already matched AES-GCM)
Docs/plans/2026-07-25-aead-architecture.md Design decisions (fork planning note)

Tests (emphasis)

Thorough coverage was a hard requirement for this package — architecture without a green GCM/CCM signal is not mergeable.

Verified locally (Delphi 13, Win32 Console DUnit):

  • GCM suite: 19/19 including:
    • existing CAVS / stream / auth-failure cases
    • multi-chunk two equal blocks (16+16)
    • uneven multi-chunk (7+25) for GHASH + keystream partials
    • multi-chunk decode with expected-tag verify
    • Done idempotent
    • Encode/Decode after Done rejected until re-Init
  • CCM suite: 16/16 (one-shot regression under the new wiring)
  • Non-AEAD cipher modes: green (no accidental coupling)
  • Full suite residual reds at the time of work: pre-existing Keccak vectors only (separate PRs) — no new reds from this package

Risk

Medium — touches core GCM authentication. Mitigations: multi-chunk NIST-style vectors, Done lifecycle tests, CCM full regression, no public interface rename.

Out of scope (intentionally)

Suggested review order

  1. Lifecycle: Init → multi Encode/DecodeDone → tag
  2. GCM state: GHASH partial, keystream remainder, FFinalized
  3. FAuthObj + property / Done wiring in DECCipherModes.pas
  4. New tests (multi-chunk + post-Done)
  5. CCM still green

Test plan

  • Build library Debug Win32
  • Run full unit test suite (or at least GCM + CCM)
  • Spot-check: multi-chunk GCM + Done after last chunk before reading tag

Happy to adjust wording or split further if anything feels too large.

…m#99)

Extract Concern A from the ChaCha PR MHumm#90 donor without ChaCha/Poly1305.

- Polymorphic FAuthObj replaces dual FGCM/FCCM fields; public IDECAuthenticatedCipher unchanged
- Keep EncodeGCM/DecodeGCM/EncodeCCM/DecodeCCM as protected wrappers (no rename break)
- GCM multi-call: absorb PR MHumm#99 streaming (GHASH partial + CTR keystream remainder + Done/FFinalized)
- Reject PR MHumm#90 fIsLastBlock CTR model (incorrect for unaligned multi-chunk)
- Base Done virtual (no-op for CCM one-shot); TDECCipherModes.Done finalizes then verifies ExpectedTag
- InitMode always FreeAndNil(FAuthObj) before recreate (leak fix)
- Multi-chunk and Done-lifecycle unit tests; corrected large GCM rsp tag

See Docs/plans/2026-07-25-aead-architecture.md.
@omonien
omonien force-pushed the package/aead-architecture branch from 47c7463 to 58b54d1 Compare July 27, 2026 09:17
Reject DataToAuthenticate after GHASH has absorbed AAD or after Done; cap
AuthenticationTagBitLength at 128 bits with a defensive tag copy; keep
EncodeCCM/DecodeCCM as independent protected entry points that call FAuthObj
directly so EncodeGCM overrides no longer affect CCM.
@MHumm

MHumm commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Habe angefangen mir diesen anzuschauen. Muss mal prüfen ob Zugriff auf CalculatedAuthenticationTag vor Aufruf von Done auch eine Exception wirft. Falls nicht würde ich das gerne einbauen, dann werden die Nutzer gleich erzogen ;-)

@MHumm

MHumm commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Nix für diesen PR, aber: soweit meine kurze Recherche nach CCM und "multi-chunk" ergab, sollte auch CCM das können. Das würde bedeuten, dass nur unsere Umsetzung das nicht kann. Falls dem so ist sollte man sich das bei Gelegenheit auch mal anschauen, anderenfalls wäre ich für irgendwelche Metadaten mit denen ein Nutzer rausfinden kann, ob der Algorithmus multi-chunk fähig ist oder nicht. Nur mal so eine Idee... Ich glaube aber immer noch dass wir das mal irgendwann irgendwie auf multi-chunk Fähig umbauen können.

@MHumm

MHumm commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Welche Konsequenz hätte eine Umbenennung von EncodeGCM in DECCipherModes.pas?
Ja, die ist protected, aber wer außer uns würde die wirklich so aufrufen? Wäre da nicht EncodeAuthenthicated oder so besser? Gleiches natürlich für EncodeCCM. Man könnte das denke ich ruhig vereinen in eine

@MHumm

MHumm commented Sep 12, 2026

Copy link
Copy Markdown
Owner

TDECCipherModes.InitMode prüft auf Blockgröße 128 bit, das könnte aber evtl. für CCM falsch sein, da lt. etwas das ich gelesen habe (vermutlich Wikipedia Artikel) das so wohl nur im RFC definiert wurde, man müsste mal schauen wo die originale CCM Spezifikaiton ist und ob die das überhaupt so her gibt. Kann ich evtl. später mal tun, nach dem Merge. Jetzt aber erst Mal Pause für mich ;-)

@MHumm

MHumm commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Ich wäre dafür, wenn CCM auch den Done Aufruf bräuchte statt im Encode/Decode das schon zu tun. Damit wäre der Livecycle für alle gleich.

@MHumm

MHumm commented Sep 12, 2026

Copy link
Copy Markdown
Owner

ECAuthenticatedCipherModesBase.pap: GetStandardAuthenticationTagBitLengths:
kann es solche Modi geben, die keine Vorschriften für die TagBit Längen machen?
Falls ja, was soll dann zurückgeliefert werden? Leeres Array? (fände ich zumindest logisch) Wenn ja, sollte das als Bemerkung in den XMLDOC Kommentar aufgenommen werden.

@MHumm
MHumm merged commit cb12fff into MHumm:development Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants