77 lines
3.9 KiB
Markdown
77 lines
3.9 KiB
Markdown
# Embedded OpenPGP compatibility
|
|
|
|
All password-store cryptography lives in `crates/storage`. IronStorage does not
|
|
read a user's GnuPG keyring and does not launch `gpg`, `gpg-agent`, `pass`, or a
|
|
pinentry process. Applications import explicit exported key files and supply a
|
|
protected key's unlock secret through the `SecretProvider` interface.
|
|
|
|
## Backend decision
|
|
|
|
IronStorage uses [`pgp` 0.20](https://crates.io/crates/pgp/0.20.0) with default
|
|
features disabled. The crate is `MIT OR Apache-2.0`, is implemented in Rust,
|
|
and does not introduce a native OpenPGP library or runtime helper process.
|
|
`rand` 0.8 supplies `OsRng` for session keys and signatures. The alternatives
|
|
and their license consequences are recorded in `DEPENDENCIES.md`.
|
|
|
|
The storage API imports binary transferable keys and ASCII-armored public or
|
|
secret keys. Self-signatures and subkey bindings are verified before a key is
|
|
committed to the in-memory store. An import stream is applied transactionally,
|
|
duplicate public/secret exports are merged by primary fingerprint, and file and
|
|
key-count limits bound untrusted input. Directory loading rejects symbolic
|
|
links and non-regular files.
|
|
|
|
## Identity and recipient behavior
|
|
|
|
The resolver accepts:
|
|
|
|
- a full primary or subkey fingerprint;
|
|
- an 8- or 16-hex-digit primary or subkey key ID, with optional `0x` prefix;
|
|
- an exact UTF-8 user ID.
|
|
|
|
Short identifiers and user IDs must identify exactly one primary certificate.
|
|
Missing and ambiguous identities are separate errors. `.gpg-id` parsing follows
|
|
upstream `pass`: text after `#` is ignored, surrounding whitespace is removed,
|
|
blank lines are skipped, and duplicate resolved recipients are coalesced.
|
|
|
|
Encryption selects a non-revoked encryption-capable subkey, falling back to an
|
|
encryption-capable primary key. Output uses AES-256 in a version 1
|
|
symmetrically-encrypted integrity-protected data packet and deliberately does
|
|
not add a compression packet, matching upstream `pass --compress-algo=none`.
|
|
Every resolved recipient receives a public-key encrypted session-key packet.
|
|
|
|
Decryption first examines those recipient packets. A secret is requested only
|
|
for imported protected keys that can match the message; unavailable identities
|
|
are skipped so any one recipient of a multi-recipient entry can decrypt it.
|
|
Cancellation stops immediately. Wrong secrets, malformed messages, and a lack
|
|
of matching secret keys remain distinct failures.
|
|
|
|
## Secret lifetime and recipient signatures
|
|
|
|
Decrypted data and provider-returned unlock secrets use `SecretBytes`. Its
|
|
debug representation is redacted and its allocation is zeroed on drop. The
|
|
OpenPGP backend's password type also zeroes its owned storage. Plaintext enters
|
|
the message encoder through an owning reader instead of being copied into an
|
|
ordinary intermediate buffer.
|
|
The production provider is the storage-owned native secret store documented in
|
|
[`secure-secret-storage.md`](secure-secret-storage.md); protected-key
|
|
passphrases are addressed only by their primary fingerprint.
|
|
|
|
Detached `.gpg-id.sig` files use binary-document signatures with SHA-256 and
|
|
issuer fingerprint/key-ID metadata. Verification succeeds only when the valid
|
|
signature belongs to an explicitly allowed primary fingerprint, which is the
|
|
`PASSWORD_STORE_SIGNING_KEY` trust rule; a cryptographically valid signature
|
|
from another imported key is rejected.
|
|
|
|
## Compatibility evidence
|
|
|
|
`crates/storage/tests/fixtures/compatibility` contains protected public and
|
|
secret exports, single- and multi-recipient `.gpg` entries, and signed
|
|
recipient files. The fixture audit established that GnuPG decrypts the packet
|
|
profile used by the generator. Production tests then exercise that same
|
|
uncompressed `MessageBuilder` profile through `KeyStore::encrypt`, independently
|
|
parse and decrypt its output, decrypt every checked-in GnuPG-audited entry, and
|
|
verify both checked-in and newly generated recipient signatures.
|
|
|
|
Tests never use a real user keyring and application runtime never executes an
|
|
external cryptographic tool.
|