Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 132 additions & 12 deletions docs/user/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ 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.
descriptors, PSBTs, and signed transactions.

Bitcoin Core wallet encryption is strongly recommended. Bitcoin Core owns the
passphrase and its prompts; codex32 never asks for, reads, or forwards it.
Expand Down Expand Up @@ -192,16 +192,133 @@ worth the extra steps.

## More protection: watch-only wallet and offline signer

On the offline signer, create an empty encrypted descriptor wallet with private
keys enabled and run `ms32 wallet`. Keep that computer disconnected from every
network while recovery text or signing keys are present.
This setup follows Bitcoin Core v32's
[offline-signing tutorial](https://github.com/bitcoin/bitcoin/blob/v32.0rc1/doc/offline-signing-tutorial.md)

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets just link to master imo

and its two wallets: `offline_wallet` holds the signing keys on a computer that
never connects to a network, and `watch_only_wallet` runs on an ordinary online
node. Where the tutorial carries a file between the computers, this guide shows
the same public data as a QR on one screen and scans it with the other
computer's camera.

After the signer is restored, follow Bitcoin Core v32's maintained
[offline-signing tutorial](https://github.com/bitcoin/bitcoin/blob/v32.0rc1/doc/offline-signing-tutorial.md).
That workflow owns the watch-only export/import and PSBT transport steps. In
Bitcoin Core v32, `exportwatchonlywallet` creates the watch-only wallet file and
`restorewallet` loads it on the online node. Do not improvise a codex32-specific
descriptor-transfer procedure in place of that maintained workflow.
Before disconnecting the offline computer for good, install Bitcoin Core and
codex32. Both computers also use `python3`, `gzip`, `qr`, and ZBar's
`zbarcam`, which Tails already includes. Disable Ethernet, internet, Tor, Wi-Fi,
Bluetooth, cellular, and every other network path on the offline computer.
Comment on lines +203 to +206

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cNACK the CipherStick and it's Persistent Storage should be backed up with Tails Cloner, that USB stick marked, and then booted in the always offline PC, + / additional settings "Networking Disabled" in the Welcome Screen then start. Launch codex32 to create the offline signing wallet.

This avoids having to install everything again.


### 1. Restore the offline signer

On the offline computer, create an empty encrypted descriptor wallet named
`offline_wallet` with private keys enabled and run `ms32 wallet`. Keep that

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep that computer and USB stick permanently offline before and after keys are present.

computer disconnected from every network while recovery text or signing keys
are present.

### 2. Create the online watch-only wallet

The tutorial moves a watch-only wallet file made by `exportwatchonlywallet`.
That file does not fit in a QR even compressed, so send the public descriptors
instead, compressed with `gzip`. On the offline computer:
Comment on lines +217 to +219

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wasn't true


```bash
bitcoin-cli -rpcwallet=offline_wallet listdescriptors |
python3 -c 'import json, sys; print(json.dumps(json.load(sys.stdin)["descriptors"]), end="")' |
gzip -9 | qr
```

The compressed descriptors take about 640 bytes; see
[QR troubleshooting](#qr-troubleshooting) if the QR does not fit. Public
descriptors cannot spend, but they reveal wallet activity. Do not use a
website, cloud scanner, chat service, or synced clipboard.

On the online computer, create a blank watch-only wallet, then scan and
decompress the QR into it:

```bash
bitcoin-cli -named createwallet wallet_name=watch_only_wallet disable_private_keys=true blank=true
zbarcam --raw --oneshot -Sdisable -Sqrcode.enable -Sbinary | gunzip |
bitcoin-cli -rpcwallet=watch_only_wallet -stdin importdescriptors
```

Every result must say `"success": true`. Core's descriptor export keeps the
stored timestamps, so the online node rescans from the same point. Run
`getnewaddress` in each wallet and compare the two addresses on the two
screens. Do not receive funds if they differ.

### 3. Receive to a checked address

Get receiving addresses and set labels in `watch_only_wallet`, as the tutorial
does, so one wallet tracks which addresses are used. Malware on the online
computer could show an address it controls, so check every address on the
offline computer before giving it out. On the online computer:

```bash
bitcoin-cli -rpcwallet=watch_only_wallet getnewaddress "LABEL" | qr
```

On the offline computer, scan it and look it up in the signing wallet:

```bash
address=$(zbarcam --raw --oneshot -Sdisable -Sqrcode.enable)
bitcoin-cli -rpcwallet=offline_wallet getaddressinfo "$address"
```

Give out the address only if the result shows `"ismine": true`. This works
while `offline_wallet` is locked. The offline wallet recognizes its first 1,000
addresses of each type; past that, a real address shows `"ismine": false` until
you unlock `offline_wallet` and run `keypoolrefill` with a larger number.

When you pay yourself from a phone wallet, or the payer is with you, show the
checked address as a QR on the offline screen and scan it there; nothing needs
comparing:

```bash
printf %s "$address" | qr
```

For an exchange withdrawal or a payer over the internet, the address must pass
through a networked computer or phone, where malware could swap it after the
check. Paste it there, then compare the address on the last screen before you
submit or send, such as the exchange's confirmation page or your sent message,
character by character with the `"address"` shown offline.

### 4. Spend with a PSBT

On the online computer, create the unsigned PSBT with your destination and
amount, and show it as a QR:

```bash
bitcoin-cli -rpcwallet=watch_only_wallet send '{"DESTINATION_ADDRESS": AMOUNT}' |
python3 -c 'import json, sys; print(json.load(sys.stdin)["psbt"], end="")' > funded_psbt.txt &&
qr < funded_psbt.txt
```

On the offline computer, scan it, then check every destination, amount, and
fee before signing:

```bash
zbarcam --raw --oneshot -Sdisable -Sqrcode.enable > funded_psbt.txt
bitcoin-cli decodepsbt "$(cat funded_psbt.txt)"
bitcoin-cli analyzepsbt "$(cat funded_psbt.txt)"
```

Unlock `offline_wallet` as the tutorial shows, then sign and show the signed
transaction as a QR:

```bash
bitcoin-cli -rpcwallet=offline_wallet walletprocesspsbt "$(cat funded_psbt.txt)" |
python3 -c 'import json, sys; r = json.load(sys.stdin); print(r["hex"] if r["complete"] else sys.exit("The PSBT is not fully signed."), end="")' > final_psbt.txt &&
qr < final_psbt.txt
```

On the online computer, scan it and broadcast:

```bash
zbarcam --raw --oneshot -Sdisable -Sqrcode.enable > final_psbt.txt
bitcoin-cli sendrawtransaction "$(cat final_psbt.txt)"
```

If a PSBT is too large for a reliable QR, use a dedicated removable drive. The
drive crosses the security boundary: keep it for this purpose, treat every file
on it as untrusted, and still verify the transaction on the offline screen.

## Recover an existing or inherited wallet

Expand Down Expand Up @@ -296,8 +413,11 @@ arbitrary-HRP format direction is not yet merged into that specification.
### QR troubleshooting

Maximize the terminal and reduce its font size if a QR does not fit. Keep `qr`
connected to the terminal; redirecting its output creates an image file. Only
public descriptors, xpubs, and PSBTs may cross the offline boundary by QR.
connected to the terminal; redirecting its output creates an image file.
Reading the compressed descriptors needs ZBar 0.23.1 or newer for `-Sbinary`;
without it, ZBar rewrites the bytes as text and `gunzip` fails. Only public
descriptors, xpubs, PSBTs, and signed transactions may cross the offline
boundary by QR.

## Technical references

Expand Down
Loading