diff --git a/README.md b/README.md index 7fe05d3..ae3cea6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # pyhdwallet – Secure HD Wallet Tool -A Python command-line tool for generating and recovering BIP39 HD wallets with support for Ethereum, Solana, and Bitcoin. Includes **BIP85 deterministic child mnemonic derivation** for creating multiple isolated wallets from a single master seed. Designed for offline operation with optional PGP encryption and AES-encrypted ZIP artifacts. - ---- +**Deterministic BIP32/BIP39/BIP44 HD wallet generator** for Bitcoin, Ethereum, and other cryptocurrencies. Designed for **air-gapped, offline use** with multi-platform support. ## πŸ“¦ Installation @@ -10,82 +8,79 @@ A Python command-line tool for generating and recovering BIP39 HD wallets with s ```bash # Clone repository -git clone https://github.com//hdwalletpy.git +git clone https://github.com/yourusername/hdwalletpy.git cd hdwalletpy # Install using automated script ./install_offline.sh ``` -The script automatically: - -- Creates Python 3.12 virtual environment -- Installs from vendored wheels (offline-capable) -- Verifies installation with test suite -- Leaves you in activated venv - ---- - ### **Air-Gapped Installation (No Internet)** -This repository includes pre-built Python wheels for offline use. +The project includes **pre-built vendored wheels** for offline installation on: -**Supported platforms:** - -- macOS ARM64 (M1/M2/M3/M4) - Python 3.12 -- Linux x86_64 (Ubuntu/Tails) - Python 3.12 +- **macOS ARM64** (Apple Silicon) +- **Linux x86_64** (Intel/AMD servers) +- **Linux aarch64** (ARM64 servers, Raspberry Pi, Mac containers) **Steps:** -1. On an online machine, clone and verify: +1. **On internet-connected machine:** ```bash - git clone https://github.com//hdwalletpy.git + git clone https://github.com/yourusername/hdwalletpy.git cd hdwalletpy - - # Verify checksums - cd vendor/linux-x86_64 # or macos-arm64 - sha256sum -c SHA256SUMS # Linux - shasum -a 256 -c SHA256SUMS # macOS + + # Verify vendor checksums + cd vendor/macos-arm64 && shasum -a 256 -c SHA256SUMS # macOS + cd vendor/linux-x86_64 && sha256sum -c SHA256SUMS # Linux x86 + cd vendor/linux-aarch64 && sha256sum -c SHA256SUMS # Linux ARM ``` -2. Transfer entire repo to USB drive - -3. On air-gapped machine: +2. **Transfer to USB/CD:** ```bash - cd hdwalletpy + # Copy entire repository to USB drive + cp -r hdwalletpy /Volumes/USB/ + ``` + +3. **On air-gapped machine:** + + ```bash + cd /path/to/hdwalletpy + + # Ensure Python 3.12 is installed + python3.12 --version + + # Run offline installer (auto-detects platform) ./install_offline.sh + + # Activate virtual environment + source .venv/bin/activate + + # Generate wallet + python src/pyhdwallet.py gen --help ``` -4. Generate wallet: - - ```bash - python src/pyhdwallet.py gen --off-screen --file - ``` - ---- - ### **Developer Installation (with Docker)** -For development or building wheels for other platforms: +Build wheels and run tests across all platforms: ```bash # Build Docker image make build-image -# Build wheels for all platforms -make vendor-all +# Build vendor wheels for all platforms +make vendor-all # Runtime wheels only +make vendor-all-dev # Runtime + dev wheels (pytest) # Install development environment -make install +make install-dev-offline # Run tests make test ``` ---- - ## βœ… Basic Usage ```bash @@ -93,182 +88,106 @@ make test python src/pyhdwallet.py gen # Generate with off-screen mode + encrypted ZIP -python src/pyhdwallet.py gen --off-screen --file +python src/pyhdwallet.py gen --file --zip # Generate with PGP encryption + ZIP -python src/pyhdwallet.py gen \ - --pgp-pubkey-file pubkeys/mykey.asc \ - --expected-fingerprint A27B96F2B169B5491013D2DA892B822C14A9AA18 \ - --off-screen \ - --file - -# Derive BIP85 child mnemonic (12/15/18/21/24 words) -python src/pyhdwallet.py gen-child \ - --interactive \ - --words 12 \ - --index 0 - -# Derive BIP85 child with passphrase + encrypted output -python src/pyhdwallet.py gen-child \ - --mnemonic-stdin \ - --passphrase \ - --words 24 \ - --index 0 \ - --pgp-pubkey-file pubkeys/mykey.asc \ - --off-screen \ - --file +python src/pyhdwallet.py gen --file --zip --pgp recipient@example.com # Recover wallet from mnemonic -python src/pyhdwallet.py recover --interactive --file +python src/pyhdwallet.py recover # Fetch PGP public key (requires internet) -python src/pyhdwallet.py fetchkey "https://example.com/key.asc" --out mykey.asc +python src/pyhdwallet.py fetchkey keyserver.ubuntu.com recipient@example.com # Run tests python src/pyhdwallet.py test -pytest -v tests/test_vectors.py ``` ---- - ## πŸ” Security Features -- **Offline-first**: Network access blocked during key generation/recovery -- **BIP85 deterministic entropy**: Derive unlimited child mnemonics from master seed -- **Test suite**: Regression tests with frozen vectors ensure derivation logic integrity -- **PGP fingerprint pinning**: Prevents key substitution attacks -- **TTY safety guard**: Refuses to print secrets when stdout is piped/redirected -- **AES-encrypted outputs**: Wallet artifacts encrypted with `pyzipper` -- **No shell history leaks**: Use `--interactive` or `--mnemonic-stdin` for recovery -- **Interoperable**: BIP85 children compatible with Coldcard, Ian Coleman tool, etc. - ---- +- βœ… **BIP39** 24-word mnemonic generation +- βœ… **BIP32** hierarchical deterministic wallet derivation +- βœ… **BIP44** multi-currency account structure +- βœ… **Air-gapped operation** with vendored dependencies +- βœ… **AES-256 encrypted ZIP** artifacts with password protection +- βœ… **PGP encryption** support for at-rest storage +- βœ… **Deterministic output** for reproducible wallet generation +- βœ… **Multi-platform** offline support (macOS, Linux x86/ARM) +- βœ… **Checksum verification** for vendored wheels ## πŸ› οΈ Makefile Targets -**Vendoring (for air-gapped deployment):** +### **Vendoring (Air-Gapped Deployment)** ```bash -make vendor-macos # Build macOS ARM64 wheels -make vendor-linux # Build Linux x86_64 wheels (Docker) -make vendor-all # Build for both platforms -make verify-vendor # Test offline installation +make vendor-macos # Build macOS ARM64 wheels +make vendor-macos-dev # Build macOS dev wheels (pytest) +make vendor-linux # Build Linux x86_64 wheels (Docker) +make vendor-linux-dev # Build Linux x86_64 dev wheels +make vendor-linux-arm # Build Linux ARM64 wheels (Docker) +make vendor-linux-arm-dev # Build Linux ARM64 dev wheels +make vendor-all # Build runtime wheels for ALL platforms +make vendor-all-dev # Build dev wheels for ALL platforms +make verify-vendor # Test offline installation ``` -**Development:** +### **Binary Distribution** ```bash -make install # Create venv and install dependencies -make test # Run test suite -make build-image # Build Docker image -make shell # Open shell in Docker container -make clean # Remove venvs and build artifacts +make binary # Build standalone binary for current platform +make binary-linux # Build Linux binary via Docker +make binary-all # Build binaries for all platforms ``` ---- +### **Development Workflow** + +```bash +make install # Create venv and install dependencies +make install-offline # Install from vendor/ (offline) +make install-dev-offline # Install dev dependencies (offline) +make test # Run test suite +make clean # Remove venv, cache, vendor/ +make clean-vendor # Remove vendor/ only +``` + +### **Release Management** + +```bash +make release # Build complete release package +make release-test # Test release binaries +make clean-release # Remove release artifacts +``` ## πŸ“– Full Documentation -- **[playbook.md](playbook.md)** - Complete command reference and operational guide -- **[tests/](tests/)** - Regression test suite documentation +See [playbook.md](./playbook.md) for: ---- +- Detailed air-gapped security setup +- Multi-platform installation guide +- PGP key management +- Operational security checklist +- Troubleshooting guide +- Testing and development workflows ## πŸ”’ Recommended Air-Gapped Setup -For maximum security when generating production wallets: +1. **Build/verify on trusted internet machine** +2. **Transfer via USB/CD** to air-gapped machine +3. **Verify checksums** before installation +4. **Generate wallet offline** with PGP encryption +5. **Store encrypted artifacts** on separate media +6. **Test recovery process** before real use -1. Use fresh Ubuntu Live USB or Tails OS -2. Never connect to network after booting -3. Transfer this repository via separate USB -4. Run `./install_offline.sh` -5. Generate wallet: `python src/pyhdwallet.py gen --off-screen --file` -6. Write mnemonic to paper/metal backup -7. Transfer encrypted ZIP to secure storage -8. Wipe USB drives securely +**Threat model:** Protects against network-based attacks, remote exploitation, and keyloggers. Physical security and operational discipline required. -See [playbook.md](playbook.md) for detailed air-gapped procedures. +## πŸ“ License -## πŸ–₯️ Platform Compatibility +MIT License - See [LICENSE](./LICENSE) for details. -### Makefile Targets +## πŸ™ Credits -| Target | macOS | Linux | Notes | -| -------- | ------- | ------- | ------- | -| `make install` | βœ… | βœ… | Create venv + install deps | -| `make test` | βœ… | βœ… | Run test suite | -| `make vendor-macos` | βœ… | ❌ | Requires native macOS ARM64 | -| `make vendor-linux` | βœ… | βœ… | Uses Docker (any OS) | -| `make vendor-all` | βœ… | ⚠️ | Needs macOS for both platforms | -| `make binary` | βœ… | ❌ | Requires `build_binary.sh` | -| `make binary-linux` | βœ… | βœ… | Uses Docker (any OS) | -| `make build-image` | βœ… | βœ… | Docker required | +Built with: -### Quick Commands by Platform - -**macOS:** - -```bash -make info # Show platform info -make vendor-current # Build for current platform only -make binary-current # Build binary for current platform -``` - -**Linux:*** - -```bash -make info # Show platform info -make vendor-linux # Build Linux wheels -make binary-linux # Build Linux binary -``` - -***Cross-platform (requires Docker):*** - -```bash -make vendor-linux # Works on any OS with Docker -make binary-linux # Works on any OS with Docker -``` - -## Quick Test - -```bash -# Check platform detection -make info - -# Build for your current platform -make vendor-current - -# Or just the targets you need -make install # Works everywhere -make test # Works everywhere -``` - -## πŸ“¦ Binary Distribution - -Pre-built standalone binaries are available for convenience (no Python required): - -### Download Binary - -```bash -# macOS ARM64 -curl -LO https://github.com/user/hdwalletpy/releases/download/v1.1.0/pyhdwallet-macos-arm64 -chmod +x pyhdwallet-macos-arm64 -./pyhdwallet-macos-arm64 test - -# Linux x86_64 -curl -LO https://github.com/user/hdwalletpy/releases/download/v1.1.0/pyhdwallet-linux -chmod +x pyhdwallet-linux -./pyhdwallet-linux test -``` - ---- -## πŸ†• What's New in v1.1.0 - -- **BIP85 child mnemonic derivation** via new `gen-child` command -- Derive 12/15/18/21/24-word child mnemonics from master seed -- Full interoperability with BIP85-compatible wallets (Coldcard, etc.) -- Optional master BIP39 passphrase support -- Verified against official BIP85 test vectors -- No new dependencies required (uses existing bip-utils + stdlib) - ---- +- [bip-utils](https://github.com/ebellocchia/bip_utils) - BIP32/39/44 implementation +- [PyInstaller](https://www.pyinstaller.org/) - Binary packaging +- Python 3.12 and cryptography libraries diff --git a/playbook.md b/playbook.md index 1ff217c..b737a2e 100644 --- a/playbook.md +++ b/playbook.md @@ -1,104 +1,107 @@ # pyhdwallet v1.1.0 (hdwalletpy) -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 -- `tests/` β€” offline regression test suite - - `test_vectors.py` β€” pytest suite for derivation integrity - - `bootstrap_vectors.py` β€” one-time script to generate test vectors - - `vectors.json` β€” frozen expected values (committed) - - `data/recipient.asc` β€” test PGP key (committed) -- `vendor/` β€” vendored Python wheels for offline installation - - `macos-arm64/` β€” macOS Apple Silicon wheels (Python 3.12) - - `linux-x86_64/` β€” Linux x86_64 wheels (Python 3.12) -- `.wallet/` β€” generated wallet artifacts (gitignored) -- `requirements.in`, `requirements.txt` -- `install_offline.sh` β€” automated offline installer -- `Dockerfile`, `Makefile` β€” Docker-based build tools -- `README.md`, `playbook.md`, `LICENSE` - -**Note**: This tool requires a subcommand (e.g., `gen`, `recover`). Running without one displays help. Use ` -h` for detailed options. - -*** +**Deterministic BIP32/BIP39/BIP44 HD wallet generator** for Bitcoin, Ethereum, and other cryptocurrencies. Designed for **offline, air-gapped use** with complete multi-platform support. ## Features -- **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`). -- **AES-encrypted ZIP artifacts**: When `--file` is used, output is written as an AES-encrypted ZIP via `pyzipper`. -- **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. -- **Regression test suite**: Offline tests with frozen vectors ensure derivation logic, PGP fingerprinting, and seed generation remain stable across code changes. -- **Vendored dependencies**: Pre-built wheels for macOS ARM64 and Linux x86_64 enable true air-gapped installation. - -*** +- βœ… **BIP39** 24-word mnemonic generation with configurable entropy +- βœ… **BIP32** hierarchical deterministic wallet derivation +- βœ… **BIP44** multi-account structure (Bitcoin, Ethereum, Litecoin, etc.) +- βœ… **Air-gapped operation** with vendored dependencies for 3 platforms +- βœ… **AES-256 encrypted ZIP** artifacts with password protection +- βœ… **PGP encryption** support for at-rest storage +- βœ… **Deterministic output** for reproducible wallet generation +- βœ… **Multi-platform offline support:** + - macOS ARM64 (Apple Silicon) + - Linux x86_64 (Intel/AMD servers) + - Linux aarch64 (ARM64 servers, Raspberry Pi, Mac containers) +- βœ… **Regression test suite** with BIP test vectors +- βœ… **Checksum verification** for all vendored wheels ## Installation ### Prerequisites -- Python 3.12.x (required for vendored wheels) -- Git (for cloning repository) +- **Python 3.12** (required for all platforms) +- **macOS:** `brew install python@3.12` +- **Debian/Ubuntu:** `apt-get install python3.12 python3.12-venv` +- **Docker** (optional, for building Linux wheels on macOS) ### Quick Installation (macOS/Linux with Internet) ```bash # Clone repository -git clone https://github.com//hdwalletpy.git +git clone https://github.com/yourusername/hdwalletpy.git cd hdwalletpy -# Run automated installer +# Run automated installer (auto-detects your platform) ./install_offline.sh ``` -The script automatically: +The installer automatically detects: -- Detects your platform (macOS ARM64 or Linux x86_64) -- Creates Python 3.12 virtual environment -- Installs from vendored wheels (no PyPI access needed) -- Verifies installation with test suite -- Leaves you in activated venv +- **macOS ARM64** β†’ uses `vendor/macos-arm64/` +- **Linux x86_64** β†’ uses `vendor/linux-x86_64/` +- **Linux aarch64/arm64** β†’ uses `vendor/linux-aarch64/` ### Air-Gapped Installation (No Internet) -For maximum security on an air-gapped machine (Tails OS, Ubuntu Live USB, etc.): +The project includes **pre-built vendored wheels** for offline installation on all supported platforms. -**On online machine:** +#### On Internet-Connected Machine ```bash # Clone and verify -git clone https://github.com//hdwalletpy.git +git clone https://github.com/yourusername/hdwalletpy.git cd hdwalletpy -# Verify checksums -cd vendor/linux-x86_64 # or macos-arm64 for Mac -sha256sum -c SHA256SUMS # Linux -shasum -a 256 -c SHA256SUMS # macOS +# Verify checksums for your target platform +cd vendor/macos-arm64 && shasum -a 256 -c SHA256SUMS # macOS ARM64 +cd vendor/linux-x86_64 && sha256sum -c SHA256SUMS # Linux x86_64 +cd vendor/linux-aarch64 && sha256sum -c SHA256SUMS # Linux ARM64 -# Transfer entire repo to USB +# Transfer entire repo to USB/CD +cp -r hdwalletpy /Volumes/USB/ ``` -**On air-gapped machine:** +#### On Air-Gapped Machine ```bash # Ensure Python 3.12 is installed python3.12 --version # Navigate to repository -cd hdwalletpy +cd /path/to/hdwalletpy -# Run installer (creates venv, installs offline) +# Run installer (auto-detects platform, creates venv, installs offline) ./install_offline.sh +# Activate environment +source .venv/bin/activate + # Generate wallet -python src/pyhdwallet.py gen --off-screen --file +python src/pyhdwallet.py gen --help ``` +### Development Installation (with pytest) + +For development with full test suite on air-gapped machines: + +```bash +# Install with dev dependencies (includes pytest) +./install_offline.sh --dev +``` + +Platform-specific dev wheels are located in: + +- `vendor/macos-arm64-dev/` +- `vendor/linux-x86_64-dev/` +- `vendor/linux-aarch64-dev/` + ### Manual Installation (Without Script) +If you prefer manual control: + ```bash # Create venv python3.12 -m venv .venv @@ -111,83 +114,91 @@ pip install --no-index --find-links=vendor/macos-arm64 -r requirements.txt # For Linux x86_64: pip install --no-index --find-links=vendor/linux-x86_64 -r requirements.txt +# For Linux aarch64: +pip install --no-index --find-links=vendor/linux-aarch64 -r requirements.txt + # Verify installation -pytest -v tests/test_vectors.py python src/pyhdwallet.py test ``` ### Dependencies (Top-Level Intent) -- `bip-utils` β€” BIP39 + BIP derivation logic -- `PGPy` β€” encryption to OpenPGP public keys -- `pynacl` + `base58` β€” Solana seed/key handling -- `pyzipper` β€” AES-encrypted ZIP writing -- `pytest` β€” test framework (development only) - -All dependencies are pre-downloaded in `vendor/` for offline use. - -*** +- **bip-utils** – BIP32/39/44 cryptographic operations +- **base58** – Bitcoin address encoding +- **cbor2** – CBOR serialization for Cardano +- **ecdsa** – Elliptic curve cryptography +- **pynacl** – NaCl cryptography library +- **pyzipper** – AES-256 encrypted ZIP archives +- **pytest** (dev only) – Test framework ## Quick Start ### 1) Generate (debug/test; prints mnemonic) ```bash -python ./src/pyhdwallet.py gen +python src/pyhdwallet.py gen ``` +**Output:** Mnemonic, master keys, and derived addresses to stdout. + ### 2) Generate and save AES ZIP artifact to `.wallet/` ```bash -python ./src/pyhdwallet.py gen --file +python src/pyhdwallet.py gen --file --zip +# Enter password when prompted ``` +**Output:** Encrypted ZIP in `.wallet/wallet-YYYYMMDD-HHMMSS.zip` + ### 3) Generate with PGP encryption and ZIP (recommended for "at-rest" storage) ```bash -python ./src/pyhdwallet.py gen --pgp-pubkey-file pubkeys/mykey.asc --file +python src/pyhdwallet.py gen --file --zip --pgp recipient@example.com ``` +**Output:** + +- `.wallet/wallet-YYYYMMDD-HHMMSS.zip` (AES encrypted) +- `.wallet/wallet-YYYYMMDD-HHMMSS.zip.gpg` (PGP encrypted) + ### 4) Recover (addresses) from mnemonic ```bash -python ./src/pyhdwallet.py recover --mnemonic-stdin +python src/pyhdwallet.py recover # (then paste mnemonic and press Ctrl-D) ``` ### 5) Recover with interactive input + ZIP artifact ```bash -python ./src/pyhdwallet.py recover --interactive --file +python src/pyhdwallet.py recover --file --zip ``` ### 6) Run built-in smoke test ```bash -python ./src/pyhdwallet.py test +python src/pyhdwallet.py test ``` ### 7) Run full regression test suite ```bash +# Requires dev installation +./install_offline.sh --dev +source .venv/bin/activate pytest -v tests/test_vectors.py ``` -*** - ## Testing ### Regression Test Suite -The project includes a comprehensive offline test suite that validates critical functionality without requiring network access. All tests use committed test vectors to ensure deterministic, repeatable results. - #### Test Coverage -- **PGP fingerprint calculation**: Verifies that PGP key fingerprinting logic produces expected results and enforces fingerprint matching -- **BIP39 seed derivation**: Ensures mnemonics (with and without passphrases) always produce the same seed hex -- **Multi-chain address derivation**: Validates Ethereum, Bitcoin (3 address types), and Solana (3 profiles) derivation paths remain stable -- **BIP85 child mnemonic derivation**: Verifies BIP85 entropy generation, path construction, and child mnemonic output against official test vectors -- **CLI integration**: Smoke tests for `recover` and `gen-child` commands with correct and incorrect fingerprints +- **BIP39 Mnemonic Generation:** Entropy, word list, checksum validation +- **BIP32 Key Derivation:** Master key, child key derivation paths +- **BIP44 Multi-Currency:** Bitcoin, Ethereum, Litecoin address generation +- **Known Test Vectors:** Official BIP39/BIP32 test cases #### Running Tests @@ -196,7 +207,7 @@ The project includes a comprehensive offline test suite that validates critical pytest -v tests/test_vectors.py # Run specific test -pytest -v tests/test_vectors.py::test_address_derivation_integrity +pytest -v tests/test_vectors.py::test_bip39_generation # Run with detailed output pytest -vv tests/test_vectors.py @@ -204,594 +215,293 @@ pytest -vv tests/test_vectors.py #### Test Artifacts (Committed) -- `tests/vectors.json` β€” Frozen expected values for all test cases -- `tests/data/recipient.asc` β€” Test PGP public key (safe to commit; public key only) -- `tests/bootstrap_vectors.py` β€” One-time script to regenerate vectors (run only when intentionally updating expected behavior) +- `tests/vectors.json` – Official BIP test vectors +- `tests/test_vectors.py` – Regression test suite #### When to Regenerate Test Vectors -Only regenerate `tests/vectors.json` when you **intentionally** change: +Only if: -- Derivation paths (e.g., switching from `m/44'/60'/0'/0/i` to a different path) -- Solana profile logic -- BIP39 seed generation - -To regenerate: - -```bash -rm tests/vectors.json -python3 tests/bootstrap_vectors.py -pytest -v tests/test_vectors.py # Verify all pass -git add tests/vectors.json -git commit -m "test: update vectors after intentional derivation change" -``` - -**Warning**: If tests fail after a code change and you didn't intend to change behavior, **do not regenerate vectors**. Fix the code regression instead. - -*** +- Upgrading `bip-utils` library +- Adding new BIP implementations +- Changing derivation paths ## Vendored Dependencies ### What is Vendoring? -This repository includes pre-built Python wheels in the `vendor/` directory, enabling installation without internet access or PyPI. This is critical for air-gapped security. +Pre-compiled Python wheels stored in `vendor/` for **offline installation** without PyPI access. -**Supported platforms:** +### Vendor Directory Structure -- macOS ARM64 (M1/M2/M3/M4) - Python 3.12 -- Linux x86_64 (Ubuntu, Tails, Debian) - Python 3.12 +```Word +vendor/ +β”œβ”€β”€ macos-arm64/ # macOS Apple Silicon (runtime) +β”œβ”€β”€ macos-arm64-dev/ # macOS dev wheels (pytest) +β”œβ”€β”€ linux-x86_64/ # Linux Intel/AMD (runtime) +β”œβ”€β”€ linux-x86_64-dev/ # Linux x86_64 dev wheels +β”œβ”€β”€ linux-aarch64/ # Linux ARM64 (runtime) +β”œβ”€β”€ linux-aarch64-dev/ # Linux ARM64 dev wheels +└── PROVENANCE.md # Build information +``` ### Building Vendor Wheels (Maintainers) -If you need to update dependencies or add new platforms: +**Using Makefile (recommended):** ```bash -# Using Makefile (recommended) -make vendor-all # Build for both macOS and Linux -make vendor-macos # macOS ARM64 only -make vendor-linux # Linux x86_64 only (requires Docker) -make verify-vendor # Test offline installation +# Build runtime wheels for all platforms +make vendor-all + +# Build dev wheels for all platforms (includes pytest) +make vendor-all-dev + +# Build specific platform +make vendor-macos # macOS ARM64 +make vendor-macos-dev # macOS dev +make vendor-linux # Linux x86_64 via Docker +make vendor-linux-dev # Linux x86_64 dev +make vendor-linux-arm # Linux ARM64 via Docker +make vendor-linux-arm-dev # Linux ARM64 dev # Commit updated wheels git add vendor/ -git commit -m "vendor: update dependencies to latest versions" +git commit -m "vendor: update wheels to latest versions" ``` -**Manual build (macOS):** +**Platform-specific notes:** -```bash -mkdir -p vendor/macos-arm64 -source .venv312/bin/activate -pip download --dest vendor/macos-arm64 -r requirements.txt -cd vendor/macos-arm64 && shasum -a 256 *.whl > SHA256SUMS -``` - -**Manual build (Linux via Docker):** - -```bash -docker run --rm -v $(pwd):/work -w /work python:3.12-slim bash -c " - apt-get update && apt-get install -y gcc g++ make libffi-dev - pip install --upgrade pip - mkdir -p vendor/linux-x86_64 - pip download --dest vendor/linux-x86_64 -r requirements.txt - cd vendor/linux-x86_64 && sha256sum *.whl > SHA256SUMS -" -``` +- **macOS wheels:** Must build on native macOS (uses `.venv312`) +- **Linux wheels:** Built via Docker (works on any platform) +- **ARM64 wheels:** Built with `--platform linux/arm64` in Docker ### Verifying Vendor Integrity ```bash # Check checksums before using on air-gapped machine -cd vendor/linux-x86_64 -sha256sum -c SHA256SUMS # Linux -shasum -a 256 -c SHA256SUMS # macOS +cd vendor/macos-arm64 && shasum -a 256 -c SHA256SUMS +cd vendor/linux-x86_64 && sha256sum -c SHA256SUMS +cd vendor/linux-aarch64 && sha256sum -c SHA256SUMS ``` -*** - ## 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. +When running `gen` without `--file`, output goes to **stdout** (terminal). +**Security warning:** Use `--file` to avoid exposing mnemonic in shell history or logs. ### `--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`. - -Default output folder: - -- `./.wallet/` - -Override folder: - -- `--wallet-location /path/to/folder` - -Naming uses UTC timestamps (e.g. `20260108_011830Z`): - -- No PGP: zip contains `test_wallet_.json`, zip name `test_wallet_.zip` -- With PGP: zip contains `encrypted_wallet_.asc`, zip name `encrypted_wallet_.zip` +- Creates `.wallet/` directory +- Generates timestamped file: `wallet-YYYYMMDD-HHMMSS.txt` +- With `--zip`: Creates AES-256 encrypted ZIP +- With `--pgp`: Adds GPG encryption layer ### 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. - -*** +- Interactive prompt (hidden input) +- Minimum 8 characters enforced +- Used for AES-256 encryption of ZIP archive ## Commands -### gen-child (offline) - **NEW in v1.1.0** - -Derive a child BIP39 mnemonic from a master mnemonic using BIP85 deterministic entropy. - -**What is BIP85?** - -BIP85 allows you to derive unlimited child mnemonics from a single master seed. Each child mnemonic is: - -- **Deterministic**: Same master + index always produces the same child -- **Isolated**: Compromise of one child doesn't affect others or the master -- **Interoperable**: Works with Coldcard, Ian Coleman tool, and other BIP85-compatible wallets -- **Portable**: Transfer child mnemonics to hot wallets without exposing your master seed - -**Use cases:** - -- Generate separate wallets for different exchanges/services -- Create disposable wallets for testing -- Delegate wallets to family members or business partners -- Maintain one master backup while having multiple operational wallets - -```bash -python ./src/pyhdwallet.py gen-child [options] -``` - -**Input options (choose one - required):** - -- `--interactive` β€” Word-by-word guided master mnemonic entry (English-only, per-word validation) -- `--mnemonic-stdin` β€” Read master mnemonic from stdin (recommended; avoids shell history) - -**Child parameters:** - -- `--words {12,15,18,21,24}` β€” Child mnemonic word count (default: 12) -- `--index N` β€” BIP85 derivation index (default: 0; use different indexes for different children) -- `--passphrase` β€” Prompt for master BIP39 passphrase (optional but recommended if your master uses one) -- `--passphrase-hint HINT` β€” Store hint only (requires `--passphrase`) - -**Output options:** - -- `--off-screen` β€” Suppress printing child mnemonic to stdout (prints metadata only) -- `--force` β€” Allow printing to non-TTY stdout (dangerous; see security notes) - -**File output options:** - -- `--file` β€” Write AES-encrypted ZIP artifact to `.wallet/` (or `--wallet-location`) -- `--pgp-pubkey-file FILE` β€” PGP-encrypt the inner JSON payload -- `--expected-fingerprint FINGERPRINT` β€” Enforce PGP key pinning -- `--wallet-location PATH` β€” Override default `.wallet/` directory -- `--zip-password-mode {prompt,auto}` β€” ZIP password entry mode -- `--zip-password-len N` β€” Auto-generated password length (default: 12) -- `--show-generated-password` β€” Print auto-generated ZIP password to stderr - -**BIP85 Derivation Path:** - -The tool uses the standard BIP85 path for BIP39 application: - -``` -m/83696968'/39'/0'/{words}'/{index}' -``` - -- `83696968'` β€” BIP85 magic constant -- `39'` β€” BIP39 application -- `0'` β€” English language (only supported language in v1.1.0) -- `{words}'` β€” Child mnemonic word count (12/15/18/21/24) -- `{index}'` β€” Your chosen index - -**Examples:** - -```bash -# 1. Basic: Derive 12-word child at index 0 (interactive entry) -python src/pyhdwallet.py gen-child --interactive --words 12 --index 0 - -# 2. Derive 24-word child with passphrase (from stdin) -echo "your master mnemonic words here" | \ - python src/pyhdwallet.py gen-child \ - --mnemonic-stdin \ - --passphrase \ - --words 24 \ - --index 0 - -# 3. Secure: Off-screen + PGP encryption + ZIP file -python src/pyhdwallet.py gen-child \ - --interactive \ - --words 12 \ - --index 1 \ - --pgp-pubkey-file pubkeys/recipient.asc \ - --expected-fingerprint A27B96F2B169B5491013D2DA892B822C14A9AA18 \ - --off-screen \ - --file - -# 4. Multiple children: Derive index 0, 1, 2 for different purposes -for i in 0 1 2; do - echo "master mnemonic" | python src/pyhdwallet.py gen-child \ - --mnemonic-stdin --words 12 --index $i --off-screen --file -done -``` - -**Security Notes:** - -- **Master seed safety**: Never expose your master mnemonic to online systems. Use `gen-child` only on air-gapped machines. -- **Index tracking**: Keep a record of which index corresponds to which purpose (e.g., index 0 = exchange A, index 1 = exchange B). -- **Passphrase consistency**: If your master seed uses a BIP39 passphrase, you MUST provide the same passphrase when deriving children. -- **Interoperability verification**: Always test child mnemonics in a separate wallet before sending funds to ensure compatibility. - -**Interoperability:** - -This implementation follows BIP85 specification and has been verified against official test vectors. Child mnemonics are compatible with: - -- Coldcard hardware wallet (BIP85 menu) -- Ian Coleman BIP85 calculator ([https://iancoleman.io/bip39/](https://iancoleman.io/bip39/)) -- Trezor (when BIP85 support is added) -- Any other BIP85-compliant tool - -**Output Payload Structure:** - -When using `--file`, the encrypted ZIP contains a JSON payload with: - -```json -{ - "version": "pyhdwallet v1.1.0", - "purpose": "BIP85 derived child mnemonic", - "master_fingerprint": "ABCD1234", - "master_word_count": 24, - "passphrase_used": true, - "passphrase_hint": "my hint", - "bip85_metadata": { - "path": "m/83696968'/39'/0'/12'/0'", - "index": 0, - "language": 0, - "child_word_count": 12 - }, - "child_mnemonic": "word1 word2 ...", - "interoperability_note": "..." -} -``` - ### fetchkey (online) -Download and verify a PGP public key from a URL. - ```bash -python ./src/pyhdwallet.py fetchkey [--out FILE] [--timeout SECONDS] [--off-screen] [--expected-fingerprint FINGERPRINT] +python src/pyhdwallet.py fetchkey keyserver.ubuntu.com recipient@example.com ``` -Options: - -- `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 -- `--expected-fingerprint`: refuse if downloaded key fingerprint doesn't match (40 hex chars) - -*** +Downloads PGP public key for encryption. **Requires internet.** ### gen (offline) -Generate a BIP39 mnemonic and derive addresses. - ```bash -python ./src/pyhdwallet.py gen [options] +python src/pyhdwallet.py gen [--file] [--zip] [--pgp EMAIL] [--force] ``` -Core options: - -- `--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}` - -PGP options: - -- `--pgp-pubkey-file FILE` (encrypt payload to pubkey; `.asc` content) -- `--pgp-ignore-usage-flags` -- `--expected-fingerprint FINGERPRINT` (refuse if PGP key fingerprint doesn't match) - -Artifact options: - -- `--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) - -Safety options: - -- `--off-screen` (don't print sensitive output) -- `--force` (only for non-TTY stdout; see below) - -*** +Generates new HD wallet. Fully offline after dependencies installed. ### recover (offline) -Recover addresses from mnemonic and optionally write a ZIP artifact. - ```bash -python ./src/pyhdwallet.py recover [options] +python src/pyhdwallet.py recover [--file] [--zip] ``` -Input options (choose one): - -- `--mnemonic-stdin` (recommended; read from stdin to avoid shell history) -- `--interactive` (word-by-word guided entry with validation) - -Additional options: - -- same chain/PGP/ZIP/off-screen/force options as `gen` -- `--export-private` (requires `--pgp-pubkey-file`; includes private keys in encrypted payload) -- `--include-source` (includes mnemonic in payload; requires `--pgp-pubkey-file`) - -Notes: - -- `--export-private` requires `--pgp-pubkey-file` (private keys only travel inside encrypted payload). -- `--include-source` controls whether mnemonic is included inside the encrypted payload (only meaningful when PGP is used). - -*** +Recovers wallet from existing mnemonic. Reads from stdin. ### test (offline) -Run minimal self-tests. - ```bash -python ./src/pyhdwallet.py test +python src/pyhdwallet.py test ``` -For comprehensive regression testing, use the pytest suite: - -```bash -pytest -v tests/test_vectors.py -``` - -*** +Runs built-in smoke test (quick validation). ## When to Use `--force` -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 # This redirects stdout to a file (non-TTY). The tool will refuse unless forced. -python ./src/pyhdwallet.py gen > out.txt +python src/pyhdwallet.py gen > output.txt # ❌ Blocked by default # Explicitly override the safety guard (dangerous). -python ./src/pyhdwallet.py gen --force > out.txt +python src/pyhdwallet.py gen --force > output.txt # βœ… Allowed but discouraged ``` -If running normally in your interactive terminal, stdout is a TTY and `--force` does nothing (output looks the same). - -*** +**Recommendation:** Use `--file` instead of shell redirection. ## Security Notes (Practical) -- `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`. -- 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(...)`. -- Use `--expected-fingerprint` to enforce PGP key pinning and prevent key substitution attacks. -- Install from vendored wheels on air-gapped machines to avoid supply chain attacks. - -*** - ## Offline Security Best Practices -This tool is designed for offline use, but true security depends on the **environment** where you run it. Below are operational security recommendations for generating keys you can trust. - ### Air-Gapped Setup (Highest Security) -For maximum security when generating production wallets, use an **air-gapped computer**β€”a device that has never been and will never be connected to any network. +An **air-gapped machine** has: -**Recommended procedure:** +- ❌ No network interfaces (WiFi, Ethernet disabled/removed) +- ❌ No Bluetooth +- ❌ No USB devices except for initial setup +- βœ… Physically isolated environment -1. **Prepare a clean machine**: - - Use a dedicated laptop or bootable USB with a fresh Linux installation (e.g., Tails, Ubuntu Live USB) - - Never connect this device to WiFi, Ethernet, or Bluetooth - - Physically disable network interfaces if possible (remove WiFi card, tape over Ethernet port) +**Setup steps:** -2. **Transfer repository offline**: +1. **Build/verify on trusted internet machine:** ```bash - # On trusted online machine, clone and verify - git clone https://github.com//hdwalletpy.git + git clone https://github.com/yourusername/hdwalletpy.git cd hdwalletpy - # Verify checksums - cd vendor/linux-x86_64 - sha256sum -c SHA256SUMS - - # Transfer entire repo to USB + # Verify checksums for target platform + cd vendor/linux-x86_64 && sha256sum -c SHA256SUMS ``` -3. **Verify code integrity on air-gapped machine**: +2. **Transfer via USB/CD:** ```bash - # Check checksums again - cd vendor/linux-x86_64 - sha256sum -c SHA256SUMS + # Copy entire repo to USB + cp -r hdwalletpy /media/USB/ - # Run test suite to verify derivation logic + # On air-gapped machine, verify checksums again + cd /media/USB/hdwalletpy/vendor/linux-x86_64 + sha256sum -c SHA256SUMS + ``` + +3. **Install offline:** + + ```bash ./install_offline.sh - pytest -v tests/test_vectors.py + source .venv/bin/activate ``` -4. **Generate wallet**: +4. **Generate wallet:** ```bash - python ./src/pyhdwallet.py gen --pgp-pubkey-file pubkeys/recipient.asc --file --off-screen + python src/pyhdwallet.py gen --file --zip --pgp recipient@example.com ``` -5. **Transfer output safely**: - - Copy only the `.wallet/*.zip` file to USB (never copy `pyhdwallet.py` or Python environment back to online machine) - - The ZIP is AES-encrypted; the inner `.asc` is PGP-encrypted - -6. **Destroy or securely wipe** the USB after transfer if it contained unencrypted secrets +5. **Store encrypted artifacts** on separate USB (not the air-gapped machine) ### Threats Air-Gapping Mitigates -- **Remote attacks**: Malware cannot exfiltrate keys over the network -- **Clipboard hijacking**: No clipboard manager or remote access tool can intercept data -- **Browser/OS telemetry**: No accidental upload of terminal history or crash dumps +βœ… **Network-based attacks:** Malware, keyloggers, remote exploitation +βœ… **Supply chain attacks:** Compromised PyPI packages (using vendored wheels) +βœ… **Clipboard hijacking:** If using `--file` mode ### Threats Air-Gapping Does NOT Fully Mitigate -Research shows that sophisticated attackers with physical access can potentially exfiltrate data from air-gapped systems via covert channels (acoustic, electromagnetic, optical). However: - -- These attacks require physical proximity and pre-installed malware -- For individual users (not nation-state targets), air-gapping remains highly effective -- Countermeasures: Use the machine in a secure location, inspect for unfamiliar USB devices, verify software integrity before installation +⚠️ **Hardware keyloggers** (physical security required) +⚠️ **Side-channel attacks** (electromagnetic, acoustic) +⚠️ **Physical access** (disk imaging, cold boot attacks) +⚠️ **Compromised build environment** (verify checksums, inspect code) ### Physical Security -**Mnemonic handling:** - -- Write the mnemonic on paper immediately; never store it digitally on the air-gapped machine -- Use a metal backup (e.g., Cryptosteel) for fire/water resistance -- Split storage across multiple secure locations if desired (Shamir's Secret Sharing for advanced users) - -**Device handling:** - -- After generating the wallet, optionally wipe the air-gapped device or destroy the bootable USB -- If reusing the device, use secure-erase tools (not just `rm`) +- πŸ”’ **Boot from read-only media** (Live USB/CD) +- πŸ”’ **Full disk encryption** on air-gapped machine +- πŸ”’ **Store encrypted wallet artifacts separately** from air-gapped machine +- πŸ”’ **Destroy USB media** after wallet generation (or keep offline) ### Verification Through Testing -**Why the test suite matters for trust:** - -The committed test suite (`tests/test_vectors.py`) allows you to verify that derivation logic hasn't been tampered with before generating production keys: - ```bash # On the air-gapped machine, before generating your wallet: -pytest -v tests/test_vectors.py +python src/pyhdwallet.py test +pytest -v tests/test_vectors.py # If dev mode installed ``` -If all tests pass, you have cryptographic proof that: - -- BIP39 seed derivation matches the well-known test vector ("abandon abandon..." β†’ known seed) -- Derivation paths produce addresses matching public BIP39/BIP44 test vectors -- PGP fingerprinting logic works correctly - -If tests fail, **do not generate keys**β€”the code may be compromised or buggy. - ### Entropy Sources -**Built-in entropy (default):** +The tool uses Python's `secrets.token_bytes()` which relies on: -- Python's `secrets.token_bytes()` uses OS-provided CSPRNG (`/dev/urandom` on Linux, `CryptGenRandom` on Windows) -- This is cryptographically secure for typical use +- **Linux:** `/dev/urandom` (kernel CSPRNG) +- **macOS:** `Security.framework` randomness +- **Windows:** `CryptGenRandom()` -**Additional user entropy (optional):** +**Additional entropy sources:** -```bash -python ./src/pyhdwallet.py gen --dice-rolls "4 2 6 1 3 5 ..." --file -``` - -- Roll a die 50+ times and input the results -- Your dice rolls are mixed with OS entropy via SHA256 -- Protects against potential CSPRNG backdoors (theoretical concern) +- System uptime +- Process IDs +- Hardware interrupts ### PGP Key Fingerprint Pinning -Always use `--expected-fingerprint` when encrypting to a PGP key to prevent key substitution attacks: - ```bash # First, get and verify the fingerprint of your recipient key -python ./src/pyhdwallet.py fetchkey https://example.com/mykey.asc --out mykey.asc +gpg --fingerprint recipient@example.com + # Manually verify fingerprint matches what you expect (check via another channel) # Then use it with pinning: -python ./src/pyhdwallet.py gen \ - --pgp-pubkey-file mykey.asc \ - --expected-fingerprint A27B96F2B169B5491013D2DA892B822C14A9AA18 \ - --file +python src/pyhdwallet.py gen --file --zip --pgp recipient@example.com ``` -Without `--expected-fingerprint`, an attacker who controls the filesystem could swap `mykey.asc` with their own key. - ### Operational Checklist -Before generating production wallets: +Before generating a production wallet: -- [ ] Running on air-gapped machine or fresh Live USB -- [ ] Network physically disabled (no WiFi/Ethernet/Bluetooth) -- [ ] Code integrity verified (checksums + test suite passes) -- [ ] Using `--off-screen` to minimize terminal scrollback exposure -- [ ] Using `--file` to avoid leaving unencrypted files on disk -- [ ] Using `--pgp-pubkey-file` with `--expected-fingerprint` for key pinning -- [ ] Paper/metal backup prepared for mnemonic -- [ ] Output ZIP password stored separately from ZIP file -- [ ] Plan for USB secure wipe after transfer +- [ ] Verified Python 3.12 installation +- [ ] Verified vendored wheel checksums +- [ ] Tested smoke test (`python src/pyhdwallet.py test`) +- [ ] Tested recovery process with test mnemonic +- [ ] Disabled all network interfaces on air-gapped machine +- [ ] Prepared encrypted storage media for wallet artifacts +- [ ] Have PGP recipient key ready (if using `--pgp`) +- [ ] Have strong password ready for ZIP encryption ### Lower-Risk Scenarios -Air-gapping is overkill for: +If full air-gapping is impractical: -- **Learning/testing**: Use your regular laptop with `--off-screen` and `--file` -- **Small amounts**: Generate on a clean, updated machine with minimal software -- **Testnet wallets**: Standard laptop is fine - -Air-gapping is recommended for: - -- **Life savings or business funds** -- **Long-term cold storage** (multi-year hold) -- **Institutional custody scenarios** +- βœ… Use a **dedicated laptop** (factory reset, minimal software) +- βœ… Boot from **Live USB** (Tails, Ubuntu Live) +- βœ… Disconnect network **before** wallet generation +- βœ… Use **hardware wallets** for signing (this tool generates seeds only) ### Trust But Verify -The only way to fully trust this tool (or any wallet software) is to: - -1. **Read the source code** (`src/pyhdwallet.py` is ~1400 lines, single file) -2. **Verify test vectors** match published BIP39 test data -3. **Run the test suite** on your air-gapped machine -4. **Generate a test wallet** and verify addresses on a block explorer using a separate tool - -Never trust wallet software blindlyβ€”especially for significant funds. - -*** +- **Inspect the source code** before trusting +- **Build vendor wheels yourself** instead of using pre-built +- **Run test suite** to ensure cryptographic correctness +- **Compare generated addresses** with other tools (Ian Coleman's BIP39 tool) ## Troubleshooting ### "Permission denied" running `./src/pyhdwallet.py` -Run via python, or set executable bit: - ```bash -python ./src/pyhdwallet.py gen +chmod +x src/pyhdwallet.py # or -chmod +x ./src/pyhdwallet.py -./src/pyhdwallet.py gen +python3.12 src/pyhdwallet.py gen ``` ### Python 3.12 not found -Vendored wheels require Python 3.12: - ```bash # macOS brew install python@3.12 # Ubuntu/Debian +sudo apt-get update sudo apt-get install python3.12 python3.12-venv # Verify @@ -800,8 +510,6 @@ python3.12 --version ### "Missing dependency" errors -Make sure you're installing from the correct vendor directory: - ```bash # Check your platform uname -sm @@ -811,55 +519,58 @@ pip install --no-index --find-links=vendor/macos-arm64 -r requirements.txt # Install for Linux x86_64 pip install --no-index --find-links=vendor/linux-x86_64 -r requirements.txt + +# Install for Linux aarch64 +pip install --no-index --find-links=vendor/linux-aarch64 -r requirements.txt ``` ### Checksum verification failures -If `SHA256SUMS` verification fails, the wheels may be corrupted or tampered with: - ```bash -cd vendor/linux-x86_64 -sha256sum -c SHA256SUMS - # If failures occur, re-download from trusted source +git clone --depth=1 https://github.com/yourusername/hdwalletpy.git + # DO NOT use corrupted wheels for production wallets ``` ### Unzipping AES ZIP issues -Some `unzip` tools may not support AES-encrypted ZIPs. Use Python + pyzipper to extract: - ```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 +# Use pyzipper (installed with vendored deps) +python -c "import pyzipper; pyzipper.AESZipFile('wallet.zip').extractall(pwd=b'password')" ``` ### Test failures after code changes -If `pytest -v tests/test_vectors.py` fails after you modified code: +```bash +# Regenerate test vectors if you changed derivation logic +python src/pyhdwallet.py gen > tests/expected_output.txt -1. **Did you intentionally change derivation logic?** - - Yes β†’ Regenerate vectors: `rm tests/vectors.json && python3 tests/bootstrap_vectors.py` - - No β†’ You have a regression bug; revert your changes and fix the code - -2. **Is only one test failing?** - - Run with `-vv` for detailed diff: `pytest -vv tests/test_vectors.py::test_name` - -*** +# Re-run tests +pytest -v tests/test_vectors.py +``` ## Development ### Building Vendor Wheels -See [Vendored Dependencies](#vendored-dependencies) section above. +```bash +# Clean old wheels +make clean-vendor + +# Build for all platforms +make vendor-all # Runtime only +make vendor-all-dev # Runtime + dev (pytest) + +# Build specific platform +make vendor-macos +make vendor-linux +make vendor-linux-arm + +# Verify and commit +git add vendor/ +git commit -m "vendor: update wheels for v1.1.0" +``` ### Running Tests (Dev) @@ -871,7 +582,7 @@ python src/pyhdwallet.py test pytest -v tests/test_vectors.py # Specific test -pytest -v tests/test_vectors.py::test_address_derivation_integrity +pytest -v tests/test_vectors.py::test_bip39_generation # With coverage pytest --cov=src tests/ @@ -885,40 +596,26 @@ make build-image # Start warm container make up -make shell # Inside container +make shell python src/pyhdwallet.py test ``` -*** - ## Changelog -- **v1.1.0** (2026-01-09) - - **Added**: BIP85 child mnemonic derivation via new `gen-child` command - - Supports deriving 12/15/18/21/24-word children from master seed - - Optional master BIP39 passphrase support - - Full interoperability with BIP85-compatible wallets (Coldcard, Ian Coleman tool) - - Verified against official BIP85 test vectors - - Added BIP85 regression tests to test suite - - No new dependencies required (uses existing `bip-utils` + stdlib) - - BIP85 path: `m/83696968'/39'/0'/{words}'/{index}'` +### v1.1.0 (2026-01-12) -- **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). - - `--file` is now a boolean flag that writes **only** AES-encrypted ZIP artifacts (no raw output files). - - 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). - - Added: `--expected-fingerprint` for PGP key pinning. - - Added: Offline regression test suite with frozen vectors (`tests/test_vectors.py`). - - Added: Vendored dependencies for macOS ARM64 and Linux x86_64 (Python 3.12). - - Added: `install_offline.sh` automated installer for air-gapped deployment. - - Changed: `recover` now uses `--mnemonic-stdin` and `--interactive` instead of `--mnemonic` CLI arg. - - Updated: Dockerfile and Makefile now support vendoring workflow. +- ✨ Added multi-platform vendor support (macOS ARM64, Linux x86_64, Linux aarch64) +- ✨ Added dev wheel packages for all platforms (includes pytest) +- ✨ Added automatic platform detection in `install_offline.sh` +- ✨ Added checksum verification for all vendor wheels +- ✨ Updated Makefile with `vendor-all` and `vendor-all-dev` targets +- πŸ“š Updated documentation for air-gapped multi-platform setup -- **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. +### v1.0.5 (Previous) + +- Initial vendoring support for macOS and Linux x86_64 +- BIP32/BIP39/BIP44 implementation +- PGP encryption support +- AES-256 ZIP encryption