diff --git a/SECURITY.md b/SECURITY.md index 7514e1b..c540b66 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,9 +1,15 @@ # Security -codex32 handles wallet recovery material. Use a trusted computer and have the +codex32 handles wallet recovery material. A **trusted computer** is under your +exclusive control, is not known or suspected to be compromised, and runs an +operating system and other software you trust for the operation. For wallet +work, that trusted software includes Python, codex32, the terminal, +`bitcoin-cli`, Bitcoin Core, and their relevant configuration. Wallet encryption, +application permissions, and RPC authentication do not make a compromised +computer trusted. Have the software and recovery plan reviewed before relying on it with funds. For -stronger isolation, use codex32 and the Bitcoin Core signing wallet only on a -computer kept permanently offline. +stronger isolation, keep the trusted computer disconnected from every network +before, while, and after it handles private recovery or signing material. A valid checksum detects many copying mistakes. It does not prove that a backup belongs to your wallet. A correction is only a suggestion; compare it with the @@ -11,10 +17,16 @@ physical backup and wallet information kept elsewhere. Python, your terminal, and your operating system may retain secret text in memory or scrollback. codex32 does not intentionally save secrets and keeps -them out of command arguments and normal machine output, but it cannot -guarantee that every copy is erased from swap, hibernation data, or crash -dumps. Using Tails and shutting down when finished helps mitigate this Python -limitation. +them out of command arguments, logs, and unrelated output. Commands that create, +recover, derive, correct, or explicitly export recovery material intentionally +display it when that is their purpose. codex32 cannot guarantee that every copy +is erased from swap, hibernation data, or crash dumps. Using Tails and shutting +down when finished helps mitigate this Python limitation. + +Run a command first and enter recovery material at its prompt, or redirect its +standard input from a separately protected source. Do not embed recovery text in +shell command text: shell history, terminal logging, wrappers, and process +tooling may retain it even though codex32 never receives it as an argument. ## Report a security problem diff --git a/docs/security/invariants.md b/docs/security/invariants.md index 5ad144a..b0c2dd7 100644 --- a/docs/security/invariants.md +++ b/docs/security/invariants.md @@ -24,8 +24,11 @@ and evidence. warning and literal `YES` confirmation for every HRP. The bound includes all admitted classes ranked equal to or better than the candidate, independently of execution order. -6. Secrets stay out of arguments, logs, ordinary output, and public transfers. - Private descriptors exist only in Python memory and child stdin. +6. Secrets stay out of command arguments, logs, unrelated output, and public + transfers. Commands whose purpose is to create, recover, derive, correct, or + explicitly export recovery material may intentionally display that material + to the operator. Private descriptors exist only in Python memory and child + stdin. 7. Bitcoin Core chains are discovered before entropy or recovery input. The operator confirms an eligible descriptor wallet by exact name. 8. Wallet state is revalidated before import. Every import must succeed and the @@ -34,8 +37,8 @@ and evidence. and verified on every exit path. 10. External text, Core output, public wallet data, and PSBTs are untrusted. 11. Only Bitcoin Core descriptor wallets sign with codex32-derived keys. - Sensitive operations use only codex32 or Core on malware-free computers - with trusted software. + Sensitive operations use only codex32 or Core on a trusted computer as + defined by the security model. 12. Offline hosts disable every network path, including Ethernet, internet, Tor, Wi-Fi, Bluetooth, and cellular. Online Core nodes synchronize before their balances or history are trusted. diff --git a/docs/security/model.md b/docs/security/model.md index 70cbbd8..91467cc 100644 --- a/docs/security/model.md +++ b/docs/security/model.md @@ -31,13 +31,21 @@ fingerprints, and wallet history cannot spend funds but remain privacy-sensitive ## Operator assumptions +A **trusted computer** is under the operator's exclusive control, is not known +or suspected to be compromised, and runs an operating system and other software +the operator trusts for the operation. For codex32 wallet work, that trusted +software includes the Python environment, codex32, the terminal, `bitcoin-cli`, +Bitcoin Core, and their relevant configuration. An offline trusted computer +remains disconnected from every network before, while, and after it handles +private recovery or signing material. Wallet encryption, application +permissions, and RPC authentication do not make a compromised computer trusted. + The operator must: - use only Bitcoin Core descriptor wallets to sign with keys derived from a codex32 master seed; -- use computers believed malware-free and whose other software is trusted for - all codex32 operations and for wallet initialization and signing with Bitcoin - Core; +- use only trusted computers as defined above for codex32 operations, wallet + initialization, and signing with Bitcoin Core; - disconnect every computer used for offline codex32 or signing work from all network paths, including Ethernet, internet, Tor, Wi-Fi, Bluetooth, and cellular; @@ -52,6 +60,14 @@ The operator must: - never put recovery text in command arguments or transfer a master seed, share, xprv, or private descriptor through QR or a network service. +`create`, `secret`, `share`, `correct`, and `xprv` can intentionally display +secret-bearing recovery material because producing or exporting that material is +their purpose. This is distinct from accidental disclosure: unrelated status, +diagnostic, logging, and wallet-integration output must not reveal secrets. +Prompted input and standard-input redirection keep recovery text out of process +arguments, but the operator must also keep the text out of shell command text; +shell history, terminal logging, wrappers, or process tooling may retain it. + ## Security limitations - Python cannot guarantee zeroization, constant-time execution, locked memory, diff --git a/docs/user/guide.md b/docs/user/guide.md index eedd5a7..bf45b97 100644 --- a/docs/user/guide.md +++ b/docs/user/guide.md @@ -79,16 +79,24 @@ codex32 detects and reports the local Bitcoin Core network. You do not need to change `bitcoin.conf` when using the standard local data directory and RPC port. -Use a computer you believe is malware-free and whose other software you trust. -Only codex32 and Bitcoin Core should perform recovery, derivation, wallet -initialization, or signing. The QR tools below transport only public -descriptors or PSBTs. +Use a trusted computer: one under your exclusive control, not known or suspected +to be compromised, with its operating system, Python environment, terminal, +codex32, `bitcoin-cli`, Bitcoin Core, and relevant configuration trusted for the +operation. Wallet encryption or RPC authentication does not make a compromised +computer safe. An offline trusted computer stays disconnected from every network +before, while, and after it handles private recovery or signing material. Only +codex32 and Bitcoin Core should perform recovery, derivation, wallet +initialization, or signing. The QR tools below transport only public descriptors +or PSBTs. Bitcoin Core wallet encryption is strongly recommended. Bitcoin Core owns the passphrase and its prompts; codex32 never asks for, reads, or forwards it. -Do not type recovery text on the same line as a command. Run the command first, -then enter a master seed or shares on the separate `>` line when prompted. +Do not type recovery text on the same line as a command: shell history, terminal +logging, wrappers, or process tooling may retain the command text. Run the +command first, then enter a master seed or shares on the separate `>` line when +prompted. Standard-input redirection also keeps recovery text out of process +arguments, but protect the redirected source separately. This keeps a 48-character string grouped in fours within an 80-column terminal. Later share prompts may show a fixed common header after `>`. Never photograph recovery text or put it in a website, chat, cloud clipboard, or online QR diff --git a/src/codex32/_cli_parser.py b/src/codex32/_cli_parser.py index fe273a4..52a44bf 100644 --- a/src/codex32/_cli_parser.py +++ b/src/codex32/_cli_parser.py @@ -86,8 +86,9 @@ def parser(prog: str = "codex32", *, master_seed: bool = False) -> argparse.Argu if master_seed else "Check, correct, recover, and derive shares from codex32 backups." ), - epilog="Never include a secret or share in command arguments.\n" - "Enter it when prompted. Some commands also accept piped input.", + epilog="Never put a secret or share in command arguments or shell command text.\n" + "Enter it when prompted; some commands also accept redirected standard input.\n" + "Protect redirected sources separately: shells, terminals, and wrappers may retain text.", formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False, ) diff --git a/tests/test_generic_hrp.py b/tests/test_generic_hrp.py index 341e504..921c714 100644 --- a/tests/test_generic_hrp.py +++ b/tests/test_generic_hrp.py @@ -123,8 +123,9 @@ def test_cli_split_and_unknown_neutral_summary() -> None: " create create or confirm a backup, or split an existing secret\n" " wallet restore a Bitcoin Core wallet\n" " xprv export the root extended private key\n\n" - "Never include a secret or share in command arguments.\n" - "Enter it when prompted. Some commands also accept piped input.\n" + "Never put a secret or share in command arguments or shell command text.\n" + "Enter it when prompted; some commands also accept redirected standard input.\n" + "Protect redirected sources separately: shells, terminals, and wrappers may retain text.\n" ) for command in ("check", "correct", "secret", "share"): assert command in generic_help and command in ms_help