From 2fcd95bd3d5dad6cf5225c8b3a1d27cd49f99e99 Mon Sep 17 00:00:00 2001 From: Zachary Watson Date: Thu, 19 Mar 2026 14:04:35 -0500 Subject: [PATCH] ADD: BIP47 (m/47') derivation path support for message signing Enable message signing with BIP47 identity keys by adding 47h to the recognized purpose bytes in parse_derivation_path(). This unlocks Auth47 challenge signing, PayNym claim proofs, and BIP47 identity message signing through the existing signmessage flow. The signing primitive (embit_utils.sign_message) already accepts any derivation path. This change updates the parser so BIP47 paths pass the policy gates in SeedSignMessageStartView and SeedSignMessageConfirmAddressView instead of redirecting to NotYetImplementedView. --- src/seedsigner/helpers/embit_utils.py | 1 + tests/test_flows_seed.py | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/seedsigner/helpers/embit_utils.py b/src/seedsigner/helpers/embit_utils.py index 805537994..7f76f24e7 100644 --- a/src/seedsigner/helpers/embit_utils.py +++ b/src/seedsigner/helpers/embit_utils.py @@ -143,6 +143,7 @@ def parse_derivation_path(derivation_path: str) -> dict: lookups = { "script_types": { "44h": SettingsConstants.LEGACY_P2PKH, + "47h": SettingsConstants.LEGACY_P2PKH, "49h": SettingsConstants.NESTED_SEGWIT, "84h": SettingsConstants.NATIVE_SEGWIT, "86h": SettingsConstants.TAPROOT, diff --git a/tests/test_flows_seed.py b/tests/test_flows_seed.py index e5f2c3469..bf993f166 100644 --- a/tests/test_flows_seed.py +++ b/tests/test_flows_seed.py @@ -498,6 +498,7 @@ def test_transcribe_seedqr_screensaver_startable_status(self): class TestMessageSigningFlows(FlowTest): MAINNET_DERIVATION_PATH = "m/84h/0h/0h/0/0" TESTNET_DERIVATION_PATH = "m/84h/1h/0h/0/0" + BIP47_DERIVATION_PATH = "m/47h/0h/0h/0/0" CUSTOM_DERIVATION_PATH = "m/99h/0/0" SHORT_MESSAGE = "I attest that I control this bitcoin address blah blah blah" NO_WHITESPACE_MESSAGE = """{"height":841407,"lightning_bolt12":"lno1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}""" @@ -534,6 +535,10 @@ def load_no_whitespace_message_into_decoder(self, view: View): self.load_signmessage_into_decoder(view, self.MAINNET_DERIVATION_PATH, self.NO_WHITESPACE_MESSAGE) + def load_bip47_message_into_decoder(self, view: View): + self.load_signmessage_into_decoder(view, self.BIP47_DERIVATION_PATH, self.SHORT_MESSAGE) + + def load_custom_derivation_into_decoder(self, view: View): self.load_signmessage_into_decoder(view, self.CUSTOM_DERIVATION_PATH, self.SHORT_MESSAGE) @@ -636,6 +641,28 @@ def test_sign_message_flow(self): ]) + def test_sign_message_bip47_flow(self): + """ + Should sign a message using a BIP47 derivation path (m/47h/0h/0h/0/0). + This enables Auth47 challenge signing and PayNym claim signing. + """ + self.settings.set_value(SettingsConstants.SETTING__MESSAGE_SIGNING, SettingsConstants.OPTION__ENABLED) + + self.run_sequence([ + FlowStep(MainMenuView, button_data_selection=MainMenuView.SCAN), + FlowStep(scan_views.ScanView, before_run=self.load_bip47_message_into_decoder), + FlowStep(seed_views.SeedSignMessageStartView, is_redirect=True), + FlowStep(seed_views.SeedSelectSeedView, button_data_selection=seed_views.SeedSelectSeedView.SCAN_SEED), + FlowStep(scan_views.ScanView, before_run=self.load_seed_into_decoder), + FlowStep(seed_views.SeedFinalizeView, button_data_selection=seed_views.SeedFinalizeView.FINALIZE), + FlowStep(seed_views.SeedOptionsView, is_redirect=True), + FlowStep(seed_views.SeedSignMessageConfirmMessageView, before_run=self.inject_mesage_as_paged_message, screen_return_value=0), + FlowStep(seed_views.SeedSignMessageConfirmAddressView, screen_return_value=0), + FlowStep(seed_views.SeedSignMessageSignedMessageQRView, screen_return_value=0), + FlowStep(MainMenuView), + ]) + + def test_sign_message_network_mismatch_flow(self): """ Should redirect to NetworkMismatchErrorView if a message's derivation path network doesn't match the current network.