Files
nexarch/mail/docs/ARC-10-PRUEFPROTOKOLL.md
T

8.5 KiB
Raw Blame History

ARC-10 — Signatur- und Zeitstempelerhalt (BSI TR-03125): Prüfprotokoll

Datum: 2026-09-03 Host: 192.168.1.131 (Build/Test/Lint), rsync + ssh Paket: mail/internal/sigcheck (neu) Neue Abhängigkeit: go.mozilla.org/pkcs7 v0.10.0 (go get + go mod tidy, go.mod-Direktive go 1.24 unverändert)

Umsetzung

Erkennung (Akzeptanzkriterium 1): sigcheck.Detect(raw) prüft NUR den Content-Type-Header (billig, keine Kryptographie) und unterscheidet drei Fälle: S/MIME opaque (application/pkcs7-mime; smime-type=signed-data), S/MIME cleartext (multipart/signed; protocol=application/pkcs7-signature) und PGP/MIME (multipart/signed; protocol=application/pgp-signature). Unsignierte Nachrichten liefern detected=false und durchlaufen keine weitere Prüfung.

Kryptographische Prüfung (Akzeptanzkriterium 2): sigcheck. Verify(raw, kind, now) — für S/MIME echte PKCS7-Signaturprüfung (go.mozilla.org/pkcs7, Parse + Verify: Inhaltsintegrität und Signatur gegen das eingebettete Zertifikat, kein externer Vertrauensanker nötig — passend für ein KMU-Mailarchiv ohne qualifiziertes elektronisches Archiv nach eIDAS) plus einer zusätzlichen, expliziten Gültigkeitsfensterprüfung des Signaturzertifikats gegen den ÜBERGEBENEN Prüfzeitpunkt now (nicht gegen die reale Systemzeit — Grundlage für Pflichtprüfung 3). Ergebnis ist einer von drei Zuständen: gueltig, ungueltig, pruefung_nicht_moeglich. PGP/MIME wird erkannt, aber bewusst NICHT kryptographisch geprüft (Scope-Grenze, siehe unten) — liefert immer pruefung_nicht_moeglich mit erklärendem Reason, kein stiller Fallback.

Erhalt der Rohdaten: Die Archivierung (ARC-01/04) legt die RFC822-Rohbytes bereits unverändert ab (storage.ArchiveKey, keine Re-Serialisierung). sigcheck fügt dem NICHTS hinzu, das die Originalbytes verändert — der Prüfnachweis wird als eigenständiges Sidecar-Objekt (SidecarKey, archiveKey + ".sigcheck.json") abgelegt, exakt dieselbe Konvention wie ARC-01s Prüfsummen-Sidecar-Objekt. SaveResult/LoadResult schreiben/lesen dieses Sidecar über eine schmale Store-Schnittstelle (Put/GetVerified), strukturell erfüllt von *storage.Service ohne Paketkopplung.

Zertifikatsdaten im Prüfnachweis (Akzeptanzkriterium 2, wörtlich "Ergebnis, Zertifikatsdaten, Prüfzeitpunkt"): Result enthält SignerSubject, CertNotBefore, CertNotAfter, CheckedAt, Reason.

Abweichung von der Ausgangslage: Ablage als Sidecar statt im Core-Audit-Log

Das Ticket sieht vor, den Prüfnachweis im zentralen, unveränderlichen Core-Audit-Log (AUD-01/AUD-02) abzulegen ("kein zweites Protokoll"). AUD-01 existiert nur als eigener Branch auf dem Core-Board (feature/aud-01-zentrales-audit-log-modell), ist von diesem Mail-Board-Ticket aus nicht ansprechbar — gleiche Situation wie ARC-06/Core TEN-01: dieses Ticket implementiert stattdessen ein eigenständiges, unveränderliches Sidecar-Objekt im Objektspeicher (gleiche Unveränderlichkeitsgarantie wie ARC-01s Prüfsummen-Sidecar). Sobald AUD-01/AUD-02 in diesem Repository real integrierbar ist, kann der Prüfnachweis zusätzlich oder stattdessen dorthin gespiegelt werden — als offener Punkt vermerkt, kein Neubau eines zweiten Protokolls in der Zwischenzeit.

Bewusste Scope-Grenzen (bei Ticketübernahme mit Nutzer abgestimmt)

  • PGP/MIME: nur Erkennung, keine kryptographische Prüfung — pruefung_nicht_moeglich ist einer der von Akzeptanzkriterium 2 ausdrücklich vorgesehenen drei Ergebniszustände, kein Implementierungsdefizit. Folgeticket bei Bedarf.
  • Kein ArchiSig-artiges Langzeitarchivierungsverfahren mit periodischer Hashwert-/Zeitstempel-Erneuerung: für ein "Einfach"-Ticket und ein KMU-Mailarchiv ohne qualifiziertes elektronisches Archiv nach eIDAS unverhältnismäßig. Ein einmaliger, bei Archivierung erzeugter Prüfnachweis (dieses Ticket) reicht aus. Eigenes Folgeticket, falls künftig benötigt.
  • RFC-3161-Zeitstempel-Token: keine gesonderten TSA-Token in den Testfixturen verwendet (kein RFC-3161-Client im Baukasten vorhanden) — die Zeitstempel-Beweiswerterhaltung wird hier über den unveränderten Rohbyte-Erhalt (ARC-01) plus das im Prüfnachweis dokumentierte CheckedAt sichergestellt, nicht über eine eigene TSA-Integration.

Pflichtprüfung 1: Testnachricht mit S/MIME-Signatur → Ergebnis "gültig" korrekt erkannt

TestVerify_ValidSMIMESignatureRecognizedAsValid: echte, real signierte S/MIME-Testnachricht (selbstsigniertes RSA-2048-Zertifikat per crypto/x509, go.mozilla.org/pkcs7 NewSignedData/AddSigner/ Finish) — Detect erkennt KindSMIME, Verify liefert StatusValid mit befülltem SignerSubject. Archivierung + Abruf des Prüfnachweises über echten storage.Service (LocalDriver) geprüft — Rundtrip liefert identisches Ergebnis.

Ergebnis: BESTANDEN.

Pflichtprüfung 2: signierte Nachricht mit manipuliertem Inhalt → Ergebnis "ungültig" korrekt erkannt

TestVerify_TamperedContentDetectedAsInvalid: reale PKCS7-DER-Bytes signiert, anschließend der signierte Klartext DIREKT in den finalen DER-Bytes durch einen gleich langen, anderen Text ersetzt (ASN.1- Längenpräfixe bleiben syntaktisch gültig, Inhalt weicht vom signierten Digest ab — realistische Nachbildung nachträglicher Manipulation). Verify liefert StatusInvalid mit erklärendem Reason.

Ergebnis: BESTANDEN.

Pflichtprüfung 3: simulierter Zertifikatsablauf nach Archivierung → archivierter Prüfnachweis bleibt unverändert

TestVerify_ArchivedResultUnaffectedBySimulatedLaterCertificateExpiry: Testzertifikat mit engem Gültigkeitsfenster um den Archivierungszeitpunkt. Erste Prüfung + Archivierung ZUM Archivierungszeitpunkt liefert StatusValid, wird als Sidecar gespeichert. Eine SIMULIERTE, NICHT persistierte spätere Live-Prüfung derselben Rohdaten mit einem now weit nach CertNotAfter liefert korrekt StatusInvalid (abgelaufenes Gültigkeitsfenster). Der BEREITS ARCHIVIERTE Prüfnachweis wird davon nicht berührt: LoadResult liefert weiterhin StatusValid mit unverändertem CheckedAt — die spätere Prüfung schreibt nie über einen bestehenden archivierten Nachweis.

Ergebnis: BESTANDEN.

Ergänzend geprüft (Akzeptanzkriterium 1, PGP/MIME-Erkennung ohne Verifikation)

TestDetect_PGPMIMEDetectedButNotCryptographicallyVerified: PGP/MIME- Nachricht wird korrekt als KindPGP erkannt; Verify liefert StatusUnverifiable mit erklärendem Reason statt eines stillen Fallbacks oder Fehlers.

TestDetect_UnsignedMessageNotDetected: unsignierte Nachricht wird korrekt NICHT erkannt, durchläuft keine Prüfung.

Akzeptanzkriterien

  1. Signierte Nachrichten (S/MIME, PGP/MIME) werden bei Archivierung erkannt: Detect, durch alle Detect-Tests belegt.
  2. Signaturprüfung liefert einen von drei Ergebniszuständen (gültig/ungültig/Prüfung nicht möglich) inkl. Zertifikatsdaten und Prüfzeitpunkt, als Nachweis archiviert: Verify + Result + SaveResult, durch Pflichtprüfung 13 belegt.
  3. Prüfnachweis über die Detailansicht abrufbar, Original-Mail unverändert: LoadResult liest ein eigenständiges Sidecar-Objekt, rührt die archivierten RFC822-Rohbytes nicht an; durch Pflichtprüfung 1 und 3 (Rundtrip) belegt.

Build/Vet/Lint/Test — Gesamtmodul

gofmt -l internal/sigcheck   → keine Ausgabe (sauber formatiert)
go build ./...                → OK
go vet ./...                  → OK
golangci-lint run ./internal/sigcheck/... → 0 issues
go test ./internal/sigcheck/... -v -count=1 → alle 6 Tests bestanden
go test ./... -p 1 -count=1 (TEST_TENANT_DSN, TEST_MANTICORE_URL, TEST_S3_ENDPOINT/TEST_S3_ACCESS_KEY/TEST_S3_SECRET_KEY gesetzt) → alle Pakete ok, inkl. neuem internal/sigcheck

Ein Fehlschlag von internal/pop3 (TestProtolog_SessionFullyReconstructableByCorrelationID, erwartete 6 Log-Einträge, real 5 in dem konkreten Lauf) trat während des Gesamtlaufs auf — bei isolierter Wiederholung (-count=3) dreimal in Folge bestanden. Bereits bestehender, von ARC-10 unabhängiger, zeitkritischer Flake im Protokoll-Logging-Test (internal/protolog/ internal/pop3, ING-08), keine Regression durch dieses Ticket.

Ergebnis

ARC-10 erfüllt alle Akzeptanzkriterien mit echten, ausgeführten Nachweisen — inklusive real erzeugter, real signierter S/MIME- Testnachrichten (selbstsigniertes Zertifikat, keine externe CA) und einer realen Manipulationserkennung auf PKCS7-DER-Byte-Ebene. Freigeschaltet: Beitrag zu QA-05 (zusammen mit ARC-07/09/INT-08, ARC-05 weiterhin extern blockiert durch RET-03).