bitbox02: support BIP388 policy signing
What changed, and why it matters
This commit adds support for signing Bitcoin transactions with registered wallet policies (BIP388) on the BitBox02 hardware wallet. Previously this feature was rejected with an error. The change translates a registered wallet descriptor into a format the BitBox02 understands and uses it when signing transaction inputs and change outputs. It is a feature addition rather than a fix for a known vulnerability, but it touches security-critical signing logic.
Review the new _bip388_script_config helper (not shown in the diff) for correct descriptor parsing, ensure change-output validation matches the registered policy, and verify that only the intended keypath is used for signing. Consider adding tests for multi-key policies and malicious or malformed descriptors.
Security signals we found
Removal of an explicit unsupported-action error for BIP388 policy signing
New xpub and fingerprint comparison logic to identify the device's key in a policy
New policy script config construction passed to hardware signing routines
Change affects how transaction inputs and change outputs are interpreted for signing
Evidence from the diff
The patch removes the UnavailableActionError for registered_descriptors in the BitBox02 sign_tx implementation. It validates that exactly one registered policy is present, locates the device’s own key in the descriptor by comparing fingerprints and xpubs, and constructs a BTCScriptConfigWithKeypath from a new _bip388_script_config helper. During transaction signing, if a policy script config exists it is returned directly instead of deriving one from the UTXO’s scriptPubKey. This enables multi-signature and policy-based signing workflows on BitBox02.
Changed components
hwilib/devices/bitbox02.pyBitBox02 sign_tx methodBIP388 registered descriptor handlingInspect captured patch +32 / −1
### hwilib/devices/bitbox02.py
@@ -609,8 +609,37 @@ def sign_tx(
Transactions with legacy inputs are not supported.
"""
+ policy_script_config: Optional[bitbox02.btc.BTCScriptConfigWithKeypath] = None
if registered_descriptors:
- raise UnavailableActionError("The BitBox02 does not support BIP388 policy signing")
+ if len(registered_descriptors) > 1:
+ raise BadArgumentError("The BitBox02 can only sign with one registered policy at a time")
+ registered_descriptor = next(iter(registered_descriptors))
+ descriptor = registered_descriptor.descriptor
+ device_fingerprint = self.get_master_fingerprint()
+ account_keypath = None
+ for pubkey in descriptor.get_pubkey_providers():
+ if (
+ pubkey.origin is None
+ or pubkey.origin.fingerprint != device_fingerprint
+ or pubkey.extkey is None
+ ):
+ continue
+ device_xpub = decode_check(self._get_xpub(pubkey.origin.path))
+ if not _xpubs_equal_ignoring_version(
+ device_xpub,
+ pubkey.extkey.serialize(),
+ ):
+ continue
+ if account_keypath is not None:
+ raise BadArgumentError("This BitBox02 occurs more than once in the policy")
+ account_keypath = pubkey.origin.path
+ if account_keypath is None:
+ raise BadArgumentError("This BitBox02 is not one of the policy keys")
+
+ policy_script_config = bitbox02.btc.BTCScriptConfigWithKeypath(
+ script_config=self._bip388_script_config(descriptor),
+ keypath=account_keypath,
+ )
def find_our_key(
keypaths: Dict[bytes, KeyOriginInfo]
@@ -658,6 +687,8 @@ def script_config_from_utxo(
redeem_script: bytes,
witness_script: bytes,
) -> bitbox02.btc.BTCScriptConfigWithKeypath:
+ if policy_script_config is not None:
+ return policy_script_config
if is_p2pkh(output.scriptPubKey):
raise BadArgumentError(
"The BitBox02 does not support legacy p2pkh scripts"Why this scored 32/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.