2021-02-24 06:18:01 -05:00
<!--
Title: How to create encrypted paper backup
Description: Learn how to create encrypted paper backup.
Author: Sun Knudsen < https: / / github . com / sunknudsen >
2021-03-03 15:10:07 -05:00
Contributors: Sun Knudsen < https: / / github . com / sunknudsen > , Alex Anderson < https: / / github . com / Serpent27 > , Nico Kaiser < https: / / github . com / nicokaiser >
2021-02-24 06:18:01 -05:00
Reviewers:
Publication date: 2021-02-23T21:53:38.495Z
Listed: true
-->
# How to create encrypted paper backup
## Requirements
- [Hardened Raspberry Pi ](../how-to-configure-hardened-raspberry-pi ) 📦
2021-03-02 17:51:33 -05:00
- Raspberry Pi OS-compatible auto-focus HD USB webcam
2021-02-24 06:18:01 -05:00
- [Adafruit PiTFT monitor ](https://www.adafruit.com/product/2423 ) (optional)
2021-03-02 16:44:24 -05:00
- macOS computer
2021-02-24 06:18:01 -05:00
## Caveats
- When copy/pasting commands that start with `$` , strip out `$` as this character is not part of the command
- When copy/pasting commands that start with `cat << "EOF"` , select all lines at once (from `cat << "EOF"` to `EOF` inclusively) as they are part of the same (single) command
## Setup guide
### Step 1: log in to Raspberry Pi
Replace `10.0.1.248` with IP of Raspberry Pi.
```shell
ssh pi@10 .0.1.248 -i ~/.ssh/pi
```
### Step 2 (optional): install [Adafruit PiTFT monitor](https://www.adafruit.com/product/2423) drivers and disable console auto login
#### Install [Adafruit PiTFT monitor](https://www.adafruit.com/product/2423) drivers
> Heads-up: don’ t worry about `PITFT Failed to disable unit: Unit file fbcp.service does not exist.`.
2021-03-02 16:44:24 -05:00
> Heads-up: when asked to reboot, type `n` and press enter.
```console
2021-02-24 06:18:01 -05:00
$ sudo apt update
$ sudo apt install -y git python3-pip
2021-03-04 09:23:37 -05:00
$ sudo pip3 install adafruit-python-shell click==7.0
2021-02-24 06:18:01 -05:00
$ git clone https://github.com/adafruit/Raspberry-Pi-Installer-Scripts.git
$ cd Raspberry-Pi-Installer-Scripts
$ sudo python3 adafruit-pitft.py --display=28c --rotation=90 --install-type=console
2021-03-03 15:10:07 -05:00
$ cd ~
$ rm -fr Raspberry-Pi-Installer-Scripts
2021-02-24 06:18:01 -05:00
```
#### Disable console auto login
2021-03-02 16:44:24 -05:00
> Heads-up: when asked to reboot, select “No” and press enter.
2021-02-24 06:18:01 -05:00
```shell
sudo raspi-config
```
Select “System Options”, then “Boot / Auto Login”, then “Console” and finally “Finish”.
### Step 3: configure keyboard keymap
> Heads-up: following instructions are for [Raspberry Pi keyboard](https://www.raspberrypi.org/products/raspberry-pi-keyboard-and-hub/) (US model).
2021-03-02 16:44:24 -05:00
> Heads-up: when asked to reboot, select “No” and press enter.
2021-02-24 06:18:01 -05:00
```shell
sudo raspi-config
```
Select “Localisation Options”, then “Keyboard”, then “Generic 105-key PC (intl.)”, then “Other”, then “English (US)”, then “English (US)”, then “The default for the keyboard layout”, then “No compose key” and finally “Finish”.
### Step 4: install dependencies
2021-03-02 16:44:24 -05:00
```console
2021-02-24 06:18:01 -05:00
$ sudo apt update
$ sudo apt install -y fim imagemagick zbar-tools
2021-03-07 05:46:55 -05:00
$ pip3 install mnemonic pillow qrcode --user
2021-02-24 06:18:01 -05:00
2021-03-03 15:10:07 -05:00
$ echo -e "export GPG_TTY=\"\$(tty)\"\nexport PATH=\$PATH:/home/pi/.local/bin" >> ~/.bashrc
2021-02-24 06:18:01 -05:00
$ source ~/.bashrc
```
2021-03-04 09:23:37 -05:00
### Step 5 (optional): install `screen` and [Trezor](https://trezor.io/)’ s [trezorcrl](https://wiki.trezor.io/Using_trezorctl_commands_with_Trezor)
2021-03-07 05:46:55 -05:00
> Heads-up: we will likely use `screen` and `trezorcrl` command line utilities in the future and this guide is designed to configure a [read-only](#step-12-make-filesystem-read-only) Raspberry Pi.
2021-03-04 09:23:37 -05:00
```console
$ sudo apt install -y screen
$ pip3 install attrs trezor --user
$ sudo curl https://data.trezor.io/udev/51-trezor.rules -o /etc/udev/rules.d/51-trezor.rules
```
2021-03-07 05:46:55 -05:00
### Step 6: download [create-seed.py](./create-seed.py) ([PGP signature](./create-seed.py.sig), [PGP public key](https://sunknudsen.com/sunknudsen.asc))
2021-02-25 14:06:45 -05:00
```shell
2021-03-07 05:46:55 -05:00
sudo curl -o /usr/local/sbin/create-seed.py https://sunknudsen.com/static/media/privacy-guides/how-to-create-encrypted-paper-backup/create-seed.py
2021-02-25 14:06:45 -05:00
```
2021-03-07 05:46:55 -05:00
### Step 7: download [validate-seed.py](./validate-seed.py) ([PGP signature](./validate-seed.py.sig), [PGP public key](https://sunknudsen.com/sunknudsen.asc))
```shell
2021-03-07 08:08:01 -05:00
sudo curl -o /usr/local/sbin/validate-seed.py https://sunknudsen.com/static/media/privacy-guides/how-to-create-encrypted-paper-backup/validate-seed.py
2021-03-07 05:46:55 -05:00
```
### Step 8: download [qr-backup.sh](./qr-backup.sh) ([PGP signature](./qr-backup.sh.sig), [PGP public key](https://sunknudsen.com/sunknudsen.asc))
2021-02-24 06:18:01 -05:00
```shell
sudo curl -o /usr/local/sbin/qr-backup.sh https://sunknudsen.com/static/media/privacy-guides/how-to-create-encrypted-paper-backup/qr-backup.sh
sudo chmod +x /usr/local/sbin/qr-backup.sh
```
2021-03-07 05:46:55 -05:00
### Step 9: download [qr-restore.sh](./qr-restore.sh) ([PGP signature](./qr-restore.sh.sig), [PGP public key](https://sunknudsen.com/sunknudsen.asc))
2021-02-24 06:18:01 -05:00
```shell
sudo curl -o /usr/local/sbin/qr-restore.sh https://sunknudsen.com/static/media/privacy-guides/how-to-create-encrypted-paper-backup/qr-restore.sh
sudo chmod +x /usr/local/sbin/qr-restore.sh
```
2021-03-07 05:46:55 -05:00
### Step 10: download [qr-clone.sh](./qr-clone.sh) ([PGP signature](./qr-clone.sh.sig), [PGP public key](https://sunknudsen.com/sunknudsen.asc))
2021-02-24 06:18:01 -05:00
```shell
sudo curl -o /usr/local/sbin/qr-clone.sh https://sunknudsen.com/static/media/privacy-guides/how-to-create-encrypted-paper-backup/qr-clone.sh
sudo chmod +x /usr/local/sbin/qr-clone.sh
```
2021-03-07 05:46:55 -05:00
### Step 11: download [secure-erase.sh](./secure-erase.sh) ([PGP signature](./secure-erase.sh.sig), [PGP public key](https://sunknudsen.com/sunknudsen.asc))
2021-03-02 16:46:45 -05:00
```shell
sudo curl -o /usr/local/sbin/secure-erase.sh https://sunknudsen.com/static/media/privacy-guides/how-to-create-encrypted-paper-backup/secure-erase.sh
sudo chmod +x /usr/local/sbin/secure-erase.sh
```
2021-03-07 05:46:55 -05:00
### Step 12: make filesystem read-only
2021-03-03 15:10:07 -05:00
> Heads-up: shout-out to Nico Kaiser for his amazing [guide](https://gist.github.com/nicokaiser/08aa5b7b3958f171cf61549b70e8a34b) on how to configure a read-only Raspberry Pi.
#### Disable swap
```shell
sudo dphys-swapfile swapoff
sudo dphys-swapfile uninstall
sudo systemctl disable dphys-swapfile.service
```
#### Remove `dphys-swapfile` `fake-hwclock` and `logrotate`
```shell
sudo apt remove -y --purge dphys-swapfile fake-hwclock logrotate
```
#### Link `/etc/console-setup` to `/tmp/console-setup`
```shell
sudo mv /etc/console-setup /tmp/console-setup
sudo ln -s /tmp/console-setup /etc/console-setup
```
#### Link `/etc/resolv.conf` to `/tmp/resolv.conf`
```shell
sudo mv /etc/resolv.conf /tmp/resolv.conf
sudo ln -s /tmp/resolv.conf /etc/resolv.conf
```
#### Link `/home/pi/.gnupg` to `/tmp/pi/.gnupg`
```shell
mkdir -m 700 /tmp/pi
mv /home/pi/.gnupg /tmp/pi/.gnupg
ln -s /tmp/pi/.gnupg /home/pi/.gnupg
```
2021-03-04 11:09:01 -05:00
#### Enable `tmp.mount` service
2021-03-03 15:10:07 -05:00
```shell
echo -e "D /tmp 1777 root root -\nD /tmp/console-setup 1700 root root -\nD /tmp/pi 1700 pi pi -\nD /tmp/pi/.gnupg 1700 pi pi -\nD /var/tmp 1777 root root -" | sudo tee /etc/tmpfiles.d/tmp.conf
sudo cp /usr/share/systemd/tmp.mount /etc/systemd/system/
sudo systemctl enable tmp.mount
```
#### Edit `/boot/cmdline.txt`
2021-03-02 17:19:42 -05:00
2021-03-03 15:10:07 -05:00
```shell
sudo cp /boot/cmdline.txt /boot/cmdline.txt.backup
sudo sed -i 's/fsck.repair=yes/fsck.repair=skip/' /boot/cmdline.txt
2021-03-03 15:23:18 -05:00
sudo sed -i '$ s/$/ fastboot noswap ro systemd.volatile=state/' /boot/cmdline.txt
2021-03-03 15:10:07 -05:00
```
#### Edit `/etc/fstab`
```shell
sudo cp /etc/fstab /etc/fstab.backup
sudo sed -i -e 's/vfat\s*defaults\s/vfat defaults,ro/' /etc/fstab
sudo sed -i -e 's/ext4\s*defaults,noatime\s/ext4 defaults,noatime,ro,noload/' /etc/fstab
```
2021-03-07 05:46:55 -05:00
### Step 13: disable Wi-Fi (if not using ethernet)
2021-03-03 15:10:07 -05:00
```shell
2021-03-04 11:09:01 -05:00
echo "dtoverlay=disable-wifi" | sudo tee -a /boot/config.txt
2021-03-03 15:10:07 -05:00
```
2021-03-07 05:46:55 -05:00
### Step 14: disable `dhcpcd`, `networking` and `wpa_supplicant` services and “fix” `rfkill` bug
2021-03-02 17:19:42 -05:00
2021-03-04 11:44:37 -05:00
```console
$ sudo systemctl disable dhcpcd networking wpa_supplicant
$ sudo rm /etc/profile.d/wifi-check.sh
2021-03-02 17:19:42 -05:00
```
2021-03-07 05:46:55 -05:00
### Step 15: delete macOS hidden files (if present)
2021-03-04 09:23:37 -05:00
```shell
2021-03-04 11:09:01 -05:00
sudo rm -fr /boot/.fseventsd /boot/.DS_Store /boot/.Spotlight-V100
2021-03-04 09:23:37 -05:00
```
2021-03-07 05:46:55 -05:00
### Step 16: reboot
2021-02-24 06:18:01 -05:00
```shell
2021-03-05 10:33:51 -05:00
sudo systemctl reboot
2021-02-24 06:18:01 -05:00
```
2021-03-03 15:10:07 -05:00
> WARNING: DO NOT CONNECT RASPBERRY PI TO NETWORK EVER AGAIN WITHOUT REINSTALLING RASPBERRY PI OS FIRST (DEVICE IS NOW "READ-ONLY" AND “COLD”).
2021-03-07 05:46:55 -05:00
### Step 17 (optional): disable auto-mount of `boot` volume (on macOS)
2021-03-03 15:10:07 -05:00
2021-03-07 05:46:55 -05:00
> Heads-up: done to prevent macOS from writing [hidden files](#step-15-delete-macos-hidden-files-if-present) to `boot` volume which would invalidate stored SHA512 hash of micro SD card.
2021-03-03 18:36:34 -05:00
Insert micro SD card into macOS computer, run following and eject card.
```shell
volume_path="/Volumes/boot"
2021-03-16 14:23:21 -04:00
volume_uuid=$(diskutil info "$volume_path" | awk '/Volume UUID:/ { print $3 }')
2021-03-03 18:36:34 -05:00
echo "UUID=$volume_uuid none msdos rw,noauto" | sudo tee -a /etc/fstab
```
2021-03-07 05:46:55 -05:00
### Step 18 (optional): compute SHA512 hash of micro SD card and store in password manager (on macOS)
2021-03-03 18:36:34 -05:00
Run `diskutil list` to find disk ID of micro SD card with “Raspberry Pi OS Lite” installed (`disk2` in the following example).
2021-03-03 15:10:07 -05:00
```console
$ diskutil list
/dev/disk0 (internal, physical):
#: TYPE NAME SIZE IDENTIFIER
0: GUID_partition_scheme *500.3 GB disk0
1: EFI EFI 209.7 MB disk0s1
2: Apple_APFS Container disk1 500.1 GB disk0s2
/dev/disk1 (synthesized):
#: TYPE NAME SIZE IDENTIFIER
0: APFS Container Scheme - +500.1 GB disk1
Physical Store disk0s2
1: APFS Volume Macintosh HD - Data 340.9 GB disk1s1
2: APFS Volume Preboot 85.9 MB disk1s2
3: APFS Volume Recovery 529.0 MB disk1s3
4: APFS Volume VM 3.2 GB disk1s4
5: APFS Volume Macintosh HD 11.3 GB disk1s5
/dev/disk2 (internal, physical):
#: TYPE NAME SIZE IDENTIFIER
0: FDisk_partition_scheme *15.9 GB disk2
1: Windows_FAT_32 boot 268.4 MB disk2s1
2: Linux 15.7 GB disk2s2
2021-03-04 11:44:37 -05:00
$ sudo diskutil unmountDisk /dev/diskn
2021-03-03 15:10:07 -05:00
Unmount of all volumes on disk2 was successful
2021-03-04 11:44:37 -05:00
$ sudo openssl dgst -sha512 /dev/rdiskn
2021-03-03 18:36:34 -05:00
SHA512(/dev/rdisk2)= 353af7e9bd78d7d98875f0e2a58da3d7cdfc494f2ab5474b2ab4a8fd212ac6a37c996d54f6c650838adb61e4b30801bcf1150081f6dbb51998cf33a74fa7f0fe
2021-03-03 15:10:07 -05:00
```
2021-02-24 06:18:01 -05:00
👍
---
## Usage guide
### Create encrypted paper backup
2021-03-02 16:44:24 -05:00
> Heads-up: use `--bip39` to test secret against BIP39 [word list](https://raw.githubusercontent.com/bitcoin/bips/master/bip-0039/english.txt).
```console
$ qr-backup.sh --help
Usage: qr-backup.sh [options]
Options:
2021-03-07 05:46:55 -05:00
--create-seed create 24-word BIP39 seed
--validate-seed validate if secret is BIP39 seed
-h, --help display help for command
2021-03-02 16:44:24 -05:00
$ qr-backup.sh
Format USB flash drive? (y or n)?
y
mkfs.fat 4.1 (2017-01-24)
Type secret and press enter (again)
this is a test yo
-----BEGIN PGP MESSAGE-----
jA0ECQMKmFCBKHBUX8z/0kUBxi8eP7LRqP0WgOF+VgTMYuvix7AMxWR/TRM+zQk/
i9JLr52Odmxv23jEC/KfAUdigAqhs3/GJRtwWuC2IR5NzfBNvXM=
=xkQH
-----END PGP MESSAGE-----
SHA512 hash: 177cc163d89498b859ce06f6f2ac1cd2f9f493b848cdf08746bfb2f4a8bf958ebb45eb70f8f20141c12aa65387ee0545b7c0757cf8d6c808e2fa449fad0e986a
SHA512 short hash: 177cc163
Show SHA512 hash as QR code? (y or n)?
n
Done
2021-02-24 06:18:01 -05:00
```
2021-03-02 16:44:24 -05:00
The following image is now available on USB flash drive.

2021-02-24 06:18:01 -05:00
### Restore encrypted paper backup
2021-02-25 15:00:00 -05:00
> Heads-up: use `--word-list` to split secret into word list.
2021-02-25 14:03:38 -05:00
2021-03-02 16:44:24 -05:00
```console
$ qr-restore.sh
Usage: qr-restore.sh [options]
Options:
2021-03-06 15:07:57 -05:00
--word-list split secret into word list
-h, --help display help for command
2021-03-02 16:44:24 -05:00
$ qr-restore.sh
Scan QR code…
-----BEGIN PGP MESSAGE-----
jA0ECQMKmFCBKHBUX8z/0kUBxi8eP7LRqP0WgOF+VgTMYuvix7AMxWR/TRM+zQk/
i9JLr52Odmxv23jEC/KfAUdigAqhs3/GJRtwWuC2IR5NzfBNvXM=
=xkQH
-----END PGP MESSAGE-----
SHA512 hash: 177cc163d89498b859ce06f6f2ac1cd2f9f493b848cdf08746bfb2f4a8bf958ebb45eb70f8f20141c12aa65387ee0545b7c0757cf8d6c808e2fa449fad0e986a
SHA512 short hash: 177cc163
Show secret? (y or n)?
y
gpg: AES256 encrypted data
gpg: encrypted with 1 passphrase
Secret: this is a test yo
Done
2021-02-24 06:18:01 -05:00
```
### Clone encrypted paper backup
2021-03-02 16:44:24 -05:00
```console
$ qr-clone.sh --help
Usage: qr-clone.sh [options]
Options:
2021-03-06 15:07:57 -05:00
-h, --help display help for command
2021-03-02 16:44:24 -05:00
$ qr-clone.sh
Scan QR code…
-----BEGIN PGP MESSAGE-----
jA0ECQMKmFCBKHBUX8z/0kUBxi8eP7LRqP0WgOF+VgTMYuvix7AMxWR/TRM+zQk/
i9JLr52Odmxv23jEC/KfAUdigAqhs3/GJRtwWuC2IR5NzfBNvXM=
=xkQH
-----END PGP MESSAGE-----
SHA512 hash: 177cc163d89498b859ce06f6f2ac1cd2f9f493b848cdf08746bfb2f4a8bf958ebb45eb70f8f20141c12aa65387ee0545b7c0757cf8d6c808e2fa449fad0e986a
SHA512 short hash: 177cc163
Show secret? (y or n)?
y
gpg: AES256 encrypted data
gpg: encrypted with 1 passphrase
Secret: this is a test yo
Done
Backing up…
Format USB flash drive? (y or n)?
y
mkfs.fat 4.1 (2017-01-24)
-----BEGIN PGP MESSAGE-----
jA0ECQMKAWdJZylXXDf/0kUB/rRdX1+5OYVh7iwzM0julwIfDe57slc6LeGeRtDa
KfY4QZkCrseEoZdSZd5mGYQ0ItW9exfBiXN5AU+rbEmzF6VuEWY=
=ul1g
-----END PGP MESSAGE-----
SHA512 hash: 524d8219b17aad59d7cec70f901dfdd449d15f21479740b0111b621cc870e6d82f2f4a0ea8303fb478b24500195325be9c3256d4d5b19700a1cdd1329fc2c71f
SHA512 short hash: 524d8219
Show SHA512 hash as QR code? (y or n)?
n
Done
```
The following image is now available on USB flash drive.

### Secure erase flash drive
```console
$ secure-erase.sh --help
Usage: secure-erase.sh [options]
Options:
2021-03-06 15:07:57 -05:00
--rounds < rounds > overwrite n times (defauls to 3)
--zero overwrite with zeros obfuscating secure erase
-h, --help display help for command
2021-03-02 16:44:24 -05:00
$ secure-erase.sh
Secure erase USB flash drive? (y or n)?
y
Erasing… (iteration 1 of 3)
dd: error writing '/dev/sda1': No space left on device
1868+0 records in
1867+0 records out
1957691392 bytes (2.0 GB, 1.8 GiB) copied, 181.888 s, 10.8 MB/s
Erasing… (iteration 2 of 3)
dd: error writing '/dev/sda1': No space left on device
1868+0 records in
1867+0 records out
1957691392 bytes (2.0 GB, 1.8 GiB) copied, 195.606 s, 10.0 MB/s
Erasing… (iteration 3 of 3)
dd: error writing '/dev/sda1': No space left on device
1868+0 records in
1867+0 records out
1957691392 bytes (2.0 GB, 1.8 GiB) copied, 195.558 s, 10.0 MB/s
Done
2021-02-24 06:18:01 -05:00
```
👍