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.
90 lines
3.6 KiB
Markdown
90 lines
3.6 KiB
Markdown
# Secure Storage Spike
|
|
|
|
Chanora proof-of-concept. **Not product code.**
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| PoC name | `secure-storage-spike` |
|
|
| PoC plan | [`docs/architecture/proof-of-concept-plan.md`](../../docs/architecture/proof-of-concept-plan.md) §2 |
|
|
| Purpose | Prove platform secure storage behaviour |
|
|
| Exit criterion | "Secret write/read/delete works through platform secure storage" |
|
|
| Authority | ADR-006 (`SecureStore trait + per-platform adapters`), SDD-078, SRS-091..095, SysRS-158..162 |
|
|
| Audit refs | `docs/security/secure-storage-audit-report.md` SS-AUD-001..006 / SS-TC-003 |
|
|
|
|
## What it proves
|
|
|
|
- A typed `SecretStorageRepository` trait that the rest of the Rust core
|
|
can depend on without backend leakage (SAD-067).
|
|
- A Linux adapter selecting between two equivalent backends per
|
|
SysRS-162 ("Secret Service, libsecret, **or equivalent**"):
|
|
- **Secret Service / libsecret** (gnome-keyring, kwallet, KeePassXC, …).
|
|
- **Kernel keyutils** (`add_key(2)` / `request_key(2)`) — used when no
|
|
D-Bus session or unlocked Secret Service collection is available.
|
|
- A typed `SecureStoreError` with `NotFound` / `Unavailable` / `Backend`
|
|
arms, satisfying SS-AUD-005 (safe error mapping).
|
|
- A `Secret` newtype with redacting `Debug` / `Display` and
|
|
zero-on-drop, satisfying SS-AUD-003 defence-in-depth.
|
|
- A miniature `LocalDatabaseRepository` (rusqlite, bundled) that holds
|
|
only **references** to secret names — proving the SAD-067 separation
|
|
empirically (SS-AUD-001, SS-AUD-002).
|
|
|
|
## Layout
|
|
|
|
```text
|
|
secure-storage-spike/
|
|
src/
|
|
lib.rs # crate root + re-exports
|
|
secret.rs # SecretStorageRepository trait, Secret type, Linux adapter
|
|
sqlite_repo.rs # LocalDatabaseRepository (non-secret state only)
|
|
test_logger.rs # in-memory log capture for SS-AUD-003
|
|
main.rs # secure-storage-cli driver (SS round-trip)
|
|
tests/
|
|
audit.rs # SS-AUD-001/002/003/005/006 + SS-TC-003
|
|
Cargo.toml
|
|
```
|
|
|
|
## Reproduce
|
|
|
|
Requires Rust stable (developed against 1.95). Linux only — non-Linux
|
|
adapters are out of scope for this spike. `cargo` needs network access
|
|
on first build.
|
|
|
|
```bash
|
|
# Run the audit test suite (uses kernel keyutils backend; hermetic).
|
|
keyctl session - cargo test --tests
|
|
|
|
# Run the CLI driver. It prefers Secret Service and falls back to
|
|
# keyutils if the default collection is locked. Either path satisfies
|
|
# SysRS-053 / SysRS-162.
|
|
keyctl session - cargo run --bin secure-storage-cli
|
|
```
|
|
|
|
> **Why `keyctl session -`?**
|
|
> The Linux kernel session keyring is inherited from the calling process.
|
|
> Cargo's test/run wrappers often inherit an expired or empty
|
|
> `_ses` keyring from non-interactive shells. `keyctl session -` creates
|
|
> a fresh session keyring before invoking the command, guaranteeing a
|
|
> valid backend for keyutils. Interactive desktop sessions normally do
|
|
> not need this wrapper.
|
|
|
|
## Scope boundaries
|
|
|
|
This spike is intentionally narrow. Out of scope:
|
|
|
|
- **Windows / macOS / iOS / Android adapters.** SS-TC-001/002/004/005
|
|
remain unverified. The trait is shaped to accommodate them but no
|
|
code is shipped here.
|
|
- **SS-AUD-004 (diagnostic export redaction).** Owned by the
|
|
`diagnostics-redaction-spike`.
|
|
- **SS-AUD-007 (per-platform documentation).** Doc-only check; lives
|
|
in the audit report itself.
|
|
- **SS-AUD-008 (migration / import).** No migration paths exist yet.
|
|
- **Encryption at rest** beyond what the platform backend provides.
|
|
- **Constant-time secret comparison.** `Secret::eq` is best-effort.
|
|
- **Threading / async semantics.** Trait is `Send + Sync` but no
|
|
concurrent stress is exercised.
|
|
|
|
## Verification log
|
|
|
|
See `VERIFICATION.md` in this directory.
|