Release v1.0.1: Add --version flag, update documentation to full README, exclude _toDelete in .gitignore, rename program to pyhdwallet
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,6 +8,7 @@ __pycache__/
|
||||
logs/
|
||||
*.log
|
||||
coverage/
|
||||
_toDelete/
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
|
||||
316
playbook.md
316
playbook.md
@@ -1,174 +1,248 @@
|
||||
Below is a practical playbook you can save as `PLAYBOOK.md` next to `hdwallet_recovery.py`.
|
||||
# pyhdwallet v1.0.1
|
||||
|
||||
## Purpose (what this tool does)
|
||||
- **Derive addresses** (ETH/SOL/BTC) from either a BIP39 mnemonic (+ optional passphrase) or a raw 64‑byte BIP39 seed hex.
|
||||
- Optionally **encrypt a payload** to a PGP public key (ASCII armored) so secrets are not shown in plaintext on screen.
|
||||
- `fetchkey` mode is the only mode that touches the network (downloads a PGP public key).
|
||||
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.
|
||||
|
||||
## Prerequisites (software + packages)
|
||||
## Table of Contents
|
||||
|
||||
### OS / Python
|
||||
- Use **Python 3.12** (recommended for compatibility with `bip_utils`, `PGPy`, etc.).
|
||||
- macOS: install Python 3.12 via Homebrew and create a clean venv.
|
||||
- [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)
|
||||
|
||||
### Python packages
|
||||
Install into a venv:
|
||||
## Features
|
||||
|
||||
**Base (derive addresses + encrypt payload):**
|
||||
- `bip-utils`
|
||||
- `PGPy`
|
||||
- **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.
|
||||
|
||||
**Only needed if you use `--export-private` and include `solana`:**
|
||||
- `PyNaCl`
|
||||
- `base58`
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Python 3.11 or higher
|
||||
- Virtual environment (recommended)
|
||||
|
||||
### Setup
|
||||
|
||||
1. Clone or download the repository.
|
||||
2. Create a virtual environment:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
||||
```
|
||||
|
||||
3. Install dependencies:
|
||||
|
||||
```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:
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py gen --chains ethereum solana --addresses 3
|
||||
```
|
||||
|
||||
2. Recover from a mnemonic:
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py recover --mnemonic "abandon abandon ... about" --chains bitcoin
|
||||
```
|
||||
|
||||
3. Fetch a PGP key:
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py fetchkey "https://example.com/key.asc" --out mykey.asc
|
||||
```
|
||||
|
||||
4. Run tests:
|
||||
|
||||
```bash
|
||||
python ./src/pyhdwallet.py test
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
### fetchkey
|
||||
|
||||
Download and verify a PGP public key from a URL.
|
||||
|
||||
**Usage:**
|
||||
|
||||
Example:
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -U pip
|
||||
pip install bip-utils PGPy
|
||||
pip install PyNaCl base58 # only if exporting Solana private keys
|
||||
python ./src/pyhdwallet.py fetchkey <url> [--out FILE] [--timeout SECONDS]
|
||||
```
|
||||
|
||||
## Files you need
|
||||
- `hdwallet_recovery.py` (the script)
|
||||
- `kccleoc.asc` (or any `*.asc`) = ASCII-armored **PGP public key** used to encrypt the payload
|
||||
**Options:**
|
||||
|
||||
## Operating modes
|
||||
- `url`: URL to the ASCII-armored PGP key
|
||||
- `--out FILE`: Save the key to a file
|
||||
- `--timeout SECONDS`: Request timeout (default: 15)
|
||||
|
||||
### Mode A — fetchkey (online, no secrets allowed)
|
||||
Use this to download a PGP public key from a URL and verify it.
|
||||
**Example:**
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
python hdwallet_recovery.py fetchkey "https://github.com/<user>.gpg" --out key.asc
|
||||
python ./src/pyhdwallet.py fetchkey "https://keys.openpgp.org/pks/lookup?op=get&search=user@example.com" --out key.asc
|
||||
```
|
||||
|
||||
**What to check:**
|
||||
- The script prints **SHA256** of the downloaded key and the **PGP fingerprint**.
|
||||
- Independently verify the fingerprint matches the intended owner (don’t trust only the URL).
|
||||
### gen
|
||||
|
||||
**Safety rule:**
|
||||
- Never pass mnemonic/seed/passphrase flags together with `fetchkey`. The script should refuse.
|
||||
Generate a new BIP39 mnemonic and derive addresses.
|
||||
|
||||
***
|
||||
**Usage:**
|
||||
|
||||
### Mode B — derive (offline, addresses only)
|
||||
Derives addresses and prints them to stdout.
|
||||
|
||||
**Command (addresses only):**
|
||||
```bash
|
||||
python hdwallet_recovery.py \
|
||||
--mnemonic '... your words ...' \
|
||||
--passphrase '' \
|
||||
--chains ethereum solana bitcoin \
|
||||
--addresses 10
|
||||
python ./src/pyhdwallet.py gen [options]
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- This prints addresses (safe) but still requires you to supply the mnemonic (sensitive) on the command line unless you use `--interactive`.
|
||||
**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 PASSPHRASE`: BIP39 passphrase
|
||||
- `--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
|
||||
|
||||
**Examples:**
|
||||
|
||||
**Preferred (avoid shell history):**
|
||||
```bash
|
||||
python hdwallet_recovery.py --interactive --chains ethereum solana bitcoin --addresses 10
|
||||
# Basic generation
|
||||
python ./src/pyhdwallet.py gen
|
||||
|
||||
# With passphrase and encryption
|
||||
python ./src/pyhdwallet.py gen --passphrase "mysecret" --pgp-pubkey-file key.asc --export-private
|
||||
|
||||
# JSON output to file
|
||||
python ./src/pyhdwallet.py gen --chains ethereum --addresses 10 --output json --file wallet.json
|
||||
```
|
||||
|
||||
***
|
||||
### recover
|
||||
|
||||
### Mode C — derive + PGP encrypt payload (recommended)
|
||||
Derives addresses, prints addresses, and prints an **encrypted PGP block** containing recovery material.
|
||||
Derive addresses from an existing mnemonic or seed.
|
||||
|
||||
**Usage:**
|
||||
|
||||
**Command (include mnemonic + passphrase):**
|
||||
```bash
|
||||
python hdwallet_recovery.py \
|
||||
--interactive \
|
||||
--chains ethereum solana bitcoin \
|
||||
--addresses 10 \
|
||||
--pgp-pubkey-file key.asc
|
||||
python ./src/pyhdwallet.py recover [options]
|
||||
```
|
||||
|
||||
**Decrypt later:**
|
||||
- Use your PGP private key (on a safe machine) to decrypt the PGP message.
|
||||
**Options:** Same as `gen`, plus:
|
||||
|
||||
***
|
||||
- `--mnemonic MNEMONIC`: BIP39 mnemonic phrase
|
||||
- `--seed HEX_SEED`: 128-character hex seed
|
||||
- `--interactive`: Prompt for mnemonic/seed interactively
|
||||
|
||||
### Mode D — derive + export private keys (encrypted only)
|
||||
This is for when you need to import per-account keys into hot wallets (e.g., Phantom) but **don’t want to type the seed phrase into the app**.
|
||||
**Examples:**
|
||||
|
||||
**Behavior (as implemented):**
|
||||
- Still prints addresses to stdout.
|
||||
- Produces a PGP-encrypted payload that includes:
|
||||
- the mnemonic (so you can fully recover later)
|
||||
- a note that a passphrase was used (but not the passphrase)
|
||||
- **Ethereum private keys** (hex) for indices `0..--addresses-1`
|
||||
- **Solana Phantom-compatible private keys** (base58 64-byte secret key) for indices `0..--addresses-1`
|
||||
- **Bitcoin: addresses only** (no BTC private keys)
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
python hdwallet_recovery.py \
|
||||
--interactive \
|
||||
--chains ethereum solana bitcoin \
|
||||
--addresses 10 \
|
||||
--export-private \
|
||||
--passphrase 'YOUR_PASSPHRASE' \
|
||||
--passphrase-hint 'memory hint here' \
|
||||
--pgp-pubkey-file key.asc
|
||||
# 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
|
||||
```
|
||||
|
||||
**Hot wallet import guidance:**
|
||||
- Import only the specific derived account key you plan to treat as “hot”.
|
||||
- Fund only that account/address.
|
||||
- Assume the device/app is compromised eventually; rotate keys.
|
||||
### test
|
||||
|
||||
## Verification checklist (before trusting results)
|
||||
- Confirm you’re using the expected Python and venv:
|
||||
```bash
|
||||
which python
|
||||
python -V
|
||||
pip show bip-utils PGPy
|
||||
```
|
||||
- Confirm the PGP public key fingerprint is correct (out-of-band verified).
|
||||
- Confirm derived addresses match known wallet UI for the same mnemonic/passphrase (test with a small index range first).
|
||||
Run minimal self-tests to verify functionality.
|
||||
|
||||
## Security warnings (read this every time)
|
||||
- **Never** run derive mode on a machine you don’t trust.
|
||||
- Avoid passing mnemonics on the command line (`--mnemonic '...'`) because:
|
||||
- shell history may capture it
|
||||
- process lists can expose arguments
|
||||
- Prefer `--interactive` so the mnemonic is hidden input.
|
||||
- The encrypted payload printed to screen can still be:
|
||||
- copied into scrollback logs
|
||||
- captured by screen recording / monitoring
|
||||
- saved by terminal multiplexer logs
|
||||
Treat it as sensitive, even if encrypted.
|
||||
- If `--export-private` is used, the encrypted payload contains a **bundle of hot private keys**. Anyone who decrypts it controls those accounts. Keep it offline and limit distribution.
|
||||
- If a **passphrase** was used, losing it makes recovery impossible even with mnemonic and derived-address list. Store the passphrase separately and securely; the payload only stores a hint/reminder.
|
||||
- Consider using a dedicated “hot” seed (separate mnemonic) for accounts intended for hot-wallet import, rather than exporting keys derived from your main long-term seed.
|
||||
**Usage:**
|
||||
|
||||
## Quick command recipes
|
||||
|
||||
**1) Download and pin a key:**
|
||||
```bash
|
||||
python hdwallet_recovery.py fetchkey "https://github.com/<user>.gpg" --out key.asc
|
||||
python ./src/pyhdwallet.py test
|
||||
```
|
||||
|
||||
**2) Offline derive addresses (no encryption):**
|
||||
**Output:** Success/failure messages for derivation tests.
|
||||
|
||||
## Examples
|
||||
|
||||
### 1. Generate and Encrypt a New Wallet
|
||||
|
||||
```bash
|
||||
python hdwallet_recovery.py --interactive --chains ethereum solana bitcoin --addresses 10
|
||||
# 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
|
||||
```
|
||||
|
||||
**3) Offline derive + encrypt payload (no private key export):**
|
||||
### 2. Recover from Mnemonic with Passphrase
|
||||
|
||||
```bash
|
||||
python hdwallet_recovery.py --interactive --chains ethereum solana bitcoin --addresses 10 --pgp-pubkey-file key.asc
|
||||
python ./src/pyhdwallet.py recover --mnemonic "word1 word2 ... word12" --passphrase "mypass" --chains ethereum --addresses 10 --output json
|
||||
```
|
||||
|
||||
**4) Offline derive + encrypt payload + export ETH/SOL private keys:**
|
||||
### 3. Fetch and Use PGP Key
|
||||
|
||||
```bash
|
||||
python hdwallet_recovery.py --interactive --chains ethereum solana bitcoin --addresses 10 --export-private --passphrase-hint '...' --pgp-pubkey-file key.asc
|
||||
# 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
|
||||
```
|
||||
|
||||
If you want, the playbook can be turned into a `Makefile` (targets: `venv`, `fetchkey`, `derive`, `export`) so you don’t have to remember flags.
|
||||
### 4. Solana-Specific Derivation
|
||||
|
||||
[1](https://ppl-ai-file-upload.s3.amazonaws.com/web/direct-files/attachments/50321846/61044779-f904-4af9-9005-77f3f639e4d6/offline_HD_wallet_generator.html)
|
||||
```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.
|
||||
- **Passphrase handling**: Passphrases are not stored; only hints are included.
|
||||
- **Private key export**: Only export what's needed; treat encrypted payloads as sensitive.
|
||||
- **Best practices**:
|
||||
- Use `--interactive` to avoid command-line history exposure.
|
||||
- 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.
|
||||
- **Version check**: Run `python ./src/pyhdwallet.py --version`
|
||||
|
||||
## Changelog
|
||||
|
||||
- **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.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile ./requirements.in
|
||||
# pip-compile
|
||||
#
|
||||
bip-utils==2.10.0
|
||||
# via -r requirements.in
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
hdwallet_recovery.py (Python 3.12)
|
||||
pyhdwallet v1.0.1 (Python 3.11+)
|
||||
|
||||
Commands:
|
||||
- fetchkey (online): download ASCII-armored PGP public key and print SHA256 + fingerprint
|
||||
@@ -523,6 +523,7 @@ def cmd_test(args):
|
||||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(description="HD wallet gen/recover (offline) + fetchkey (online)")
|
||||
parser.add_argument("--version", action="version", version="pyhdwallet v1.0.1")
|
||||
sub = parser.add_subparsers(dest="cmd")
|
||||
|
||||
# fetchkey
|
||||
|
||||
16
test_output.json
Normal file
16
test_output.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"master_fingerprint": "DD1449B7",
|
||||
"passphrase_used": false,
|
||||
"passphrase_hint": "",
|
||||
"dice_rolls_used": false,
|
||||
"solana_profile": "phantom_bip44change",
|
||||
"addresses": {
|
||||
"ethereum": [
|
||||
{
|
||||
"index": 0,
|
||||
"path": "m/44'/60'/0'/0/0",
|
||||
"address": "0x9d3e3540f4C507ca992035607326798130051e03"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user