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

173 lines
8.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).