mirror of
https://github.com/SeedSigner/seedsigner.git
synced 2026-10-05 23:18:25 +00:00
work in progress
This commit is contained in:
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
||||
|
||||
setuptools.setup(
|
||||
name="seedsigner",
|
||||
version="0.4.1",
|
||||
version="0.4.4",
|
||||
author="SeedSigner",
|
||||
author_email="author@example.com",
|
||||
description="Build an offline, airgapped Bitcoin signing device for less than $50!",
|
||||
|
||||
@@ -174,7 +174,7 @@ class Controller(Singleton):
|
||||
### Generate Last Word 12 / 24 Menu
|
||||
|
||||
def show_generate_last_word_tool(self) -> int:
|
||||
seed = Seed()
|
||||
seed = Seed(wordlist=self.settings.wordlist)
|
||||
ret_val = 0
|
||||
|
||||
while True:
|
||||
@@ -215,7 +215,7 @@ class Controller(Singleton):
|
||||
### Create a Seed w/ Dice Screen
|
||||
|
||||
def show_create_seed_with_dice_tool(self) -> int:
|
||||
seed = Seed()
|
||||
seed = Seed(wordlist=self.settings.wordlist)
|
||||
ret_val = True
|
||||
|
||||
while True:
|
||||
@@ -249,7 +249,7 @@ class Controller(Singleton):
|
||||
### Create a Seed w/ Image
|
||||
|
||||
def show_create_seed_with_image_tool(self) -> int:
|
||||
seed = Seed()
|
||||
seed = Seed(wordlist=self.settings.wordlist)
|
||||
ret_val = True
|
||||
|
||||
while True:
|
||||
@@ -287,7 +287,7 @@ class Controller(Singleton):
|
||||
### Store a seed (temp) Menu
|
||||
|
||||
def show_store_a_seed_tool(self):
|
||||
seed = Seed()
|
||||
seed = Seed(wordlist=self.settings.wordlist)
|
||||
ret_val = 0
|
||||
display_saved_seed = False
|
||||
ret_val = self.menu_view.display_saved_seed_menu(self.storage, 1, "... [ Return to Seed Tools ]")
|
||||
@@ -410,7 +410,7 @@ class Controller(Singleton):
|
||||
### Generate XPUB
|
||||
|
||||
def show_generate_xpub(self):
|
||||
seed = Seed()
|
||||
seed = Seed(wordlist=self.settings.wordlist)
|
||||
|
||||
# If there is a saved seed, ask to use saved seed
|
||||
if self.storage.num_of_saved_seeds() > 0:
|
||||
@@ -487,7 +487,7 @@ class Controller(Singleton):
|
||||
qr_xpub_type = self.settings.qr_xpub_type
|
||||
|
||||
self.signing_tools_view.draw_modal(["Generating xPub QR ..."])
|
||||
e = EncodeQR(seed_phrase=seed.mnemonic_list, passphrase=seed.passphrase, derivation=self.settings.derivation, network=self.settings.network, policy=self.settings.script_policy, qr_type=qr_xpub_type, qr_density=self.settings.qr_density)
|
||||
e = EncodeQR(seed_phrase=seed.mnemonic_list, passphrase=seed.passphrase, derivation=self.settings.derivation, network=self.settings.network, policy=self.settings.script_policy, qr_type=qr_xpub_type, qr_density=self.settings.qr_density, wordlist=self.settings.wordlist)
|
||||
|
||||
while e.totalParts() > 1:
|
||||
image = e.nextPartImage(240,240,2)
|
||||
@@ -506,7 +506,7 @@ class Controller(Singleton):
|
||||
### Sign Transactions
|
||||
|
||||
def show_sign_transaction(self):
|
||||
seed = Seed()
|
||||
seed = Seed(wordlist=self.settings.wordlist)
|
||||
used_saved_seed = False
|
||||
|
||||
# reusable qr scan function
|
||||
@@ -514,7 +514,7 @@ class Controller(Singleton):
|
||||
# Scan QR using Camera
|
||||
self.menu_view.draw_modal(["Initializing Camera"])
|
||||
self.camera.start_video_stream_mode(resolution=(480, 480), framerate=12, format="rgb")
|
||||
decoder = DecodeQR()
|
||||
decoder = DecodeQR(wordlist=self.settings.wordlist)
|
||||
|
||||
def live_preview(camera, decoder, scan_text):
|
||||
while True:
|
||||
@@ -714,7 +714,7 @@ class Controller(Singleton):
|
||||
|
||||
# Display Animated QR Code
|
||||
self.menu_view.draw_modal(["Generating PSBT QR ..."])
|
||||
e = EncodeQR(psbt=trimmed_psbt, qr_type=self.settings.qr_psbt_type, qr_density=self.settings.qr_density)
|
||||
e = EncodeQR(psbt=trimmed_psbt, qr_type=self.settings.qr_psbt_type, qr_density=self.settings.qr_density, wordlist=self.settings.wordlist)
|
||||
while True:
|
||||
image = e.nextPartImage(240,240,1)
|
||||
View.DispShowImage(image)
|
||||
|
||||
@@ -2,11 +2,9 @@ from embit import bip39
|
||||
from embit.bip39 import mnemonic_to_bytes, mnemonic_from_bytes
|
||||
import unicodedata
|
||||
|
||||
from seedsigner.models import Settings
|
||||
|
||||
|
||||
|
||||
def calculate_checksum(partial_mnemonic: list):
|
||||
def calculate_checksum(partial_mnemonic: list, wordlist):
|
||||
# Provide 11- or 23-word mnemonic, returns complete mnemonic w/checksum
|
||||
if len(partial_mnemonic) not in [11, 23]:
|
||||
raise Exception("Pass in a 11- or 23-word mnemonic")
|
||||
@@ -16,7 +14,7 @@ def calculate_checksum(partial_mnemonic: list):
|
||||
mnemonic_copy.append("abandon")
|
||||
|
||||
# Ignores the final checksum word and recalcs
|
||||
mnemonic_bytes = bip39.mnemonic_to_bytes(unicodedata.normalize("NFKD", " ".join(mnemonic_copy)), ignore_checksum=True, wordlist=Settings.get_instance().wordlist)
|
||||
mnemonic_bytes = bip39.mnemonic_to_bytes(unicodedata.normalize("NFKD", " ".join(mnemonic_copy)), ignore_checksum=True, wordlist=wordlist)
|
||||
|
||||
# Return as a list
|
||||
return bip39.mnemonic_from_bytes(mnemonic_bytes).split()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from .qr_type import *
|
||||
from .seed import *
|
||||
from .decode_qr import *
|
||||
from .encode_qr import *
|
||||
from .settings import *
|
||||
from .psbt_parser import *
|
||||
from .seed import *
|
||||
from .seed_storage import *
|
||||
from .seed_storage import *
|
||||
from .settings import *
|
||||
@@ -8,7 +8,6 @@ from binascii import a2b_base64, b2a_base64
|
||||
from seedsigner.helpers.ur2.ur_decoder import URDecoder
|
||||
from seedsigner.helpers.bcur import (cbor_decode, bc32decode)
|
||||
from seedsigner.models.qr_type import QRType
|
||||
from seedsigner.models.settings import Settings
|
||||
from seedsigner.models.seed import Seed
|
||||
|
||||
###
|
||||
@@ -25,11 +24,18 @@ class DecodeQR:
|
||||
self.specter_qr = SpecterDecodePSBTQR() # Specter Desktop PSBT QR base64 decoder
|
||||
self.legacy_ur = LegacyURDecodeQR() # UR Legacy decoder
|
||||
self.base64_qr = Base64DecodeQR() # Single Segments Base64
|
||||
self.seedqr = SeedQR()
|
||||
self.wordlist = None
|
||||
|
||||
for key, value in kwargs.items():
|
||||
if key == "qr_type":
|
||||
self.qr_type = value
|
||||
if key == "wordlist":
|
||||
self.wordlist = value
|
||||
|
||||
if self.wordlist == None:
|
||||
raise Exception('Wordlist Required')
|
||||
|
||||
self.seedqr = SeedQR(wordlist=self.wordlist)
|
||||
|
||||
def addImage(self, image):
|
||||
qr_str = DecodeQR.QR2Str(image)
|
||||
@@ -42,7 +48,7 @@ class DecodeQR:
|
||||
if qr_str == None:
|
||||
return DecodeQRStatus.FALSE
|
||||
|
||||
qr_type = DecodeQR.SegmentType(qr_str)
|
||||
qr_type = DecodeQR.SegmentType(qr_str, wordlist=self.wordlist)
|
||||
|
||||
if self.qr_type == None:
|
||||
self.qr_type = qr_type
|
||||
@@ -188,27 +194,29 @@ class DecodeQR:
|
||||
return barcode.data.decode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def SegmentType(s):
|
||||
|
||||
_WORDLIST = Settings.get_instance().wordlist
|
||||
_4LETTER_WORDLIST = [word[:4].strip() for word in _WORDLIST]
|
||||
def SegmentType(s, wordlist=None):
|
||||
|
||||
# PSBT
|
||||
if re.search("^UR:CRYPTO-PSBT/", s, re.IGNORECASE):
|
||||
return QRType.PSBTUR2
|
||||
elif re.search(r'^p(\d+)of(\d+) ', s, re.IGNORECASE):
|
||||
return QRType.PSBTSPECTER
|
||||
elif re.search("^UR:BYTES/", s, re.IGNORECASE):
|
||||
return QRType.PSBTURLEGACY
|
||||
elif re.search(r'\d{48,96}', s):
|
||||
elif DecodeQR.isBase64PSBT(s):
|
||||
return QRType.PSBTBASE64
|
||||
|
||||
_4LETTER_WORDLIST = [word[:4].strip() for word in wordlist]
|
||||
|
||||
# Seed
|
||||
if re.search(r'\d{48,96}', s):
|
||||
return QRType.SEEDSSQR
|
||||
elif all(x in _WORDLIST for x in s.strip().split(" ")):
|
||||
elif all(x in wordlist for x in s.strip().split(" ")):
|
||||
# checks if all words in list are in bip39 word list
|
||||
return QRType.SEEDMNEMONIC
|
||||
elif all(x in _4LETTER_WORDLIST for x in s.strip().split(" ")):
|
||||
# checks if all 4 letter words are in list are in 4 letter bip39 word list
|
||||
return QRType.SEED4LETTERMNEMONIC
|
||||
elif DecodeQR.isBase64PSBT(s):
|
||||
return QRType.PSBTBASE64
|
||||
else:
|
||||
return QRType.INVALID
|
||||
|
||||
@@ -422,16 +430,19 @@ class Base64DecodeQR:
|
||||
|
||||
class SeedQR:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, wordlist=None):
|
||||
self.total_segments = 1
|
||||
self.collected_segments = 0
|
||||
self.complete = False
|
||||
self.seed_phrase = []
|
||||
self.wordlist = wordlist
|
||||
|
||||
if self.wordlist == None:
|
||||
raise Exception('Wordlist Required')
|
||||
|
||||
def add(self, segment, qr_type=QRType.SEEDSSQR):
|
||||
|
||||
_WORDLIST = Settings.get_instance().wordlist
|
||||
_4LETTER_WORDLIST = [word[:4].strip() for word in _WORDLIST]
|
||||
_4LETTER_WORDLIST = [word[:4].strip() for word in self.wordlist]
|
||||
|
||||
if qr_type == QRType.SEEDSSQR:
|
||||
|
||||
@@ -442,7 +453,7 @@ class SeedQR:
|
||||
num_words = int(len(segment) / 4)
|
||||
for i in range(0, num_words):
|
||||
index = int(segment[i * 4: (i*4) + 4])
|
||||
word = _WORDLIST[index]
|
||||
word = self.wordlist[index]
|
||||
self.seed_phrase.append(word)
|
||||
if len(self.seed_phrase) > 0:
|
||||
if self.is1224Phrase() == False:
|
||||
@@ -459,7 +470,7 @@ class SeedQR:
|
||||
|
||||
try:
|
||||
# embit mnemonic code to validate
|
||||
seed = Seed(segment, passphrase="")
|
||||
seed = Seed(segment, passphrase="", wordlist=self.wordlist)
|
||||
if not seed:
|
||||
# seed is not valid, return invalid
|
||||
return DecodeQRStatus.INVALID
|
||||
@@ -478,10 +489,10 @@ class SeedQR:
|
||||
sl = segment.strip().split(" ")
|
||||
words = []
|
||||
for s in sl:
|
||||
words.append(_WORDLIST[_4LETTER_WORDLIST.index(s)])
|
||||
words.append(self.wordlist[_4LETTER_WORDLIST.index(s)])
|
||||
|
||||
# embit mnemonic code to validate
|
||||
seed = Seed(words, passphrase="")
|
||||
seed = Seed(words, passphrase="", wordlist=self.wordlist)
|
||||
if not seed:
|
||||
# seed is not valid, return invalid
|
||||
return DecodeQRStatus.INVALID
|
||||
|
||||
@@ -8,7 +8,6 @@ from seedsigner.helpers.ur2.ur import UR
|
||||
from seedsigner.helpers.bcur import (bc32encode, cbor_encode, bcur_encode)
|
||||
from seedsigner.helpers.qr import QR
|
||||
from seedsigner.models.qr_type import QRType, EncodeQRDensity
|
||||
from seedsigner.models.settings import Settings
|
||||
from seedsigner.models.seed import Seed
|
||||
|
||||
###
|
||||
@@ -21,7 +20,6 @@ class EncodeQR:
|
||||
WORDLIST = None
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
EncodeQR.WORDLIST = Settings.get_instance().wordlist
|
||||
self.psbt = None
|
||||
self.seed_phrase = None
|
||||
self.passphrase = None
|
||||
@@ -31,6 +29,7 @@ class EncodeQR:
|
||||
self.qr_type = None
|
||||
self.qr_density = None
|
||||
self.qr = QR()
|
||||
self.wordlist = None
|
||||
|
||||
for key, value in kwargs.items():
|
||||
if key == "psbt":
|
||||
@@ -49,6 +48,11 @@ class EncodeQR:
|
||||
self.qr_type = value
|
||||
elif key == "qr_density":
|
||||
self.qr_density = value
|
||||
elif key == "wordlist":
|
||||
self.wordlist = value
|
||||
|
||||
if self.wordlist == None:
|
||||
raise Exception('Wordlist Required')
|
||||
|
||||
if self.qr_type == None:
|
||||
raise Exception('Encoder Type Required')
|
||||
@@ -217,7 +221,7 @@ class XPubQR:
|
||||
self.sent_complete = False
|
||||
|
||||
self.network = network
|
||||
self.seed = Seed(self.seed_phrase, self.passphrase)
|
||||
self.seed = Seed(mnemonic=self.seed_phrase, passphrase=self.passphrase, wordlist=self.wordlist)
|
||||
self.root = bip32.HDKey.from_seed(self.seed.seed, version=NETWORKS[self.network]["xprv"])
|
||||
self.fingerprint = self.root.child(0).fingerprint
|
||||
self.bip48_xprv = self.root.derive(self.derivation)
|
||||
|
||||
@@ -2,8 +2,6 @@ from embit import psbt, script, ec, bip32, bip39
|
||||
from embit.networks import NETWORKS
|
||||
from io import BytesIO
|
||||
|
||||
from seedsigner.models.seed import Seed
|
||||
|
||||
class PSBTParser():
|
||||
|
||||
def __init__(self, p, seed: Seed, network="main"):
|
||||
|
||||
@@ -9,11 +9,10 @@ class Seed:
|
||||
self._valid = False
|
||||
self.passphrase = passphrase
|
||||
self.mnemonic = mnemonic
|
||||
if wordlist == None:
|
||||
from seedsigner.models.settings import Settings
|
||||
self._wordlist = Settings.get_instance().wordlist
|
||||
else:
|
||||
self._wordlist = wordlist
|
||||
self.wordlist = wordlist
|
||||
|
||||
if self.wordlist == None:
|
||||
raise Exception('Wordlist Required')
|
||||
|
||||
self._valid = self._generate_seed()
|
||||
self._init_complete = True
|
||||
|
||||
@@ -665,7 +665,7 @@ class SeedToolsView(View):
|
||||
###
|
||||
|
||||
def display_last_word(self, partial_seed_phrase) -> list:
|
||||
finalseed = mnemonic_generation.calculate_checksum(partial_seed_phrase)
|
||||
finalseed = mnemonic_generation.calculate_checksum(partial_seed_phrase, wordlist=self.controller.settings.wordlist)
|
||||
last_word = finalseed[-1]
|
||||
|
||||
self.draw.rectangle((0, 0, View.canvas_width, View.canvas_height), outline=0, fill=0)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import pytest
|
||||
from mock import MagicMock
|
||||
from seedsigner.models.seed import Seed
|
||||
from seedsigner.models.decode_qr import DecodeQR, QRType, DecodeQRStatus
|
||||
from seedsigner.models.psbt_parser import PSBTParser
|
||||
from seedsigner.models.seed import Seed
|
||||
from embit import psbt, bip39
|
||||
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ def test_calculate_checksum():
|
||||
"""
|
||||
# Test mnemonics from https://iancoleman.io/bip39/
|
||||
partial_mnemonic = "crawl focus rescue cable view pledge rather dinner cousin unfair day"
|
||||
mnemonic = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "))
|
||||
mnemonic = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "), wordlist=bip39.WORDLIST)
|
||||
assert bip39.mnemonic_is_valid(" ".join(mnemonic))
|
||||
|
||||
partial_mnemonic = "bubble father debate ankle injury fence mesh evolve section wet coyote violin pyramid flower rent arrow round clutch myth safe base skin mobile"
|
||||
mnemonic = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "))
|
||||
mnemonic = mnemonic_generation.calculate_checksum(partial_mnemonic.split(" "), wordlist=bip39.WORDLIST)
|
||||
assert bip39.mnemonic_is_valid(" ".join(mnemonic))
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import pytest
|
||||
from mock import MagicMock
|
||||
from seedsigner.models.psbt_parser import PSBTParser
|
||||
from embit import psbt
|
||||
|
||||
def test_sig_count():
|
||||
print("test")
|
||||
|
||||
Reference in New Issue
Block a user