#! /bin/bash set -e set -o pipefail shamir_secret_sharing=false number_of_shares=5 share_threshold=3 positional=() while [ $# -gt 0 ]; do argument="$1" case $argument in -h|--help) printf "%s\n" \ "Usage: qr-backup.sh [options]" \ "" \ "Options:" \ " --create-bip39-mnemonic create BIP39 mnemonic" \ " --create-electrum-mnemonic create Electrum mnemonic" \ " --validate-bip39-mnemonic validate if secret is valid BIP39 mnemonic" \ " --shamir-secret-sharing split secret using Shamir Secret Sharing" \ " --number-of-shares number of shares (defaults to 5)" \ " --share-threshold shares required to access secret (defaults to 3)" \ " --no-qr disable show SHA512 hash as QR code prompt" \ " --label <label> print label after short hash" \ " -h, --help display help for command" exit 0 ;; --create-bip39-mnemonic) create_bip39_mnemonic=true shift ;; --create-electrum-mnemonic) create_electrum_mnemonic=true shift ;; --validate-bip39-mnemonic) validate_bip39_mnemonic=true shift ;; --shamir-secret-sharing) shamir_secret_sharing=true shift ;; --number-of-shares) number_of_shares=$2 shift shift ;; --share-threshold) share_threshold=$2 shift shift ;; --no-qr) no_qr=true shift ;; --label) label=$2 shift shift ;; *) positional+=("$1") shift ;; esac done set -- "${positional[@]}" bold=$(tput bold) red=$(tput setaf 1) normal=$(tput sgr0) basedir=$(dirname "$0") dev="/dev/sda1" tmp="/tmp/pi" usb="/tmp/usb" tput reset wait_for_usb_flash_drive () { if [ ! -e $dev ]; then printf "$bold%s$normal" "Insert USB flash drive and press enter" read -r confirmation wait_for_usb_flash_drive fi } wait_for_usb_flash_drive printf "$bold%s$normal\n" "Format USB flash drive (y or n)?" read -r answer if [ "$answer" = "y" ]; then if mount | grep $dev > /dev/null; then sudo umount $dev fi sudo mkfs -t vfat $dev fi sudo mkdir -p $usb if ! mount | grep $dev > /dev/null; then sudo mount $dev $usb --options uid=pi,gid=pi fi if [ -z "$duplicate" ] && [ "$create_bip39_mnemonic" = true ]; then printf "%s\n" "Creating BIP39 mnemonic…" secret=$(python3 $basedir/create-bip39-mnemonic.py) echo $secret sleep 1 fi if [ -z "$duplicate" ] && [ "$create_electrum_mnemonic" = true ]; then printf "%s\n" "Creating Electrum mnemonic…" secret=$(electrum make_seed --nbits 264 --offline) echo $secret sleep 1 fi if [ -z "$duplicate" ] && [ -z "$secret" ]; then tput sc printf "$bold%s$normal\n" "Please type secret and press enter, then ctrl+d" readarray -t secret_array secret=$(printf "%s\n" "${secret_array[@]}") tput rc tput ed printf "$bold%s$normal\n" "Please type secret and press enter, then ctrl+d (again)" readarray -t secret_confirmation_array secret_confirmation=$(printf "%s\n" "${secret_confirmation_array[@]}") if [ ! "$secret" = "$secret_confirmation" ]; then printf "$bold$red%s$normal\n" "Secrets do not match" exit 1 fi fi if [ -z "$duplicate" ] && [ "$validate_bip39_mnemonic" = true ]; then printf "%s\n" "Validating if secret is valid BIP39 mnemonic…" if ! echo -n "$secret" | python3 $basedir/validate-bip39-mnemonic.py; then printf "$bold$red%s$normal\n" "Invalid BIP39 mnemonic" exit 1 fi fi read_passphrase () { local -n data=$1 printf "$bold%s$normal\n" "Please type passphrase and press enter" read -rs data printf "$bold%s$normal\n" "Please type passphrase and press enter (again)" read -rs data_confirmation if [ ! "$data" = "$data_confirmation" ]; then printf "$bold$red%s$normal\n" "Passphrases do not match" return 1 fi printf "$bold%s$normal\n" "Show passphrase (y or n)?" read -r answer if [ "$answer" = "y" ]; then printf "%s\n" $data fi } if [ "$shamir_secret_sharing" = true ]; then read_passphrase passphrase share_number=1 for share in $(echo -n "$secret" | secret-share-split -n $number_of_shares -t $share_threshold); do printf "$bold%s$normal\n" "Encrypting secret share $share_number of $number_of_shares…" encrypted_secret=$(echo -n "$share" | gpg --batch --passphrase-fd 3 --s2k-mode 3 --s2k-count 65011712 --s2k-digest-algo sha512 --cipher-algo AES256 --symmetric --armor 3<<<"$passphrase") encrypted_secret_hash=$(echo -n "$encrypted_secret" | openssl dgst -sha512 | sed 's/^.* //') encrypted_secret_short_hash=$(echo -n "$encrypted_secret_hash" | head -c 8) printf "%s\n" "$encrypted_secret" printf "%s: $bold%s$normal\n" "SHA512 hash" "$encrypted_secret_hash" printf "%s: $bold%s$normal\n" "SHA512 short hash" "$encrypted_secret_short_hash" echo -n "$encrypted_secret" | qr --error-correction L > "$tmp/secret.png" font_size=$(echo "$(convert "$tmp/secret.png" -format "%h" info:) / 8" | bc) text_offset=$(echo "$font_size * 1.5" | bc) if [ -z "$label" ]; then text="$encrypted_secret_short_hash" else text="$encrypted_secret_short_hash $label" fi convert "$tmp/secret.png" -gravity center -scale 200% -extent 125% -scale 125% -gravity south -font /usr/share/fonts/truetype/noto/NotoMono-Regular.ttf -pointsize $font_size -fill black -draw "text 0,$text_offset '$text'" "$usb/$encrypted_secret_short_hash.jpg" if [ -z "$no_qr" ]; then printf "$bold%s$normal\n" "Show SHA512 hash as QR code (y or n)?" read -r answer if [ "$answer" = "y" ]; then printf "$bold%s$normal\n" "Press q to quit" sleep 1 echo -n "$encrypted_secret_hash" | qr --error-correction L > "$tmp/secret-hash.png" sudo fim --autozoom --quiet --vt 1 "$tmp/secret-hash.png" fi fi share_number=$((share_number+1)) done else if [ "$duplicate" = true ] && [ -n "$encrypted_secret" ]; then printf "%s\n" "Duplicating encrypted secret…" else read_passphrase passphrase printf "$bold%s$normal\n" "Encrypting secret…" encrypted_secret=$(echo -n "$secret" | gpg --batch --passphrase-fd 3 --s2k-mode 3 --s2k-count 65011712 --s2k-digest-algo sha512 --cipher-algo AES256 --symmetric --armor 3<<<"$passphrase") fi encrypted_secret_hash=$(echo -n "$encrypted_secret" | openssl dgst -sha512 | sed 's/^.* //') encrypted_secret_short_hash=$(echo -n "$encrypted_secret_hash" | head -c 8) printf "%s\n" "$encrypted_secret" printf "%s: $bold%s$normal\n" "SHA512 hash" "$encrypted_secret_hash" printf "%s: $bold%s$normal\n" "SHA512 short hash" "$encrypted_secret_short_hash" echo -n "$encrypted_secret" | qr --error-correction L > "$tmp/secret.png" font_size=$(echo "$(convert "$tmp/secret.png" -format "%h" info:) / 8" | bc) text_offset=$(echo "$font_size * 1.5" | bc) if [ -z "$label" ]; then text="$encrypted_secret_short_hash" else text="$encrypted_secret_short_hash $label" fi convert "$tmp/secret.png" -gravity center -scale 200% -extent 125% -scale 125% -gravity south -font /usr/share/fonts/truetype/noto/NotoMono-Regular.ttf -pointsize $font_size -fill black -draw "text 0,$text_offset '$text'" "$usb/$encrypted_secret_short_hash.jpg" if [ -z "$no_qr" ]; then printf "$bold%s$normal\n" "Show SHA512 hash as QR code (y or n)?" read -r answer if [ "$answer" = "y" ]; then printf "$bold%s$normal\n" "Press q to quit" sleep 1 echo -n "$encrypted_secret_hash" | qr --error-correction L > "$tmp/secret-hash.png" sudo fim --autozoom --quiet --vt 1 "$tmp/secret-hash.png" fi fi fi sudo umount $dev printf "%s\n" "Done"