BitBoxSwiss / bitbox02-firmware

Firmware code of the BitBox02 hardware wallet
https://bitbox.swiss/bitbox02
Apache License 2.0
217 stars 81 forks source link

Not able to sign timelocked PSBT via HWI without wallet registration #1126

Closed dr-orlovsky closed 9 months ago

dr-orlovsky commented 10 months ago

I know it is a feature and not a bug - but maybe there is some way to solve that, since it works for non-timelocked multisigs.

What I get is

BitBox02 attestation check PASSED
{"error": "Input or change script type not recognized.", "code": -7}

Descriptor is

wsh(or_i(
    multi(3,
            [362a0411/48h/0h/0h/2h]xpub6DnLtr7gtXNFZJcDLni2ezMrtaQRLDQTvW8rdyWn33pWUymmeocvNTaQyBZWEYPosEJehmfsbe2A8Tg9jS2YwKWjiwAkuRZ6vzVjFs52Juf/<0;1>/*,
            [dbd96ebc/48h/0h/0h/2h]xpub6F8HEj83d5th3okdS2eJ4r1eNmcHw7VtLUzqnMRq6KNr7bajS8hKUUUzxjAvRMH5jdbzpR3qfBRhKfsTpBYFu6Pba1UjKLZN95sB8DEo3Ja/<0;1>/*,
            [feda4d11/48h/0h/0h/2h]xpub6EgHtjXXbGmVHEuKw49fke3iqXoo5vTx2bjHaTkE9ozbo25h7eU4SuNUSAGmNnw2rD2MPxm7e5pzCxSgpyJfuzc7QwHbsNBe8vfqnEjhGW6/<0;1>/*
    ), 
    andor(multi(1,
            [362a0411/48h/0h/2h/2h]xpub6EynZEts5RmopiuAcaiF1vWnGHmNxvgrqmvvXGtyCJVRdZGP7bpRcGt7xVQUfWRQAQk8wNYdnMg5bxr38HkGYMQCkVxorUMCvphZy5HgPwU/<0;1>/*,
            [dbd96ebc/48h/0h/2h/2h]xpub6EgSRG4jge1K5rdMQFe24VSHXSYzjzFF7FvPWAkz7vWs4E7abo1i34AcyZsHhXwvpDzjEv2MqTMXBcATVfH2En3YJfpvgpE25JHRxF87Sus/<0;1>/*,
            [feda4d11/48h/0h/2h/2h]xpub6DkehEoVVoixuc9aqWdPvUAFy9zWbdigqcF6DPQThESgkJbU1TCS2iSXjB7BP2ToGgU5yErX2Ev1fp88b81NW3UGzyHdV3AsFGw7s2KEQRi/<0;1>/*),
            older(4255897),
    and_v(v:multi(2,
            [362a0411/48h/0h/1h/2h]xpub6DnFr7Ff8NbjcdrN2awC164Dgz64JHFCYF6YK18fqVqjZFUwQPQRyz38LR4C6QzLkHfbnttUPqWwL4FEvoA4uzS86YssWN8emQZjXiyuPtz/<0;1>/*,
            [dbd96ebc/48h/0h/1h/2h]xpub6EyjEwQ2rzHYttiTJUPW8sJxpnQhGDJCnBjK73ZGChK512fKfcdGQWhPoagrYhdJR8N1NcPKXwaTyh8tTLkj7Ej3bsZpYBNFiB2ZRQ8NyDa/<0;1>/*,
            [feda4d11/48h/0h/1h/2h]xpub6EmMNcU5bZKw6Xq1wbjTwhnVkG14vjhA9mvjii2Mk3QW8byAYahqJm7qYJaT2gAANviWgSu7xs7n2rn4ndnZGbhnDWbZdT1V2PuU6uaDyMe/<0;1>/*),
            after(1735689600)))))

PSBT is

cHNidP8BAFICAAAAASXBTaCNPS6qvR5GCFdjXDS5axe2fUfqsvpGV/ZGS5G7AAAAAAD9////Ac0BAAAAAAAAFgAUYLlCcpDeQDVWvogTYvXdl9WrLevmNAsATwEEiLIeBB3mM8yAAAACoI52PAvM15x1l2UN9/FXJ1IKkM8sTrhIu2uJMGDKJmwDE3k+Yo8GntAiSwsBlAqyBludB7E+uiinHVK8IK7yRWUU/tpNETAAAIAAAACAAgAAgAIAAIBPAQSIsh4EIaqojYAAAALk8s98JdgslIjNHNg8Q8LUKnEGVaOsMYq+wMpABV+IlANCRGGG/oLgrr1xGKAIWJlRlPRXXHctYlfSlNVIu+Od5xQ2KgQRMAAAgAAAAIABAACAAgAAgE8BBIiyHgQh3uopgAAAApYWSvGyPwA478hFMZASbs4ZfN3HX8PkxsLzBD6QaJstAlLRPfVyqA0UaidITf+u7AN7hmPc4y1X7KpUDNeUQs9lFDYqBBEwAACAAAAAgAAAAIACAACATwEEiLIeBJu55nKAAAACgCw2Cr9w/jZPXqziUxOUJXcG4Hok1qov9oILsTfgVe0CwJDpmbcmGLgmbZppKOR3wq9XR8o6QpristcopneM37MU/tpNETAAAIAAAACAAAAAgAIAAIBPAQSIsh4EnBItW4AAAAJdVTZLqFVQZT1XoBg6uPe4iiZM2vO8asAQTLBkY2/ouwN0HIvYJbHLPy2agVHcc1o3W1R1DxKohzKR3fWf4hCMMRTb2W68MAAAgAAAAIACAACAAgAAgE8BBIiyHgSnmHGGgAAAAkIScKz40q7/ldPXo8XTk7kIJ9Zbyuacsyct4Hd5tyjeAuJkwomsZNtHWOnOXqmjhG7I/b5qFB4EkEsDdqJru9FoFP7aTREwAACAAAAAgAEAAIACAACATwEEiLIeBMSg3YKAAAAC+pqzLMN+sCRIyUSy6joi+vAmcNT79y7TWQ+f9TGt9a8DGZZoE4Q2wT7if6PAXroDLhQX3uLnj4UUsQH6qlhRPBsU29luvDAAAIAAAACAAQAAgAIAAIBPAQSIsh4ExMMxU4AAAAKRSY8cmwKapqXPRnfaeUV7W9OGCzUBkTOeDGdahxaZYgPJY4Fps53xfoH8ZBIdGJmrhwSDiTn9hwyioVpcW6FKnhQ2KgQRMAAAgAAAAIACAACAAgAAgE8BBIiyHgTYsCYSgAAAAnCwHzlI8niTyPXJSkHhJ8tY8v95FLCbGFRCpnWsUyQTApjw5Us+xgoFIwrwFlHZi0J5sAjBEsHs3QKEeqY9h2LwFNvZbrwwAACAAAAAgAAAAIACAACAEPwJTXlDaXRhZGVsADYqBBEGYml0Ym94EPwJTXlDaXRhZGVsANvZbrwHbGVkZ2VycxD8CU15Q2l0YWRlbAD+2k0RB2xlZGdlcngAAQBeAgAAAAHbNEY1Pt0NXS2iVuHx1e1LQAEyKwr0/6B9kOyHrlMIAwAAAAAA/f///wFMAwAAAAAAACIAIMqifl87hvgRLE9CZObEFnU6ivXsg3CsyQLlblDVaKpy5jQLAAEBK0wDAAAAAAAAIgAgyqJ+XzuG+BEsT0Jk5sQWdTqK9eyDcKzJAuVuUNVoqnIBAwQBAAAAAQX9TAFjUyECe71bmLmw7veVKqQIGsIfOc+8osIePjeqnefJIv2URochA8FAk81unqFF2NZ4i/LvZqansSFSQCWzMUgM1/Pd7ifSIQIVl1AlMNr7+xEoGf0dgznJhdJV14l8S3ptvgvbf3+e5lOuZ1EhAputjaPGw3LMNNYt96mb7gyS00KaVTK3woGpoNkC2enfIQPtLK6QVtwsjQeImna88nk6WfgBpPreFhmn3ZuFYe8DmiEDUCFulwOwZ+YE+PpZ4idGXOAH9jL/+SRqTwGspRbIWttTrmRSIQMralk4UO+D7915SCZZgA2GCMSiNf23JRdUPfwP+GBDbiEDj+cYks0De3c9rsVKWrhwfP+tJVhhPrq3eR6GyfHrcXIhAx7ERqswS9MXz2iIHeFw59foRkY3Vu+KI+rUaky184rzU68EgIV0Z7FnA5nwQLJoaCIGAhWXUCUw2vv7ESgZ/R2DOcmF0lXXiXxLem2+C9t/f57mHP7aTREwAACAAAAAgAAAAIACAACAAAAAAAAAAAAiBgJ7vVuYubDu95UqpAgawh85z7yiwh4+N6qd58ki/ZRGhxw2KgQRMAAAgAAAAIAAAACAAgAAgAAAAAAAAAAAIgYCm62No8bDcsw01i33qZvuDJLTQppVMrfCgamg2QLZ6d8cNioEETAAAIAAAACAAgAAgAIAAIAAAAAAAAAAACIGAx7ERqswS9MXz2iIHeFw59foRkY3Vu+KI+rUaky184rzHP7aTREwAACAAAAAgAEAAIACAACAAAAAAAAAAAAiBgMralk4UO+D7915SCZZgA2GCMSiNf23JRdUPfwP+GBDbhw2KgQRMAAAgAAAAIABAACAAgAAgAAAAAAAAAAAIgYDUCFulwOwZ+YE+PpZ4idGXOAH9jL/+SRqTwGspRbIWtsc/tpNETAAAIAAAACAAgAAgAIAAIAAAAAAAAAAACIGA4/nGJLNA3t3Pa7FSlq4cHz/rSVYYT66t3kehsnx63FyHNvZbrwwAACAAAAAgAEAAIACAACAAAAAAAAAAAAiBgPBQJPNbp6hRdjWeIvy72amp7EhUkAlszFIDNfz3e4n0hzb2W68MAAAgAAAAIAAAACAAgAAgAAAAAAAAAAAIgYD7SyukFbcLI0HiJp2vPJ5Oln4AaT63hYZp92bhWHvA5oc29luvDAAAIAAAACAAgAAgAIAAIAAAAAAAAAAAAAA

Txid which is being spent: bb914b46f65746fab2ea477db6176bb9345c635708461ebdaa2e3d8da04dc125:0

Yes, that's mainnet. I do write code and debug it only on mainnet, not testnet. Because that is the way.

And yes, I know, that is a crazy descriptor. Here is what it means: Screenshot from 2023-09-08 00-19-39 Screenshot from 2023-09-08 00-20-22

For debug purposes, here is interpretation of PSBT content:

psbt_version: V0
tx_version: 2
fallback_locktime: 734438
inputs:
- index: 0
  previous_outpoint: bb914b46f65746fab2ea477db6176bb9345c635708461ebdaa2e3d8da04dc125:0
  sequence_number: 4294967293
  required_time_locktime: null
  required_height_locktime: null
  non_witness_utxo:
    version: 2
    lock_time: 734438
    input:
    - previous_output: 030853ae87ec907da0fff40a2b3201404bedd5f1e156a22d5d0ddd3e354634db:0
      script_sig: ''
      sequence: 4294967293
      witness: []
    output:
    - value: 844
      script_pubkey: 0020caa27e5f3b86f8112c4f4264e6c416753a8af5ec8370acc902e56e50d568aa72
  witness_utxo:
    value: 844
    script_pubkey: 0020caa27e5f3b86f8112c4f4264e6c416753a8af5ec8370acc902e56e50d568aa72
  partial_sigs: {}
  sighash_type:
    inner: 1
  redeem_script: null
  witness_script: 635321027bbd5b98b9b0eef7952aa4081ac21f39cfbca2c21e3e37aa9de7c922fd9446872103c14093cd6e9ea145d8d6788bf2ef66a6a7b121524025b331480cd7f3ddee27d221021597502530dafbfb112819fd1d8339c985d255d7897c4b7a6dbe0bdb7f7f9ee653ae675121029bad8da3c6c372cc34d62df7a99bee0c92d3429a5532b7c281a9a0d902d9e9df2103ed2cae9056dc2c8d07889a76bcf2793a59f801a4fade1619a7dd9b8561ef039a210350216e9703b067e604f8fa59e227465ce007f632fff9246a4f01aca516c85adb53ae645221032b6a593850ef83efdd79482659800d8608c4a235fdb72517543dfc0ff860436e21038fe71892cd037b773daec54a5ab8707cffad2558613ebab7791e86c9f1eb717221031ec446ab304bd317cf68881de170e7d7e846463756ef8a23ead46a4cb5f38af353af0480857467b1670399f040b26868
  bip32_derivation:
    021597502530dafbfb112819fd1d8339c985d255d7897c4b7a6dbe0bdb7f7f9ee6:
    - feda4d11
    - m/48'/0'/0'/2'/0/0
    027bbd5b98b9b0eef7952aa4081ac21f39cfbca2c21e3e37aa9de7c922fd944687:
    - 362a0411
    - m/48'/0'/0'/2'/0/0
    029bad8da3c6c372cc34d62df7a99bee0c92d3429a5532b7c281a9a0d902d9e9df:
    - 362a0411
    - m/48'/0'/2'/2'/0/0
    031ec446ab304bd317cf68881de170e7d7e846463756ef8a23ead46a4cb5f38af3:
    - feda4d11
    - m/48'/0'/1'/2'/0/0
    032b6a593850ef83efdd79482659800d8608c4a235fdb72517543dfc0ff860436e:
    - 362a0411
    - m/48'/0'/1'/2'/0/0
    0350216e9703b067e604f8fa59e227465ce007f632fff9246a4f01aca516c85adb:
    - feda4d11
    - m/48'/0'/2'/2'/0/0
    038fe71892cd037b773daec54a5ab8707cffad2558613ebab7791e86c9f1eb7172:
    - dbd96ebc
    - m/48'/0'/1'/2'/0/0
    03c14093cd6e9ea145d8d6788bf2ef66a6a7b121524025b331480cd7f3ddee27d2:
    - dbd96ebc
    - m/48'/0'/0'/2'/0/0
    03ed2cae9056dc2c8d07889a76bcf2793a59f801a4fade1619a7dd9b8561ef039a:
    - dbd96ebc
    - m/48'/0'/2'/2'/0/0
  final_script_sig: null
  final_script_witness: null
  ripemd160_preimages: {}
  sha256_preimages: {}
  hash160_preimages: {}
  hash256_preimages: {}
  tap_key_sig: null
  tap_script_sigs: {}
  tap_scripts: {}
  tap_key_origins: {}
  tap_internal_key: null
  tap_merkle_root: null
  proprietary: {}
  unknown: {}
outputs:
- index: 0
  amount: 461
  script: 001460b9427290de403556be881362f5dd97d5ab2deb
  redeem_script: null
  witness_script: null
  bip32_derivation: {}
  tap_internal_key: null
  tap_tree: null
  tap_key_origins: {}
  proprietary: {}
  unknown: {}
xpub:
  xpub6DkehEoVVoixuc9aqWdPvUAFy9zWbdigqcF6DPQThESgkJbU1TCS2iSXjB7BP2ToGgU5yErX2Ev1fp88b81NW3UGzyHdV3AsFGw7s2KEQRi:
  - feda4d11
  - m/48'/0'/2'/2'
  xpub6DnFr7Ff8NbjcdrN2awC164Dgz64JHFCYF6YK18fqVqjZFUwQPQRyz38LR4C6QzLkHfbnttUPqWwL4FEvoA4uzS86YssWN8emQZjXiyuPtz:
  - 362a0411
  - m/48'/0'/1'/2'
  xpub6DnLtr7gtXNFZJcDLni2ezMrtaQRLDQTvW8rdyWn33pWUymmeocvNTaQyBZWEYPosEJehmfsbe2A8Tg9jS2YwKWjiwAkuRZ6vzVjFs52Juf:
  - 362a0411
  - m/48'/0'/0'/2'
  xpub6EgHtjXXbGmVHEuKw49fke3iqXoo5vTx2bjHaTkE9ozbo25h7eU4SuNUSAGmNnw2rD2MPxm7e5pzCxSgpyJfuzc7QwHbsNBe8vfqnEjhGW6:
  - feda4d11
  - m/48'/0'/0'/2'
  xpub6EgSRG4jge1K5rdMQFe24VSHXSYzjzFF7FvPWAkz7vWs4E7abo1i34AcyZsHhXwvpDzjEv2MqTMXBcATVfH2En3YJfpvgpE25JHRxF87Sus:
  - dbd96ebc
  - m/48'/0'/2'/2'
  xpub6EmMNcU5bZKw6Xq1wbjTwhnVkG14vjhA9mvjii2Mk3QW8byAYahqJm7qYJaT2gAANviWgSu7xs7n2rn4ndnZGbhnDWbZdT1V2PuU6uaDyMe:
  - feda4d11
  - m/48'/0'/1'/2'
  xpub6EyjEwQ2rzHYttiTJUPW8sJxpnQhGDJCnBjK73ZGChK512fKfcdGQWhPoagrYhdJR8N1NcPKXwaTyh8tTLkj7Ej3bsZpYBNFiB2ZRQ8NyDa:
  - dbd96ebc
  - m/48'/0'/1'/2'
  xpub6EynZEts5RmopiuAcaiF1vWnGHmNxvgrqmvvXGtyCJVRdZGP7bpRcGt7xVQUfWRQAQk8wNYdnMg5bxr38HkGYMQCkVxorUMCvphZy5HgPwU:
  - 362a0411
  - m/48'/0'/2'/2'
  xpub6F8HEj83d5th3okdS2eJ4r1eNmcHw7VtLUzqnMRq6KNr7bajS8hKUUUzxjAvRMH5jdbzpR3qfBRhKfsTpBYFu6Pba1UjKLZN95sB8DEo3Ja:
  - dbd96ebc
  - m/48'/0'/0'/2'
proprietary:
  ? prefix: 4d794369746164656c
    subtype: 0
    key: 362a0411
  : 626974626f78
  ? prefix: 4d794369746164656c
    subtype: 0
    key: dbd96ebc
  : 6c656467657273
  ? prefix: 4d794369746164656c
    subtype: 0
    key: feda4d11
  : 6c656467657278
unknown: {}
benma commented 9 months ago

As mentioned in https://github.com/bitcoin-core/HWI/issues/706#issuecomment-1710194139, HWI does not support miniscript policies for the BitBox02, as the policy has to be sent to the BitBox02, and it is not part of PSBT (yet?) and HWI does not currently provide the API to send it separately. The policy that needs to be sent to the BitBox02 is following this BIP.

The Rust BitBox02 library on the other hand, which is being integrated into Rust async-hwi, does support miniscript policies already and could be a better choice for you.

To get support into HWI, we should open an issue there and discuss with the maintainers what a good way would be. Ideally the policy would be encoded in the PSBT itself, but afaik there is no standard around that yet.

dr-orlovsky commented 9 months ago

Thank you for all the details. I will do an issue in HWI repo as you have suggested.

However, one more thing: it is not clear for me why we need to send the policy to the BitBox if the policy can be re-constructed using miniscript library from the PSBT_IN_WITNESS_SCRIPT (for pre-taproot) and PSBT_IN_TAP_LEAF_SCRIPT (for taproot), which is already present in the PSBT - and together with key derivation fields (like PSBT_GLOBAL_XPUB, PSBT_IN_BIP32_DERIVATION, PSBT_IN_TAP_BIP32_DERIVATION, PSBT_IN_TAP_INTERNAL_KEY) the wallet ALREADY has all information to register the policy.

So my point is that no additional PSBT fields or call to API to send it separately should be required. The algorithm is the following:

  1. Check which inputs have keys (from BIP32 derivation fields) derived from the device seed, for each of them:
  2. If the input is pre-taproot, reconstruct miniscript policy from PSBT_IN_WITNESS_SCRIPT and register it;
  3. If the input is taproot, reconstruct TapTree (again using miniscript library) from the individual control blocks and register it; 3.a. If PSBT doesn't contain PSBT_IN_TAP_LEAF_SCRIPT to reconstruct the full tree, fail with error message.
dr-orlovsky commented 9 months ago

Ok, I have read [all the arguments in the discussion(https://github.com/bitcoin/bips/pull/1389) and see why you need a new "descriptor template" language (next to miniscript, miniscript policy, output descriptor and xkey descriptor languages).

However, I still doubt whether a separate API call or a custom PSBT field is required, since all information to build "descriptor template" and display it to the user in the device UI is already there in PSBT (all my arguments/algorithm from the comment above still apply).

dr-orlovsky commented 9 months ago

To get support into HWI, we should open an issue there and discuss with the maintainers what a good way would be.

It also seems there is already a WIP implementation for this: https://github.com/bitcoin-core/HWI/pull/647

benma commented 9 months ago

Isn't PSBT_IN_TAP_LEAF_SCRIPT only part of the tree, but one needs the full tree to reconstruct the full policy?

The BitBox02 also needs the policy to register the wallet on the device before any signing or receiving, and is also needed to display receive addresses, so it must be available on the host anyway.

dr-orlovsky commented 9 months ago

Each individual PSBT_IN_TAP_LEAF_SCRIPT represents a single taptree leaf - but PSBT can provide multiple - up to covering all leafs (property is key-mapped, where the key is control block, so you can have many of the keys for the same property). In such way the wallet communicates whole taptree - and if some leaf is missed, that device may notify the user and reject PSBT.

Thus, you do not need custom PSBT structures.

Next, you use tap tree to reconstruct miniscript descriptor - and from it build your policy template and register it.

benma commented 9 months ago

I prefer the explicit approach the BitBox02 took already. The code complexity and binary bloat to support reconstructing a policy string is significant, including all the multipath <n;m> that could appear. The compatibility issues would be greater, as PSBTs will not normally contain all leafs.

Furthermore, I am not sure the reconstructed policy will always be unique, and may not match the exact way it is shown in a coordinator wallet (e.g. @1/** vs @1/<0;1>/*, etc.). There was also talk about breaking miniscript roundtrips by extending and(X1,X1) to and(X1,X2,X3,...), which would be equivalent to and(...(and(X1,X2),X3),...) - not sure if that was abandoned or not.

Next, you use tap tree to reconstruct miniscript descriptor - and from it build your policy template and register it.

That is a UX problem as well, if you want to e.g. show in the coordinator wallet that the wallet should be registered, and give it a name, the registration must be separate.

For now the BitBox02 will stick to explicit registration and sending the policy for receive addresses and signing.

Closing this issue for the above reason, and the rest of the issue is about HWI.

dr-orlovsky commented 9 months ago

I am not sure we are talking about the same thing here.

I assume that you are already using rust-miniscript in your firmware, thus everything I have described is available out of the box already inside your binary - you just need to call one more method. At the same time asking to add a PSBT field will end up in "standardization hell" and will take forever.

If you are not using rust-miniscript, everything you said makes perfect sense.

benma commented 9 months ago

We are using rust-miniscript, but assembling a policy string including multipaths is not part of it. Not sure if even producing the descriptor from the leaf scripts is there or not. Regardless, just because some of the code already exists it does not mean it is not complex or that it does not induce significant bloat. This work is best offloaded to host coordinators, which usually already have the policy.

There are more reasons to prefer the explicit approach. Some of them given above (UX, uniqueness, etc.), but also the BitBox02 streams the transaction but displays which account/policy it spends from in the beginning before any inputs are streamed. If the info is reconstructed from inputs, the signing/streaming API of the BitBox02 would have to be changed significantly.

At the same time asking to add a PSBT field will end up in "standardization hell" and will take forever.

One can send it separately of the PSBT (Liana is doing this for the Ledger and the BitBox02) and how the HWI PR you linked does it, or BIP174 Proprietary keys could be made use of until a standard emerges.