Support BIP48 multisig derivation paths for message signing#874
Support BIP48 multisig derivation paths for message signing#874AusDavo wants to merge 2 commits into
Conversation
parse_derivation_path() previously raised "Not implemented" for m/48' paths, blocking message signing for multisig keys. The underlying sign_message() function already works with any derivation path — the restriction was only in the path parser. This extends parse_derivation_path() to handle BIP48 multisig paths by mapping the script type index at the 4th level (1h = nested segwit, 2h = native segwit) to the correct SettingsConstants value. The address shown during signing confirmation is the individual cosigner's single-key address, which is sufficient for key ownership verification. Fixes SeedSigner#519
|
@newtonick @kdmukai — this is a small change that unblocks message signing for multisig derivation paths (fixes #519). Happy to address any feedback. |
|
Wow - well done @AusDavo - this is an awesome enhancement - would really elevate some of the custody services we provide via the SeedSigner platform if something like this could get merged. |
This is a project run by volunteers who are contributing in their free time. By creating a PR you are inherently asking for contributors' attention. We see there's an open PR. Obviously we already know it wants eyeballs. But if you look at the dates of other PRs, you'll see that PR review is almost always a very slow process (see my own PR #747, for example). So I consider it rude to directly "@" reference any of us just to call for our attention. Notice that your "@" message adds nothing that is not already stated in the PR description. Reserve "@" call outs for when there are specific questions that the targeted person is the best resource and the discussion may be stalled until they weigh in. If this sounds harsh, well, understand that I am fiercely protective of our volunteers' time. If you had just tagged me, I would have been mildly annoyed. But tagging Nick or anyone else in this way simply because of impatience aggravates me. Setting this all aside, more specific / constructive comments will follow. |
|
Any new feature or altered behavior needs new tests. In the PR description you list a series of tests. These should be written into actual test cases and included in this PR. Since those tests are still yet to be included, this PR should be in the DRAFT state. DRAFT signals that it is not yet ready for review. This is an act of courtesy so that others will not spend time reviewing a PR that is not yet complete. This PR would also ideally include a new |
|
It would help to list some of the services that use this sort of verification. I have not encountered this approach to verification for a multisig key, but I have not kept up with all the services out there. I'm only familiar with Unchained which just verifies via a wallet export json format (or direct usb hww connection) that is just checking for, I believe, an xpub match. |
|
Thanks for the feedback, and apologies for the unnecessary @-mentions — I understand, and I'll keep that in mind going forward. |
Unit tests: 13 BIP48 vectors for parse_derivation_path() covering native segwit (2h), nested segwit (1h), and unrecognised script type fallback across mainnet/testnet/regtest with change flag variations. FlowTest: end-to-end message signing flow for both BIP48 native segwit and nested segwit derivation paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Thanks for the guidance on test expectations — really helpful for getting up to speed with the project's standards. Tests added in f5ca0a3:
|
I'll add weight to the use case. I was asked by an auditor to prove ownership and control of a fund's bitcoin holdings. The suggested mechanism was signing a message with the associated keys. This is not possible by default with a multisig wallet structure. I built an image with AusDavo's patch as was able to use it to sign the challenge by the CertainKey service and they were able to provide a report for my auditor. (I was previously getting a System Error: embit_utils.py, 130, in parse_derivation_path Not implemented"; this patch fixes that error) Stories in discussion groups here are pointing to more auditors now demanding cryptographic proof of control. |
|
Just adding some context from our side as a custody services provider in Australia using the combination of SeedSigner, SeedHammer and SeedSleeve in real-world multisig deployments for SMSF (the Australian equivalent of a pension fund or retirement savings account). Fully appreciate that this is a volunteer-run project and that review bandwidth is limited - that's why every SeedSigner we get made and sell sees a portion of our revenue sent as a donation to the project. Not trying to rush anything here, just explaining why this truly matters now operationally. At mineracks, we help clients take and maintain secure physical possession of their keys. Most of those setups are BIP48 native segwit multisig. Increasingly, auditors and fund administrators are requiring cryptographic proof of control — i.e., signing a challenge message — rather than relying solely on wallet exports or screenshots that have historically been sufficient. In singlesig that’s easy. In multisig, it’s awkward unless the device supports signing at the BIP48 derivation path. That parser restriction has been one of the few friction points when deploying SeedSigner in structured custody environments. From our perspective, what’s nice about this PR is that it doesn’t introduce new signing behavior — it just removes a path restriction. The device still signs with the derived single-key path, and verification can be done independently by matching the pubkey to the descriptor xpub. That aligns well with how cosigner verification already works. We’re seeing a broader trend of auditors explicitly asking for cryptographic challenge signatures to demonstrate continued key control. Having parity between singlesig and multisig derivations makes SeedSigner much easier to use in those contexts. The alternative more auditors are offering is to insecurely share xpubs and blockchain addresses that then significantly impact on safety and security for key holders. Appreciate the thoughtful review process and the emphasis on proper tests and flow coverage — that rigor is exactly why we’re comfortable using and recommending SeedSigner in custody settings where it really counts. |
|
Thank you to everyone above for understanding my reaction, adding the tests, and providing the fuller real-world context. I have not yet reviewed the changes but I'm onboard with a basic concept ACK. And since I have no privileges in our repo, here I will invoke necessary "@" mentions: @newtonick or @SeedSigner can you approve the workflow to run the CI tests on this PR? |

Summary
raise Exception("Not implemented")block form/48'paths inparse_derivation_path()1h= nested segwit,2h= native segwit)sign_message()function already supports any derivation path — this was purely a parser restrictionContext
Message signing currently only works for single-sig derivation paths (m/44', m/49', m/84', m/86'). Attempting to sign with a multisig path like
m/48'/0'/0'/2'/0/0raises "Not implemented" fromparse_derivation_path().This is needed for key ownership and control verification in multisig setups — e.g. proving to an auditor that you control a specific cosigner key in a multisig wallet, without needing to construct a transaction.
The address displayed on the SeedSigner confirmation screen will be the individual cosigner's single-key address (not the full multisig address, which would require all cosigner xpubs). This is sufficient for verification — the auditor can independently confirm the signing public key matches an xpub in the multisig wallet descriptor.
Tested on a Pi Zero W with a custom build, signing at
m/48'/0'/0'/2'/0/0and verifying the signature externally.Fixes #519
Test plan
m/48'/0'/0'/2'/0/0(native segwit multisig) — should succeedm/48'/0'/0'/1'/0/0(nested segwit multisig) — should succeedm/84'/0'/0'/0/0(single-sig) — should still work as before