modified doc to v1.0.5

This commit is contained in:
LC mac
2026-01-07 00:40:47 +08:00
parent 4cf32f9ba0
commit ce26b3560a

View File

@@ -1,284 +1,306 @@
# pyhdwallet v1.0.3
# pyhdwallet v1.0.5 (hdwalletpy)
A command-line tool for generating and recovering HD wallets (BIP39) with support for Ethereum, Solana, and Bitcoin. Features offline operation, PGP encryption, and multi-chain address derivation.
A command-line tool for generating and recovering HD wallets (BIP39) with support for Ethereum, Solana, and Bitcoin. It is designed for offline operation, optional PGP encryption, and writing deterministic AES-encrypted ZIP “wallet artifacts” into a local `.wallet/` folder.
Repository structure (current):
- `src/pyhdwallet.py` — main CLI script
- `.wallet/` — generated wallet artifacts (should be gitignored)
- `requirements.in`, `requirements.txt`
- `README.md`, `playbook.md`, `LICENSE`
**Note**: This tool requires a subcommand (e.g., `gen`, `recover`). Running without one displays help. Use `<subcommand> -h` for detailed options.
## Table of Contents
- [Features](#features)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Commands](#commands)
- [fetchkey](#fetchkey)
- [gen](#gen)
- [recover](#recover)
- [test](#test)
- [Examples](#examples)
- [Security](#security)
- [Troubleshooting](#troubleshooting)
- [Changelog](#changelog)
---
## Features
- **Offline-first**: Generate and recover wallets without internet access.
- **Multi-chain support**: Derive addresses for Ethereum, Solana, and Bitcoin.
- **PGP encryption**: Securely encrypt sensitive data (mnemonics, private keys) to PGP public keys.
- **Flexible input**: Accept BIP39 mnemonics or hex seeds.
- **BIP39 passphrase support**: Optional passphrase for additional security.
- **Private key export**: Export derived private keys in encrypted payloads.
- **Solana profiles**: Multiple derivation paths for Solana compatibility.
- **Self-testing**: Built-in tests to verify functionality.
- **Off-screen mode**: Optional paranoid mode with memory zeroing, temp files, and no output printing for high-security use.
- **Offline-first**: `gen`, `recover`, and `test` block network access (best-effort in-process guard).
- **Multi-chain support**: Derives addresses for Ethereum, Solana, and Bitcoin.
- **PGP encryption**: Encrypts a JSON payload to a PGP public key and outputs an ASCII-armored PGP message (typically saved as `.asc`). [web:46]
- **AES-encrypted ZIP artifacts**: When `--file` is used, output is written as an AES-encrypted ZIP via `pyzipper`. [web:102]
- **TTY safety guard + --force**: If stdout is piped/redirected (non-TTY), the tool refuses to print sensitive data unless `--force` is explicitly set (to avoid accidental leaks into logs/files). `isatty()` is the classic way to detect whether stdout is connected to a terminal.
- **Off-screen mode**: `--off-screen` suppresses printing sensitive data to stdout.
---
## Installation
### Prerequisites
- Python 3.11 or higher
- Virtual environment (recommended)
- Python 3.11+
- Recommended: a virtual environment
### Setup
1. Clone or download the repository.
2. Create a virtual environment:
```bash
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
```
```bash
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
```
### Dependencies (top-level intent)
3. Install dependencies:
- `bip-utils` — BIP39 + BIP derivation logic
- `PGPy` — encryption to OpenPGP public keys [web:46]
- `pynacl` + `base58` — Solana seed/key handling
- `pyzipper` — AES-encrypted ZIP writing (only needed when using `--file`) [web:102]
```bash
pip install -r requirements.txt
```
### Dependencies
- `bip-utils`: BIP39 and derivation logic
- `PGPy`: PGP encryption
- `PyNaCl` & `base58`: Solana private key handling (optional for Solana private key export)
---
## Quick Start
1. Generate a new wallet:
### 1) Generate (debug/test; prints mnemonic)
```bash
python ./src/pyhdwallet.py gen --chains ethereum solana --addresses 3
```
```bash
python ./src/pyhdwallet.py gen
```
2. Recover from a mnemonic:
### 2) Generate and save AES ZIP artifact to `.wallet/`
```bash
python ./src/pyhdwallet.py recover --mnemonic "abandon abandon ... about" --chains bitcoin
```
```bash
python ./src/pyhdwallet.py gen --file
```
3. Fetch a PGP key:
### 3) Generate with PGP encryption and ZIP (recommended for “at-rest” storage)
```bash
python ./src/pyhdwallet.py fetchkey "https://example.com/key.asc" --out mykey.asc
```
```bash
python ./src/pyhdwallet.py gen --pgp-pubkey-file pubkeys/mykey.asc --file
```
4. Use off-screen mode for high-security operations:
### 4) Recover (addresses) from mnemonic
```bash
python ./src/pyhdwallet.py gen --off-screen --pgp-pubkey-file key.asc --chains ethereum --addresses 1
```
```bash
python ./src/pyhdwallet.py recover --mnemonic "abandon abandon ... about"
```
5. Run tests:
### 5) Recover with interactive input + ZIP artifact
```bash
python ./src/pyhdwallet.py test
```
```bash
python ./src/pyhdwallet.py recover --interactive --file
```
### 6) Run tests
```bash
python ./src/pyhdwallet.py test
```
---
## Outputs and file structure
### Stdout behavior
- `gen` prints the mnemonic + derived addresses by default (intended for debug/test comparisons).
- `--off-screen` suppresses printing sensitive data to stdout.
### `--file` behavior (deterministic, secured output)
If `--file` is present, the tool writes **only** an AES-encrypted ZIP file (no raw `.json`/`.asc` file is left on disk). AES ZIP is implemented using `pyzipper`. [web:102]
Default output folder:
- `./.wallet/`
Override folder:
- `--wallet-location /path/to/folder`
Naming uses UTC timestamps (e.g. `20260106_161830Z`):
- No PGP: zip contains `test_wallet_<UTC>.json`, zip name `test_wallet_<UTC>.zip`
- With PGP: zip contains `encrypted_wallet_<UTC>.asc`, zip name `encrypted_wallet_<UTC>.zip`
### Password handling for ZIP
- Default: `--zip-password-mode prompt` prompts for the ZIP password (attempts hidden entry).
- If hidden entry is not supported in the environment, it falls back to visible `input()` with loud warnings.
- Optional: `--zip-password-mode auto` generates a Base58 password (length controlled by `--zip-password-len`).
- If auto mode is used, password is shown **only if** `--show-generated-password` is set, and it prints to **stderr** (not stdout) to reduce accidental capture when stdout is redirected.
`pyzipper` supports AES encryption via `AESZipFile` and password-setting APIs. [web:102]
---
## Commands
### fetchkey
### fetchkey (online)
Download and verify a PGP public key from a URL.
**Usage:**
```bash
python ./src/pyhdwallet.py fetchkey <url> [--out FILE] [--timeout SECONDS]
python ./src/pyhdwallet.py fetchkey <url> [--out FILE] [--timeout SECONDS] [--off-screen]
```
**Options:**
Options:
- `url`: URL to the ASCII-armored PGP key
- `--out FILE`: Save the key to a file
- `--timeout SECONDS`: Request timeout (default: 15)
- `--off-screen`: Enable off-screen mode (temp files, no extra output)
- `url`: URL to the ASCII-armored PGP public key
- `--out FILE`: save key to file
- `--timeout SECONDS`: request timeout (default 15)
- `--off-screen`: reduced output / temp-file behavior
**Example:**
---
```bash
python ./src/pyhdwallet.py fetchkey "https://keys.openpgp.org/pks/lookup?op=get&search=user@example.com" --out key.asc
```
### gen (offline)
### gen
Generate a new BIP39 mnemonic and derive addresses.
**Usage:**
Generate a BIP39 mnemonic and derive addresses.
```bash
python ./src/pyhdwallet.py gen [options]
```
**Options:**
Core options:
- `--words {12,15,18,21,24}`: Number of mnemonic words (default: 12)
- `--dice-rolls "1 2 3 ..."`: Space-separated dice rolls for entropy
- `--passphrase`: Prompt for BIP39 passphrase interactively
- `--passphrase-hint HINT`: Hint for the passphrase
- `--chains {ethereum,solana,bitcoin}`: Chains to derive (default: all)
- `--addresses N`: Number of addresses per chain (default: 5)
- `--sol-profile {phantom_bip44change,phantom_bip44,phantom_deprecated,solana_bip39_first32}`: Solana derivation profile
- `--output {text,json}`: Output format (default: text)
- `--file FILE`: Save output to file
- `--pgp-pubkey-file FILE`: Encrypt payload to PGP key
- `--pgp-ignore-usage-flags`: Ignore PGP key usage flags
- `--export-private`: Include private keys in encrypted payload
- `--include-source`: Include mnemonic in encrypted payload
- `--unsafe-print`: Print mnemonic even when encrypting
- `--off-screen`: Enable off-screen mode (no printing, temp files, memory zeroing)
- `--words {12,15,18,21,24}`
- `--dice-rolls "1 2 3 ..."` (space-separated; adds user entropy)
- `--passphrase` (prompts for BIP39 passphrase)
- `--passphrase-hint HINT`
- `--chains ethereum solana bitcoin`
- `--addresses N`
- `--sol-profile {phantom_bip44change,phantom_bip44,phantom_deprecated,solana_bip39_first32}`
**Examples:**
PGP options:
```bash
# Basic generation
python ./src/pyhdwallet.py gen
- `--pgp-pubkey-file FILE` (encrypt payload to pubkey; `.asc` content) [web:46]
- `--pgp-ignore-usage-flags`
# With off-screen mode
python ./src/pyhdwallet.py gen --off-screen --pgp-pubkey-file key.asc
Artifact options:
# With passphrase and encryption
python ./src/pyhdwallet.py gen --passphrase --pgp-pubkey-file key.asc --export-private
- `--file` (write AES ZIP only)
- `--wallet-location PATH` (default: `./.wallet`)
- `--zip-password-mode {prompt,auto}`
- `--zip-password-len N` (default: 12)
- `--show-generated-password` (auto mode only; prints password to stderr)
# JSON output to file
python ./src/pyhdwallet.py gen --chains ethereum --addresses 10 --output json --file wallet.json
```
Safety options:
### recover
- `--off-screen` (dont print sensitive output)
- `--force` (only for non-TTY stdout; see below)
Derive addresses from an existing mnemonic or seed.
---
**Usage:**
### recover (offline)
Recover addresses from mnemonic or seed and optionally write a ZIP artifact.
```bash
python ./src/pyhdwallet.py recover [options]
```
**Options:** Same as `gen`, plus:
Input options (choose one):
- `--mnemonic MNEMONIC`: BIP39 mnemonic phrase
- `--seed HEX_SEED`: 128-character hex seed
- `--interactive`: Prompt for mnemonic/seed interactively
- `--off-screen`: Enable off-screen mode (no printing, temp files, memory zeroing)
- `--mnemonic "..."` (not recommended due to shell history; prefer `--interactive`)
- `--seed HEX` (128 hex chars / 64 bytes)
- `--interactive`
**Examples:**
Additional options:
- same chain/PGP/ZIP/off-screen/force options as `gen`
Notes:
- `--export-private` requires `--pgp-pubkey-file` (private keys only travel inside encrypted payload).
- `--include-source` controls whether mnemonic/seed is included inside the encrypted payload (only meaningful when PGP is used).
---
### test (offline)
Run minimal self-tests.
```bash
# From mnemonic
python ./src/pyhdwallet.py recover --mnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" --chains ethereum solana
# Interactive input
python ./src/pyhdwallet.py recover --interactive --chains bitcoin
# From seed
python ./src/pyhdwallet.py recover --seed "0123456789abcdef..." --chains solana
python ./src/pyhdwallet.py test
```
### test
---
Run minimal self-tests to verify functionality.
## When to use `--force`
**Usage:**
Use `--force` only if you **intentionally** want to print sensitive output while stdout is being piped/redirected.
Why it exists:
- When you pipe/redirect output (e.g., `>` or `|`), stdout is typically **not a TTY**.
- The program checks `sys.stdout.isatty()` and refuses to print secrets by default to avoid accidental leakage into files/logs.
- `isatty()` is commonly used to detect terminal vs pipe/redirect.
Examples:
```bash
python ./src/pyhdwallet.py test [--off-screen]
# This redirects stdout to a file (non-TTY). The tool will refuse unless forced.
python ./src/pyhdwallet.py gen > out.txt
# Explicitly override the safety guard (dangerous).
python ./src/pyhdwallet.py gen --force > out.txt
```
**Options:**
If running normally in your interactive terminal, stdout is a TTY and `--force` does nothing (output looks the same).
- `--off-screen`: Enable off-screen mode (no extra output)
---
**Output:** Success/failure messages for derivation tests.
## Security notes (practical)
## Examples
- `gen` printing the mnemonic is intentionally “debug/test” behavior. Assume stdout can be recorded (scrollback, logging, screen recording, CI logs).
- Prefer `--off-screen` for reduced exposure.
- Prefer `--file` so artifacts go into `.wallet/` and are AES-encrypted via `pyzipper`. [web:102]
- For stronger at-rest security: combine `--pgp-pubkey-file` + `--file` so the ZIP contains only an encrypted `.asc` payload. PGPy encryption style follows `PGPMessage.new(...)` then `pubkey.encrypt(...)`. [web:46]
### 1. Generate and Encrypt a New Wallet
```bash
# Generate with encryption
python ./src/pyhdwallet.py gen --pgp-pubkey-file key.asc --include-source --export-private --chains ethereum solana --addresses 5
# Decrypt the output later with GPG
echo "-----BEGIN PGP MESSAGE-----..." | gpg -d
```
### 2. Recover from Mnemonic with Passphrase
```bash
python ./src/pyhdwallet.py recover --mnemonic "word1 word2 ... word12" --passphrase --chains ethereum --addresses 10 --output json
```
### 3. Fetch and Use PGP Key
```bash
# Fetch key
python ./src/pyhdwallet.py fetchkey "https://example.com/pubkey.asc" --out mykey.asc
# Use in recovery
python ./src/pyhdwallet.py recover --interactive --pgp-pubkey-file mykey.asc --export-private
```
### 4. High-Security Operations with Off-Screen Mode
```bash
# Generate without printing sensitive data
python ./src/pyhdwallet.py gen --off-screen --pgp-pubkey-file key.asc --chains ethereum --addresses 1
# Recover in off-screen mode
python ./src/pyhdwallet.py recover --off-screen --interactive --pgp-pubkey-file key.asc --export-private
```
### 5. Solana-Specific Derivation
```bash
python ./src/pyhdwallet.py gen --chains solana --sol-profile phantom_bip44change --addresses 3
```
## Security
- **Offline operation**: `gen`, `recover`, and `test` commands block network access.
- **No plaintext secrets**: Mnemonics and private keys are never printed unless encrypted or `--unsafe-print` is used.
- **PGP encryption**: Use for secure storage of sensitive data.
- **Off-screen mode**: Use `--off-screen` for paranoid operations—suppresses output, uses temp files with auto-deletion, and zeros memory.
- **Passphrase handling**: Passphrases are not stored; only hints are included.
- **Private key export**: Only export what's needed; treat encrypted payloads as sensitive.
- **File permissions**: Output files are set to owner-only (0o600) for security.
- **Memory zeroing**: In secure mode, sensitive variables are cleared after use.
- **Best practices**:
- Use `--interactive` to avoid command-line history exposure.
- Use `--off-screen` for high-risk operations.
- Verify PGP fingerprints out-of-band.
- Run on trusted, offline machines.
---
## Troubleshooting
- **Missing dependencies**: Run `pip install -r requirements.txt`
- **Network errors in offline modes**: Ensure no internet access; the tool blocks it.
- **Invalid mnemonic**: Check word count and spelling.
- **PGP decryption fails**: Ensure you have the correct private key.
- **Secure mode issues**: Ensure temp files are deleted; check permissions on output files.
- **Version check**: Run `python ./src/pyhdwallet.py --version`
### “Permission denied” running `./src/pyhdwallet.py`
Run via python, or set executable bit:
```bash
python ./src/pyhdwallet.py gen
# or
chmod +x ./src/pyhdwallet.py
./src/pyhdwallet.py gen
```
### “Missing dependency: pyzipper” but pip says installed
You likely installed into a different venv. Use:
```bash
python -m pip install pyzipper
python -c "import pyzipper; print(pyzipper.__version__)"
```
### Unzipping AES ZIP issues
Some `unzip` tools may not support AES-encrypted ZIPs. Use Python + pyzipper to extract if needed:
```bash
python - <<'PY'
import pyzipper
zip_path = ".wallet/your_wallet.zip"
out_dir = "unzip_out"
pw = input("ZIP password: ").encode()
with pyzipper.AESZipFile(zip_path) as zf:
zf.pwd = pw
zf.extractall(out_dir)
print("Extracted to", out_dir)
PY
```
AES ZIP support is the reason `pyzipper` is used. [web:102]
---
## Changelog
- **v1.0.3**: Changed default behavior to always show help without subcommand; added README.md; updated documentation.
- **v1.0.2**: Security patches - added --off-screen, memory zeroing, file permission fixes, auto-deletion in off-screen mode, sanitized errors.
- **v1.0.1**: Renamed to pyhdwallet, added --version flag, updated documentation, excluded _toDelete in .gitignore.
- **v1.0.0**: Initial release with gen, recover, fetchkey, and test commands.
- **v1.0.5**
- `--unsafe-print` removed; `gen` prints mnemonic by default (debug/test behavior).
- `--output` removed; file payload is always JSON (unencrypted) or `.asc` (PGP encrypted). [web:46]
- `--file` is now a boolean flag that writes **only** AES-encrypted ZIP artifacts (no raw output files). [web:102]
- Added: `--wallet-location`, `--zip-password-mode`, `--zip-password-len`, `--show-generated-password`.
- Added: `--force` to override the non-TTY printing safety guard (use only when redirecting/piping).
- **v1.0.3** Documentation and CLI behavior updates.
- **v1.0.2** Added off-screen behaviors and security improvements.
- **v1.0.1** Renamed to pyhdwallet and added version flag.
- **v1.0.0** Initial release.