SeedSigner / seedsigner

Use an air-gapped Raspberry Pi Zero to sign for Bitcoin transactions! (and do other cool stuff)
MIT License
699 stars 161 forks source link

Opinion on emergency recovery approach #511

Open yanone opened 10 months ago

yanone commented 10 months ago

I've decided for Seedsigner as my cold storage solution. Excellent work, by the way.

Before getting on with it, tho, I was looking for an emergency recovery method that would allow me to recover my seed phrase from a SeedQR. This is in case I would lose or brick my Seedsigner device, but I would have my QR handy, but not the seed phrase which would be stored safely somewhere else for backup.

The idea would be to run this code on an air-gapped TailsOS machine or something for privacy.

Since I couldn't get zbar to run on my Mac, I implemented the QR code reading with OpenCV, which comes through PyPI and is therefore better suited for this recovery tool.

Since I don't actually have the Seedsigner yet, I wanted to ask for opinions on this code and whether someone could please test it using their existing QR codes (both compact and standard) and verify the output.

Especially the except statement in compact() is a bit awkward but I couldn't get this to work otherwise. In fact this works reliably ;)

import cv2
from embit import bip39

wordlist = bip39.WORDLIST

def compact(filepath):
    image = cv2.imread(filepath)
    detect = cv2.QRCodeDetector()

    try:
        value, points, straight_qrcode = detect.detectAndDecode(image)
    except UnicodeDecodeError as ex:
        value = ex.object

    mnemonic = bip39.mnemonic_from_bytes(value).split()
    if len(mnemonic) not in (12, 24):
        raise Exception("Invalid seed phrase")
    return " ".join(mnemonic)

def standard(filepath):
    image = cv2.imread(filepath)
    detect = cv2.QRCodeDetector()
    segment, _pts, _st_code = detect.detectAndDecode(image)

    mnemonic = []
    num_words = int(len(segment) / 4)
    for i in range(0, num_words):
        index = int(segment[i * 4 : (i * 4) + 4])
        word = wordlist[index]
        mnemonic.append(word)
    if len(mnemonic) not in (12, 24):
        raise Exception("Invalid seed phrase")
    return " ".join(mnemonic)