60 lines
3.4 KiB
Markdown
60 lines
3.4 KiB
Markdown
# Secure secret storage
|
|
|
|
`crates/storage` owns the complete secret-storage contract. Configuration,
|
|
password-store repositories, Git configuration, command arguments, and logs
|
|
contain only opaque identifiers; passphrases, tokens, and HTTPS passwords are
|
|
stored by the operating system.
|
|
|
|
`SecretReference` has three validated forms. OpenPGP passphrases are keyed by the
|
|
primary fingerprint. HTTPS Git credentials are keyed by purpose, server ID,
|
|
application ID, and account. The account is kept inside the protected record,
|
|
so a configured server/application pair can retrieve it without adding an
|
|
account or secret value to TOML. SSH private-key passphrases are keyed by the
|
|
key's SHA-256 fingerprint. References, locators, store state, and errors all use
|
|
redacted `Debug` output.
|
|
|
|
Stored values use a small versioned binary envelope containing their reference
|
|
and secret bytes. Retrieval validates the envelope and exact reference before
|
|
release. Malformed or ambiguous records produce a typed `Corrupted` error;
|
|
asking for another account at the same Git locator produces `Missing` without
|
|
altering the stored account. `create` refuses to overwrite, `replace` requires
|
|
an exact existing record, and missing, denied, cancelled, locked, corrupted,
|
|
unsupported-policy, and unavailable-store results remain distinct. Returned
|
|
and cached bytes use `SecretBytes`, which zeroizes its allocation when dropped.
|
|
|
|
## Operating-system adapters
|
|
|
|
The platform-selection code is isolated in
|
|
`crates/storage/src/secret_store/platform.rs` and calls only safe Rust APIs:
|
|
|
|
- macOS uses legacy Keychain for command-line-compatible device-unlocked
|
|
credentials and Protected Data for `RequireUserPresence`; iOS uses Protected
|
|
Data. User cancellation is mapped from the native Security Framework status.
|
|
- Windows uses Credential Manager. Store operations are serialized because the
|
|
upstream adapter documents unreliable same-entry sequencing across threads.
|
|
- Linux uses Secret Service through zbus with the Rust cryptography feature. It
|
|
does not launch `secret-tool`, a shell, or any other helper. A missing session
|
|
service is a typed `Unavailable` result.
|
|
|
|
IronStorage contains no direct platform FFI or unsafe Rust. The selected adapter
|
|
crates own their OS calls, so there is no project-local FFI safety contract
|
|
beyond providing validated UTF-8 identifiers and bounded byte slices.
|
|
|
|
## Locking and caching
|
|
|
|
A new store starts logically locked. `unlock` must succeed before create,
|
|
retrieve, replace, or delete; `lock` immediately clears all cached values even
|
|
if the platform lock operation reports an error. Caching is disabled unless a
|
|
caller explicitly selects `SecretCachePolicy::Timed`. Timed policies are capped
|
|
at 128 entries and 15 minutes, expire lazily, and are always cleared on lock.
|
|
|
|
The same unlocked store implements the OpenPGP `SecretProvider`, HTTPS Git
|
|
`GitCredentialProvider`, and SSH `SshPassphraseProvider`. CLI, TUI, and desktop
|
|
pass one prompted SSH passphrase as zeroizing bytes for one retry. Storage binds
|
|
that override to the requested key fingerprint, and the authentication handle
|
|
persists it only after the Git operation succeeds; cancellation, rejection, and
|
|
other failures never create or replace a record. Prompts are hidden or masked,
|
|
and secret values are excluded from arguments, history, normal output, debug
|
|
models, notifications, and the clipboard. Tests inject a memory backend and
|
|
never access a developer or CI user keyring.
|