- job_schema: geschlossene Job-Type-Allowlist (C5_SAVE_OBJECT/C5_DELETE_OBJECT), Pfad-/Längen-Validierung - job_state_machine: deterministische States (CREATED/READY/CLAIMED/EXECUTING/SUCCEEDED/FAILED) - job_claim: atomare Claim-/Lease-/Recovery-Logik (kein TOCTOU) - job_store: getrennte SQLite-Inbox-DBs (c5a_save.db/c5a_delete.db), delegiert an job_claim - save_executor_core: SAVE-only, Content-Rekonstruktion, Provenance-Validierung - delete_executor_core: DELETE-only, AUTH.3D-Composition, TOCTOU-Defense (Re-Read nach Claim) - test_job_channel: T1-T40 + adversarial (72 Tests) - test_job_channel_adversarial: adversarial + Substitution + DB-Manipulation - sensitivity_proof_auth3e: Mutationen A-L (12/12 Invarianten PRESENT) - AUTH3B/3C/3D/3E_DESIGN: autoritative Security-Dokumentation (e25 Reconciliation) COMMAND != AUTHORIZATION. Kein generischer Dispatcher. RQ credential-free. Keine produktive Mutation. Keine echten Credentials.
422 lines
20 KiB
Markdown
422 lines
20 KiB
Markdown
# AUTH.3D — HUMAN DELETE APPROVAL AUTHENTICITY
|
||
|
||
**Status:** SECURITY DESIGN + CODE + ISOLATED TESTS + REVIEW ONLY (KEIN produktives Deployment)
|
||
**Datum:** 2026-08-27
|
||
**Mission Type:** SECURITY DESIGN + CODE + ISOLATED TESTS + REVIEW ONLY
|
||
**Baseline:** AUTH.1 (40bbc40) + AUTH.2 (edcb4902) + AUTH.3A (a11c1bb) + AUTH.3B + AUTH.3C = COMPLETE
|
||
|
||
> ⚠️ Dies ist ein **Design + Code + Test-Dokument**. Es wird KEIN produktives
|
||
> Deployment ausgeführt, kein echter Signing Key erzeugt, kein Christians Private
|
||
> Key erzeugt, kein echter Public Key deployed, keine ENV geändert, kein Container
|
||
> erstellt, kein Executor deployed, keine produktive SAVE/DELETE-Probe. Bei Zweifel:
|
||
> FAIL CLOSED + HARD STOP.
|
||
|
||
---
|
||
|
||
## 1. CURRENT APPROVAL FLOW (Phase 1) — rekonstruiert (READ-ONLY)
|
||
|
||
### 1a. Vollständiger Flow
|
||
```
|
||
Delete Request → State Machine → Approval Creation → Approval Persistence
|
||
→ DeleteExecutor → Approval Validation → Tolaria DELETE → Ledger/Consumption
|
||
```
|
||
|
||
1. **Delete Request:** Ein Forgejo-Commit enthält einen DELETE-ObjectChange (`operation == OP_DELETE_REQUEST`).
|
||
2. **State Machine:** Commit wird in C5-State-Machine verarbeitet (C5AStore, c5a.db).
|
||
3. **Approval Creation:** `cmd_c5_delete_approve` (CLI, rq_c5_cli.py Z.355) → `store.create_delete_approval()`.
|
||
4. **Approval Persistence:** SQLite-Tabelle `delete_approvals` (c5a.db).
|
||
5. **DeleteExecutor:** `cmd_c5_delete_execute` (CLI, Z.424) → `DeleteExecutor.execute()`.
|
||
6. **Approval Validation:** `_validate_approval()` (rq_c5_delete.py Z.105) — Invarianten D/E/F/I.
|
||
7. **Tolaria DELETE:** `_do_delete()` → `client.delete()`.
|
||
8. **Ledger/Consumption:** `mark_delete_approval_used()` → `approval_status='USED'`, Commit → ST_UPDATING_SEARCH.
|
||
|
||
### 1b. Wer kann Approval heute erzeugen?
|
||
- **NUR der CLI-Befehl `approve-delete`** (`cmd_c5_delete_approve`). Kein Code erzeugt Approval automatisch (Invariante B/C).
|
||
- **Jeder, der die CLI im RQ-Container ausführen kann** (User `hermes`), kann eine Approval erzeugen.
|
||
- **`approved_by` ist ein frei wählbares CLI-Argument** (Default `human:christian`). **KEINE kryptographische Bindung an Christian.**
|
||
|
||
### 1c. Exakte Felder (DB-Tabelle `delete_approvals`)
|
||
| Feld | Typ | Bedeutung |
|
||
|---|---|---|
|
||
| `approval_id` | TEXT (uuid4) | Eindeutige ID |
|
||
| `workflow_commit` | TEXT | Commit-Binding |
|
||
| `object_change_id` | INT | ObjectChange-Binding |
|
||
| `object_id` | TEXT | Objekt-Binding |
|
||
| `path` | TEXT | Pfad-Binding |
|
||
| `operation` | TEXT | 'DELETE' |
|
||
| `approval_status` | TEXT | APPROVED/USED/REVOKED |
|
||
| `approved_by` | TEXT | **frei wählbar** (Default human:christian) |
|
||
| `approval_nonce` | TEXT (uuid4) | Nonce (kryptographisch zufällig, aber NICHT signiert) |
|
||
| `approved_at` | INT (ms) | Zeitpunkt |
|
||
| `used_at` | INT (ms) | Single-Use-Zeitpunkt |
|
||
|
||
### 1d. Bestehende Bindings (im Code verifiziert)
|
||
- **Object-Binding:** `_validate_approval` Z.143 — `approval.object_id == oid` (Invariante E).
|
||
- **Commit-Binding:** Z.136 — `approval.workflow_commit == commit_sha` (Invariante F).
|
||
- **Path-Binding:** Z.150 — `approval.path == path` (Invariante I).
|
||
- **Operation-Binding:** `create_delete_approval` setzt `operation='DELETE'` fest.
|
||
- **Nonce:** `approval_nonce` = uuid4, aber **nicht signiert, nicht verifiziert**.
|
||
- **Single-Use:** `mark_delete_approval_used` → `approval_status='USED'`; `_validate_approval` prüft `approval_status == 'APPROVED'`.
|
||
- **Mission-Binding:** **FEHLT** — kein `mission_id`-Feld.
|
||
- **Provenance-Binding:** **FEHLT** — kein `expected_provenance_hash`-Feld.
|
||
- **Expiry:** **FEHLT** — kein `expires_at`-Feld.
|
||
- **Actor-/Identity-Information:** `approved_by` (frei wählbar).
|
||
- **Authentizitätsprüfung:** **KEINE** — es gibt keine kryptographische Prüfung, dass die Approval von Christian stammt.
|
||
|
||
### 1e. Kernbefund
|
||
**HUMAN_APPROVAL_AUTHENTICITY_GAP = OPEN.** Die Approval ist eine reine DB-Zeile mit
|
||
frei wählbarem `approved_by`. Jeder mit CLI-/DB-Zugriff (RQ/Hermes, User `hermes`)
|
||
kann eine syntaktisch perfekte Approval erzeugen. Es gibt **keine kryptographische
|
||
Bindung an Christian**. → DELETE_OPERATION_ACTIVATION bleibt BLOCKED.
|
||
|
||
---
|
||
|
||
## 2. THREAT MODEL (Phase 2)
|
||
|
||
> ⚠️ NUR ANALYSE. FAIL CLOSED.
|
||
|
||
| Threat | CURRENT_PROTECTION | CURRENT_GAP | TARGET_CONTROL | FAIL_CLOSED_BEHAVIOR |
|
||
|---|---|---|---|---|
|
||
| **T1 RQ erzeugt selbst Approval** | CLI-Zugriff (User hermes) | RQ kann `approve-delete` ausführen | RQ besitzt keinen Signing Key → kann keine gültige signierte Approval erzeugen | DELETE_DENIED |
|
||
| **T2 Hermes erzeugt selbst Approval** | Gleiche Boundary wie RQ | Hermes kann CLI ausführen | Hermes besitzt keinen Signing Key | DELETE_DENIED |
|
||
| **T3 Rain erzeugt Approval** | Separater Container | Rain hat keinen CLI-Zugriff auf c5a.db | Rain besitzt keinen Signing Key | DELETE_DENIED |
|
||
| **T4 SAVE Executor erzeugt Approval** | Separater Container (AUTH.3C) | Kein CLI-Zugriff auf c5a.db | SAVE Executor besitzt keinen Signing Key | DELETE_DENIED |
|
||
| **T5 DELETE Executor erzeugt Approval** | Separater Container (AUTH.3C) | Kein CLI-Zugriff auf c5a.db | DELETE Executor ist **verify-only**, besitzt keinen Signing Key | DELETE_DENIED |
|
||
| **T6 Agent verändert gespeicherte Approval** | DB-Zugriff | DB-Zeile kann geändert werden | Signatur über Payload → Änderung invalidiert Signatur | DELETE_DENIED |
|
||
| **T7 Agent verändert Object nach Approval** | Object-Binding (E) | Nur DB-Vergleich, nicht kryptographisch | Object im signierten Payload → Änderung invalidiert Signatur | DELETE_DENIED |
|
||
| **T8 Agent verändert Commit nach Approval** | Commit-Binding (F) | Nur DB-Vergleich | Commit im signierten Payload | DELETE_DENIED |
|
||
| **T9 Agent verändert Path nach Approval** | Path-Binding (I) | Nur DB-Vergleich | Path im signierten Payload | DELETE_DENIED |
|
||
| **T10 Approval wird kopiert** | approval_id uuid4 | Kopie ist gültig (keine Signatur) | Signatur + Single-Use → Kopie hat gleiche Signatur, aber Nonce/consumed | DELETE_DENIED (Replay) |
|
||
| **T11 Approval wird wiederverwendet** | Single-Use (USED) | Nur DB-Status | Signatur + consumed-check | DELETE_DENIED |
|
||
| **T12 Approval wird zwischen Missions übertragen** | Kein mission_id | Kein Mission-Binding | mission_id im signierten Payload | DELETE_DENIED |
|
||
| **T13 alte Approval nach State Change** | Commit-Status-Check (Gate 2) | Nur DB-Status | State-Check + Signatur | DELETE_DENIED |
|
||
| **T14 Nonce wird wiederverwendet** | uuid4 | Nonce nicht signiert/verifiziert | Nonce im signierten Payload + consumed-check | DELETE_DENIED |
|
||
| **T15 DB-Eintrag wird manipuliert** | — | DB-Zeile allein erzeugt gültige Approval | Signatur → DB-Forgery ohne Private Key unmöglich | DELETE_DENIED |
|
||
| **T16 Approval aus Backup wiederhergestellt** | — | Backup enthält gültige Approval | Signatur + consumed-check + expires_at | DELETE_DENIED |
|
||
| **T17 Approval nach Executor-Restart erneut verwendet** | Single-Use (USED) | Nur DB-Status | consumed-check + Signatur | DELETE_DENIED |
|
||
| **T18 Race zwischen Verify und Delete** | — | TOCTOU-Fenster | Atomare Reservation (RESERVED) | DELETE_DENIED |
|
||
| **T19 LLM erzeugt syntaktisch perfekte Fake-Approval** | — | Keine Signatur | Signatur → LLM ohne Private Key kann nicht signieren | DELETE_DENIED |
|
||
| **T20 kompromittierter RQ-Prozess versucht Approval-Bypass** | — | RQ kann CLI ausführen | RQ besitzt keinen Signing Key | DELETE_DENIED |
|
||
|
||
**Kern-Gap:** Alle Bindings (Object/Commit/Path) sind nur DB-Vergleiche, nicht
|
||
kryptographisch. `approved_by` ist frei wählbar. **Es fehlt eine kryptographische
|
||
Bindung an Christian.**
|
||
|
||
---
|
||
|
||
## 3. HUMAN IDENTITY ROOT (Phase 3) — Optionen
|
||
|
||
> ⚠️ NUR BEWERTUNG. KEINE Keys erzeugen.
|
||
|
||
| Option | HUMAN_AUTH | LLM_CAN_FORGE | RQ_CAN_FORGE | DELETE_EXEC_CAN_FORGE | PRIVATE_SECRET_EXPOSURE | REPLAY_RES | AUDIT | ROTATION | REVOCATION | BACKUP_RISK | OP_COMPLEXITY | STACK_COMPAT | OFFLINE_RECOVERY |
|
||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||
| **A: HMAC (geteiltes Secret)** | Mittel | Nein (ohne Secret) | Nein (ohne Secret) | Nein (verify-only) | **Hoch** (Secret muss bei Verifier liegen) | Mittel | Mittel | Ja | Ja | Mittel | Niedrig | Hoch | Ja |
|
||
| **B: Asymmetrisch (Ed25519)** | **Hoch** | **Nein** | **Nein** | **Nein** (verify-only) | **Niedrig** (nur Public Key bei Verifier) | **Hoch** | **Hoch** | Ja | Ja | Niedrig | Mittel | **Hoch** (Python stdlib/cryptography) | Ja |
|
||
| **C: Forgejo-signiertes Artefakt** | Mittel | Nein | Nein | Nein | Mittel (Key bei Forgejo) | Mittel | Hoch | Ja | Ja | Mittel | Hoch | Hoch | Mittel |
|
||
| **D: Separater Approval-Service** | Hoch | Nein | Nein | Nein | Niedrig | Hoch | Hoch | Ja | Ja | Niedrig | **Hoch** (neue Infrastruktur) | **Niedrig** | Mittel |
|
||
| **E: Telegram + kryptogr. Bindung** | Mittel | Nein | Nein | Nein | Mittel | Mittel | Mittel | Ja | Ja | Mittel | Mittel | Hoch | Ja |
|
||
| **F: Andere minimal sichere** | — | — | — | — | — | — | — | — | — | — | — | — | — |
|
||
|
||
**Bewertung:**
|
||
- **OPTION B (Ed25519 asymmetrisch)** ist die **stärkste und stack-kompatible** Lösung:
|
||
Christian besitzt den Private Key, der DELETE Executor kennt nur den Public Key.
|
||
**LLM/RQ/SAVE/DELETE-Executor können ohne Private Key nicht signieren.**
|
||
Python `cryptography`-Bibliothek (oder `pynacl`) unterstützt Ed25519 nativ.
|
||
- **OPTION A (HMAC)** ist schwächer: das geteilte Secret müsste beim Verifier liegen,
|
||
was die Exposure-Fläche vergrößert. Nicht bevorzugt.
|
||
- **OPTION C (Forgejo-signiert)** ist möglich, aber komplexer und bindet an Forgejo.
|
||
- **OPTION D (separater Service)** erfordert neue Infrastruktur → Scope-Creep.
|
||
- **OPTION E (Telegram)** allein ist KEIN kryptographischer Proof (siehe §7).
|
||
|
||
**Empfehlung: OPTION B (Ed25519 asymmetrisch).**
|
||
|
||
---
|
||
|
||
## 4. PREFERRED SECURITY PROPERTY (Phase 4)
|
||
|
||
**Bevorzugte Eigenschaft (bestätigt):**
|
||
- **Christian:** PRIVATE KEY (Ed25519)
|
||
- **DELETE Executor:** PUBLIC KEY ONLY (verify-only)
|
||
- **RQ/Hermes:** NO PRIVATE KEY
|
||
- **SAVE Executor:** NO PRIVATE KEY
|
||
- **Rain:** NO PRIVATE KEY
|
||
- **Tolaria:** NO PRIVATE KEY
|
||
|
||
**Gegen reale Architektur bewertet:**
|
||
- Python `cryptography`/`pynacl` ist im Stack verfügbar (C5-Caller sind Python).
|
||
- Keine neue Infrastruktur nötig.
|
||
- Der DELETE Executor kann eine Approval **verifizieren**, aber **nicht erzeugen**.
|
||
- **Bestätigt als bevorzugte Lösung.**
|
||
|
||
---
|
||
|
||
## 5. APPROVAL PAYLOAD (Phase 5) — kanonisch
|
||
|
||
### Kanonischer Payload
|
||
```json
|
||
{
|
||
"approval_version": 1,
|
||
"approval_id": "<uuid4>",
|
||
"mission_id": "<mission-id>",
|
||
"delete_request_id": "<delete-request-id>",
|
||
"operation": "DELETE",
|
||
"object_id": "<object-id>",
|
||
"vault_path": "<vault-path>",
|
||
"expected_commit": "<commit-sha>",
|
||
"expected_provenance_hash": "<sha256>",
|
||
"nonce": "<uuid4>",
|
||
"issued_at": "<ISO-8601-UTC>",
|
||
"expires_at": "<ISO-8601-UTC>"
|
||
}
|
||
```
|
||
|
||
### Canonicalization
|
||
- **CANONICALIZATION:** Deterministische Serialisierung: Felder in fester Reihenfolge
|
||
(wie oben), keine Duplikate, keine Whitespace-Varianz, UTF-8, keine Unicode-Normalisierung
|
||
(exakte Bytes), Pfad-Normalisierung (kein `./`, kein `..`, keine doppelten Slashes).
|
||
- **ENCODING:** UTF-8, JSON ohne optionale Felder (nur die 12 Pflichtfelder).
|
||
- **HASH:** SHA-256 über den kanonisierten Payload-Bytes.
|
||
- **SIGNATURE_INPUT:** Die kanonisierten Payload-Bytes selbst (nicht der Hash) werden
|
||
signiert (Ed25519 signiert intern den Hash). Keine Signatur über frei formatierte Texte.
|
||
|
||
---
|
||
|
||
## 6. APPROVAL SIGNATURE (Phase 6) — Ed25519
|
||
|
||
- **SIGNATURE_ALGORITHM:** **Ed25519** (RFC 8032), via Python `cryptography` oder `pynacl`.
|
||
- **PRIVATE_KEY_OWNER:** Christian (einziger Besitzer).
|
||
- **PRIVATE_KEY_LOCATION:** Christians vertrauenswürdiges Gerät (NICHT im Stack).
|
||
- **PUBLIC_KEY_LOCATION:** DELETE Executor-Konfiguration (read-only, injiziert via Secret/File).
|
||
- **KEY_ID:** SHA-256-Hash des Public Keys (kurz, zur Key-Identifikation).
|
||
- **SIGNATURE_FORMAT:** Ed25519-Signatur (64 Bytes), Base64-URL-encoded.
|
||
- **VERIFICATION:** `verify(public_key, canonical_payload_bytes, signature)`.
|
||
- **ROTATION:** Neuer Key → neue KEY_ID; alte Approvals mit altem Key bleiben bis Expiry gültig (Multi-Key-Transition).
|
||
- **REVOCATION:** Key-Revocation-Liste (KEY_ID → revoked); verifier prüft.
|
||
- **MULTI_KEY_TRANSITION:** Verifier akzeptiert mehrere Public Keys (aktuelle + vorherige), markiert ältere als "transitioning".
|
||
|
||
**Private Key darf NICHT liegen in:** RQ ENV, Hermes ENV, Rain ENV, SAVE Executor,
|
||
DELETE Executor, Forgejo Repo, C5 DB, Tolaria, Logs, Reports.
|
||
|
||
---
|
||
|
||
## 7. APPROVAL CREATION CHANNEL (Phase 7)
|
||
|
||
> ⚠️ NUR DESIGN. KEINE Implementierung in AUTH.3D (nur Code + Tests).
|
||
|
||
### Modelle
|
||
| Modell | UX | Sicherheit | Stack-Kompatibilität |
|
||
|---|---|---|---|
|
||
| **A: Lokales Signing Tool auf Christians Gerät** | Mittel (Christian signiert manuell) | **Hoch** (Private Key nie im Stack) | Hoch (Python-Skript) |
|
||
| **B: Separater Approval-Container/Service** | Niedrig | Hoch | **Niedrig** (neue Infrastruktur) |
|
||
| **C: Telegram Approval → separater Signer** | Hoch | Mittel (Signer muss getrennt sein) | Mittel |
|
||
| **D: Forgejo Workflow** | Mittel | Mittel | Hoch |
|
||
| **E: Anderer sicherer Kanal** | — | — | — |
|
||
|
||
**Empfehlung: OPTION A (lokales Signing Tool auf Christians Gerät).** Christian
|
||
erzeugt den kanonischen Payload (aus dem Delete-Request), signiert ihn mit seinem
|
||
Private Key, und übergibt die signierte Approval (Payload + Signatur + KEY_ID) an
|
||
den DELETE Executor (via CLI/Inbox). Der Executor verifiziert mit dem Public Key.
|
||
|
||
**WICHTIG (strikte Unterscheidung):**
|
||
- **HUMAN_INTENT:** Ein Telegram-Text "Ja"/"Approve"/"Delete" ist NUR Intent, KEIN kryptographischer Proof.
|
||
- **CRYPTOGRAPHIC_APPROVAL_ARTIFACT:** Die signierte Approval (Payload + Signatur) ist der einzige gültige Proof.
|
||
- RQ darf NICHT aus einem Telegram-Text den finalen Approval-Datensatz erzeugen können (ohne Christians Private Key).
|
||
|
||
---
|
||
|
||
## 8. APPROVAL VERIFICATION (Phase 8)
|
||
|
||
**DeleteExecutor MUSS VOR DELETE prüfen (alle, sonst DELETE_DENIED):**
|
||
1. Schema gültig (kanonischer Payload)
|
||
2. Unterstützte Approval-Version
|
||
3. Signatur gültig (Ed25519, Public Key)
|
||
4. Key nicht revoked
|
||
5. Operation == DELETE
|
||
6. Mission stimmt
|
||
7. Delete Request stimmt
|
||
8. Object stimmt
|
||
9. Path stimmt
|
||
10. Commit stimmt
|
||
11. Provenance stimmt
|
||
12. Nonce stimmt
|
||
13. issued_at plausibel (nicht in der Zukunft)
|
||
14. expires_at nicht überschritten
|
||
15. Approval noch nicht consumed
|
||
16. State erlaubt Delete
|
||
17. kein Replay
|
||
18. kein Cross-Object-Reuse
|
||
19. kein Cross-Mission-Reuse
|
||
|
||
**Jeder Fehler → DELETE_DENIED. KEIN Tolaria HTTP DELETE.**
|
||
|
||
---
|
||
|
||
## 9. TOCTOU / RACE SAFETY (Phase 9)
|
||
|
||
- **VERIFY → STATE CHANGE → DELETE:** Zwischen Verification und Mutation gibt es ein Fenster.
|
||
- **Lösung:** Atomare **Reservation** in der DB: Approval-Status `APPROVED → RESERVED`
|
||
(mit `execution_attempt_id` + `idempotency_key`) in einer SQLite-Transaktion, BEVOR
|
||
der Tolaria-DELETE ausgeführt wird. Nur eine Reservation pro Approval möglich.
|
||
- **DB Transaction:** `UPDATE ... SET approval_status='RESERVED' WHERE approval_status='APPROVED'`
|
||
— atomar, nur wenn noch APPROVED.
|
||
- **Crash before delete:** Approval bleibt RESERVED → Recovery: prüfen, ob DELETE
|
||
tatsächlich ausgeführt wurde (Read-Back). Wenn nicht → zurück zu APPROVED (oder
|
||
neuer Attempt mit neuem idempotency_key).
|
||
- **Crash after delete before ledger:** Read-Back zeigt Ziel absent → Ledger nachtragen,
|
||
Approval → CONSUMED.
|
||
- **Retry:** Idempotenz via `idempotency_key` — gleicher Attempt wird nicht doppelt ausgeführt.
|
||
- **Uncertain network result:** **DELETE_OUTCOME_UNKNOWN** — der Executor darf bei
|
||
Unsicherheit NICHT blind wiederholen. Er meldet OUTCOME_UNKNOWN und wartet auf
|
||
manuelle Klärung (Read-Back).
|
||
|
||
**Recovery State für DELETE_OUTCOME_UNKNOWN:** Approval bleibt RESERVED; ein manueller
|
||
Read-Back entscheidet, ob CONSUMED (Ziel absent) oder zurück zu APPROVED (Ziel noch da).
|
||
|
||
---
|
||
|
||
## 10. SINGLE USE (Phase 10)
|
||
|
||
**Approval Lifecycle:**
|
||
```
|
||
CREATED → VERIFIED → RESERVED → EXECUTED → CONSUMED
|
||
```
|
||
|
||
| Zustand | Verhalten |
|
||
|---|---|
|
||
| **INVALID** | DELETE_DENIED (Signatur/Binding-Fehler) |
|
||
| **EXPIRED** | DELETE_DENIED (expires_at überschritten) |
|
||
| **REVOKED** | DELETE_DENIED (Key oder Approval revoked) |
|
||
| **FAILED** | DELETE_DENIED (Execution-Fehler) |
|
||
| **OUTCOME_UNKNOWN** | Kein blinder Retry; manuelle Klärung |
|
||
|
||
**Eine Approval darf nach CONSUMED niemals erneut ausführbar sein.** Der consumed-check
|
||
ist Teil der Verification (§8, Punkt 15) und der Reservation (§9).
|
||
|
||
---
|
||
|
||
## 11. STORAGE (Phase 11)
|
||
|
||
**Was wird gespeichert:**
|
||
- Payload (kanonisch)
|
||
- Signature (Ed25519, Base64-URL)
|
||
- Public Key ID (KEY_ID)
|
||
- Verification Result (PASS/DENIED + Reason)
|
||
- consumed_at
|
||
- execution_attempt (attempt_id, idempotency_key)
|
||
- Result (DELETE_OK / DELETE_OUTCOME_UNKNOWN / DELETE_DENIED)
|
||
|
||
**Was wird NICHT gespeichert:**
|
||
- Private Key
|
||
- Signing Secret
|
||
|
||
**DB-Manipulationsrisiko:** Eine DB-Zeile allein darf keine gültige Approval erzeugen.
|
||
Selbst vollständige Schreibkontrolle über die Approval-DB darf ohne Christians
|
||
Private Key keine neue gültige Approval erzeugen können (Signatur fehlt → DENIED).
|
||
|
||
---
|
||
|
||
## 12. ISOLATED IMPLEMENTATION (Phase 12)
|
||
|
||
> ⚠️ NUR Code + Tests. KEIN produktives Deployment.
|
||
|
||
**Modular (keine Vermischung mit Tolaria HTTP):**
|
||
- `approval_payload.py` — kanonischer Payload, Canonicalization, Encoding, Hash
|
||
- `approval_signature.py` — Ed25519 sign/verify (synthetische Test-Keypairs)
|
||
- `approval_verifier.py` — Verification-Logik (alle §8-Checks)
|
||
- `approval_state.py` — Lifecycle (CREATED→VERIFIED→RESERVED→EXECUTED→CONSUMED), Single-Use, Reservation
|
||
|
||
**Bestehende C5-Invarianten erhalten.** Keine Änderung an Tolaria-HTTP-Code.
|
||
|
||
---
|
||
|
||
## 13. TEST CONTRACT (Phase 13)
|
||
|
||
> ⚠️ NUR synthetische Test-Keypairs. KEINE echten Keys.
|
||
|
||
**T1–T30 (siehe Freigabe):**
|
||
- T1 gültige Christian-Approval → PASS
|
||
- T2 falsche Signatur → DENIED
|
||
- T3 falscher Public Key → DENIED
|
||
- T4 Payload nach Signatur verändert → DENIED
|
||
- T5 Object verändert → DENIED
|
||
- T6 Path verändert → DENIED
|
||
- T7 Commit verändert → DENIED
|
||
- T8 Provenance verändert → DENIED
|
||
- T9 Mission verändert → DENIED
|
||
- T10 Request-ID verändert → DENIED
|
||
- T11 Nonce verändert → DENIED
|
||
- T12 expired → DENIED
|
||
- T13 future-issued invalid → DENIED
|
||
- T14 falsche Operation → DENIED
|
||
- T15 unbekannte Version → DENIED
|
||
- T16 malformed payload → DENIED
|
||
- T17 malformed signature → DENIED
|
||
- T18 Replay → DENIED
|
||
- T19 consumed Approval → DENIED
|
||
- T20 cross-object reuse → DENIED
|
||
- T21 cross-mission reuse → DENIED
|
||
- T22 DB fake row ohne Signature → DENIED
|
||
- T23 DB fake row mit erfundener Signature → DENIED
|
||
- T24 DELETE Executor kann keine Approval erzeugen
|
||
- T25 RQ besitzt keinen Signing Key
|
||
- T26 SAVE Executor besitzt keinen Signing Key
|
||
- T27 Logs enthalten keinen Private Key
|
||
- T28 DB enthält keinen Private Key
|
||
- T29 Retry nach confirmed delete führt nicht zu zweitem Delete
|
||
- T30 OUTCOME_UNKNOWN führt nicht zu blindem Retry
|
||
|
||
**Adversarial:** canonicalization ambiguity, duplicate JSON keys, Unicode normalization,
|
||
path normalization, signature substitution, key-id substitution, oversized payload,
|
||
malformed timestamps, nonce collision, stale state, concurrent execution.
|
||
|
||
---
|
||
|
||
## 14. TEST SENSITIVITY (Phase 14)
|
||
|
||
**Mutationen (jede muss Tests ROT machen):**
|
||
- A Signaturprüfung entfernen
|
||
- B Object Binding entfernen
|
||
- C Commit Binding entfernen
|
||
- D Nonce Binding entfernen
|
||
- E Expiry entfernen
|
||
- F consumed-check entfernen
|
||
- G Operation-check entfernen
|
||
- H Mission Binding entfernen
|
||
- I Provenance Binding entfernen
|
||
- J Replay-Schutz entfernen
|
||
|
||
---
|
||
|
||
## 15. REGRESSION (Phase 15)
|
||
|
||
Volle relevante C5-Test-Suite. Keine bestehenden Sicherheitsinvarianten abschwächen.
|
||
Baseline-Fails separat beweisen. Keine unrelated Reparaturen.
|
||
|
||
---
|
||
|
||
## 16. SECURITY REVIEW (Phase 16)
|
||
|
||
- NO_PRIVATE_KEY_IN_REPO / ENV / DB / LOGS
|
||
- RQ_CANNOT_SIGN / HERMES_CANNOT_SIGN / RAIN_CANNOT_SIGN / SAVE_EXECUTOR_CANNOT_SIGN / DELETE_EXECUTOR_CANNOT_SIGN
|
||
- DELETE_EXECUTOR_VERIFY_ONLY
|
||
- SIGNATURE_FAIL_CLOSED / REPLAY_FAIL_CLOSED / EXPIRY_FAIL_CLOSED / STATE_FAIL_CLOSED
|
||
|
||
---
|
||
|
||
## 17. COMMIT / PUSH (Phase 17)
|
||
|
||
Nur AUTH.3D-relevante Dateien. Vor Commit: git diff, git status, Secret-Scan,
|
||
Private-Key-Pattern-Scan. Keine echten Keys. Tests nur mit synthetischen Test-Keypairs.
|
||
FF-only Push. HEAD == origin/main verifizieren.
|
||
|
||
---
|
||
|
||
## 18. FRESH CHECKER (Phase 18)
|
||
|
||
> ⚠️ Wird als unabhängiger Subagent ausgeführt.
|
||
|
||
---
|
||
|
||
## 19. FINAL REPORT (Phase 19)
|
||
|
||
> ⚠️ Wird nach Fresh Checker ausgefüllt.
|