feat(security): encrypt stored credentials at rest with keychain-backed master key - #114
feat(security): encrypt stored credentials at rest with keychain-backed master key#114sunxiaobin89 wants to merge 2 commits into
Conversation
|
@GOODBOY008 Friendly ping on this one — happy to adjust or rebase as needed. 🙂 One thing I noticed: since this PR was opened, |
570b52a to
982d418
Compare
|
Rebased onto the latest While rebasing, I also folded the new SSH tunnel (jump host) fields from #79 into the encryption scope: Verification: 718 frontend tests + 170 Rust tests pass; |
…ed master key Store connection secrets (password, passphrase, proxyPassword, vncPassword) encrypted in localStorage using AES-256-GCM with a single app-level master key kept in the OS keychain, instead of plaintext. - Rust: credential_seal/credential_open commands + master_key() that reads (or first-use creates) a random 32-byte key from the OS keychain via the keyring crate. Sealed format: v1:<b64 nonce>:<b64 ciphertext>. - Frontend: credential-crypto.ts (SECRET_FIELDS, seal/open, legacy migration); connection-storage.persistConnections() is the single write path and strips any non-v1: plaintext secret as a defensive layer. - Startup migration seals legacy plaintext (keeps plaintext on failure). - Edit dialog never echoes a stored secret; blank keeps stored, typed replaces. - Export strips all secret fields from shared config bundles. - Tests updated for sealed v1: values; 636 frontend + 152 Rust pass. Fixes GOODBOY008#100
- Add tunnelPassword and tunnelPassphrase to SECRET_FIELDS so the jump host credentials get the same AES-256-GCM treatment as other secrets - Seal tunnel secrets in every dialog persist path; blank fields keep the stored value - Never echo stored tunnel secrets back into the edit form (same hint as the password field) - Restore blank tunnel secrets from storage (decrypted) when a saved connection is used to connect Test: 718 frontend tests + 170 Rust tests pass; tsc and i18n parity clean
982d418 to
c5fbea6
Compare
|
Rebased again onto Verification: 732 frontend tests + 177 Rust tests pass; |
Summary
Encrypts connection secrets (SSH password, key passphrase, proxy password, VNC password) at rest. Previously these were stored in plaintext in the app's WebView
localStorage; the export feature also wrote them into shareable files.Approach
Single app-level master key + AES-256-GCM:
keyringcrate, one entry for the whole app → one authorization prompt, never per-credential).v1:<b64 nonce>:<b64 ciphertext>. Plaintext only exists in memory transiently at connect time — never written tolocalStorage.persistConnections()is the single write path and strips any non-v1:plaintext secret as a defensive layer.Verification
tsc --noEmitclean; i18n key parity (en/zh-CN) holds.Fixes #100