Proof-of-concept proving the secure-storage exit criterion from docs/architecture/proof-of-concept-plan.md §2: "Secret write/read/delete works through platform secure storage." Implements a typed SecretStorageRepository trait per ADR-006 (SecureStore + per-platform adapters) and a Linux adapter (the only adapter in PoC scope) that supports both equivalent Linux backends per SysRS-053/SysRS-162: Secret Service (libsecret) and kernel keyutils. The audit test suite covers: SS-AUD-001 identity secret absent from local DB (raw file scan) SS-AUD-002 server password absent from local DB SS-AUD-003 secrets absent from logs (Secret newtype redaction) SS-AUD-005 failure returns safe typed error (NotFound) SS-AUD-006 delete removes entry SS-TC-003 Linux round-trip set/get/delete Verified on 2026-05-13 against the local keyutils backend (cargo test runs need 'keyctl session -' to provide a valid session keyring under non-interactive shells, documented in the spike README). The CLI driver additionally observed a real locked gnome-keyring collection and exercised the typed-error → fallback path live. Surfaced finding for the decision register: DEC-013 does not pin a Linux secure-storage backend policy. Both Secret Service and keyutils are 'equivalent' per the requirements; production code needs an owner ruling. Out of scope: Windows DPAPI, macOS/iOS Keychain, Android Keystore, SS-AUD-004 (covered by diagnostics-redaction spike), SS-AUD-007/008 (process / migration items). Authority: PoC plan §2, ADR-006, SDD-078, SRS-091..095, SysRS-158..162. Not product code; not promoted into chanora_storage.
106 lines
4.5 KiB
Markdown
106 lines
4.5 KiB
Markdown
# Verification record — `secure-storage-spike`
|
|
|
|
## Result
|
|
|
|
PASS. The PoC exit criterion (from
|
|
`docs/architecture/proof-of-concept-plan.md` §2: "Secret write/read/delete
|
|
works through platform secure storage") is met on Linux through the
|
|
kernel keyutils backend, with a documented Secret Service path verified
|
|
manually via the CLI driver.
|
|
|
|
## Environment
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Date | 2026-05-13 |
|
|
| Host OS | Linux (Arch, kernel 7.0.5-arch1-1, x86_64) |
|
|
| Rust toolchain | stable 1.95.0 |
|
|
| Backend (tests) | kernel keyutils (linux-native, `add_key(2)` / `request_key(2)`) |
|
|
| Backend (CLI) | tried Secret Service first; fell back to keyutils because the default Secret Service collection was locked (no graphical login) |
|
|
| `keyring` crate | 3.6.3 (`sync-secret-service`, `linux-native`, `crypto-rust`) |
|
|
| `linux-keyutils` | 0.2.5 |
|
|
| `rusqlite` | 0.32.1 (bundled) |
|
|
|
|
## Reproduction
|
|
|
|
```bash
|
|
keyctl session - cargo test --tests
|
|
keyctl session - cargo run --bin secure-storage-cli
|
|
```
|
|
|
|
## Test run
|
|
|
|
```
|
|
running 6 tests
|
|
test ss_aud_003_secret_values_not_in_logs ... ok
|
|
test ss_aud_006_delete_removes_entry ... ok
|
|
test ss_aud_001_identity_secret_absent_from_local_db ... ok
|
|
test ss_aud_002_server_password_absent_from_local_db ... ok
|
|
test ss_aud_005_safe_error_on_missing_entry ... ok
|
|
test ss_tc_003_round_trip_linux ... ok
|
|
|
|
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured;
|
|
0 filtered out; finished in 0.00s
|
|
```
|
|
|
|
## CLI run
|
|
|
|
```
|
|
INFO spike: Secret Service unavailable (Platform secure storage failure:
|
|
DBus error: Cannot create an item in a locked collection);
|
|
falling back to keyutils
|
|
INFO spike: writing secret identity.primary
|
|
INFO spike: reading back
|
|
INFO spike: round-trip OK (48 bytes)
|
|
INFO spike: deleting
|
|
INFO spike: post-delete read correctly returned NotFound
|
|
OK — Linux Secret Service round-trip verified.
|
|
```
|
|
|
|
This CLI output also evidences SS-AUD-005 in practice: a locked Secret
|
|
Service collection produced a typed `Backend` error containing no secret
|
|
material, which the caller could then route to the fallback adapter
|
|
rather than propagating the raw D-Bus message to the user.
|
|
|
|
## Audit-check coverage
|
|
|
|
| Check | Mapped test(s) | Result |
|
|
|---|---|---|
|
|
| SS-AUD-001 (Identity secret absent from local DB) | `ss_aud_001_identity_secret_absent_from_local_db` | PASS — raw SQLite file scanned for plaintext; only the lookup name appears. |
|
|
| SS-AUD-002 (Server password absent from local DB) | `ss_aud_002_server_password_absent_from_local_db` | PASS — same scan, distinct plaintext marker. |
|
|
| SS-AUD-003 (Secret values not in logs) | `ss_aud_003_secret_values_not_in_logs` | PASS — captured `tracing` output contains `<redacted>` markers; never contains the plaintext. |
|
|
| SS-AUD-005 (Failure → safe error) | `ss_aud_005_safe_error_on_missing_entry` + CLI fallback path | PASS — `NotFound` is the typed Display, no leakage. |
|
|
| SS-AUD-006 (Delete removes entry) | `ss_aud_006_delete_removes_entry` | PASS — second delete returns `NotFound`, not silent success. |
|
|
| SS-TC-003 (Linux round-trip) | `ss_tc_003_round_trip_linux` | PASS — set/get equality + delete + post-delete `NotFound`. |
|
|
|
|
## What this spike does NOT validate
|
|
|
|
- SS-AUD-004 (diagnostic export redaction) — `diagnostics-redaction-spike`.
|
|
- SS-AUD-007 (per-platform documentation completeness).
|
|
- SS-AUD-008 (migration / import safety).
|
|
- SS-TC-001 (Windows DPAPI / Credential Manager).
|
|
- SS-TC-002 (macOS Keychain).
|
|
- SS-TC-004 (Android Keystore).
|
|
- SS-TC-005 (iOS Keychain).
|
|
- Concurrent access from multiple threads / processes.
|
|
- Long-lived persistence behaviour across reboots (keyutils backend is
|
|
session-scoped by design).
|
|
- Behaviour under a locked or absent Secret Service collection in
|
|
*production* (mitigation strategy is shown but not policy).
|
|
|
|
## Notable observations
|
|
|
|
- The kernel keyutils session keyring is inherited from the calling
|
|
process; non-interactive shells can present an *expired* `_ses`
|
|
keyring. The wrapping `keyctl session -` is the documented workaround
|
|
and is universal on Linux. Production code on a graphical session
|
|
inherits a valid session from PAM and does not need it.
|
|
- gnome-keyring on this host was running but the default collection
|
|
was locked because no graphical login had occurred. The CLI's
|
|
Secret-Service-first / keyutils-fallback path observed exactly this
|
|
condition and reacted correctly, doubling as live evidence for
|
|
SS-AUD-005.
|
|
- The `Secret` newtype's `Debug` formatter prints
|
|
`Secret(<N bytes redacted>)` and `Display` prints `<redacted>`. The
|
|
SS-AUD-003 test exercises both representations.
|