Add failing test for hardened timelocks
What changed, and why it matters
This commit only adds a new automated test file. It does not change the actual Ledger Bitcoin app code. The test checks that the app correctly rejects wallet policies containing relative timelock values that are outside the allowed range. Because no application code is modified, this commit by itself does not introduce or fix a security vulnerability. It is a test-only change that documents expected behavior for an existing validation rule.
No immediate action is required for this commit alone. Treat it as a regression test addition. If the test is currently failing, review the corresponding application logic that validates miniscript timelock bounds and patch it so the test passes. If the test already passes, ensure it is run in CI to prevent future regressions.
Security signals we found
Test-only commit; no application code modified
Tests validation of relative timelock bounds in miniscript wallet policies
Expects NotSupportedError / EC_REGISTER_WALLET_POLICY_NOT_SANE for out-of-range older() values
Commit title frames the test as 'failing' for hardened timelocks, suggesting it documents a known gap or regression test
Evidence from the diff
The diff adds test cases in tests/test_register_wallet.py that attempt to register wallet policies using miniscript fragments older(n) with n values of 65536, 2^22, and 2^22+65536. The test asserts that the device returns NotSupportedError with error code EC_REGISTER_WALLET_POLICY_NOT_SANE for both native SegWit v0 (wsh) and Taproot (tr) descriptor templates. The commit message explicitly calls this a ‘failing test for hardened timelocks,’ indicating the test is meant to reproduce or guard against a bug where hardened timelock bounds were not enforced. No C or Rust application code is changed.
Changed components
tests/test_register_wallet.pyInspect captured patch +38 / −0
diff --git a/tests/test_register_wallet.py b/tests/test_register_wallet.py
index 1ff9485..3880b87 100644
--- a/tests/test_register_wallet.py
+++ b/tests/test_register_wallet.py
@@ -351,6 +351,44 @@ def test_register_wallet_not_sane_policy(navigator: Navigator, firmware: Firmwar
error_code = int.from_bytes(e.value.data, 'big')
assert error_code == EC_REGISTER_WALLET_POLICY_NOT_SANE
+ # Relative timelocks outside of the sane range.
+ # Not testing for older(0), since it's already rejected by the miniscript parser, which enforces 1 <= n < 2^31,
+ # and is therefore rejected with IncorrectDataError rather than NotSupportedError.
+ INVALID_RELATIVE_TIMELOCKS = [65536, (1 << 22) + 0, (1 << 22) + 65536]
+ for invalid_timelock in INVALID_RELATIVE_TIMELOCKS:
+ with pytest.raises(ExceptionRAPDU) as e:
+ client.register_wallet(WalletPolicy(
+ name="Invalid relative timelock",
+ descriptor_template=f"wsh(and_v(v:pk(@0/**),older({invalid_timelock})))",
+ keys_info=[
+ "[f5acc2fd/48'/1'/0'/2']tpubDFAqEGNyad35aBCKUAXbQGDjdVhNueno5ZZVEn3sQbW5ci457gLR7HyTmHBg93oourBssgUxuWz1jX5uhc1qaqFo9VsybY1J5FuedLfm4dK",
+ ],
+ ),
+ navigator,
+ testname=test_name
+ )
+ assert DeviceException.exc.get(e.value.status) == NotSupportedError
+ assert len(e.value.data) == 2
+ error_code = int.from_bytes(e.value.data, 'big')
+ assert error_code == EC_REGISTER_WALLET_POLICY_NOT_SANE
+ # repeat the test for a taproot policy
+ with pytest.raises(ExceptionRAPDU) as e:
+ client.register_wallet(WalletPolicy(
+ name="Invalid relative timelock",
+ descriptor_template=f"tr(@0/<0;1>/*,and_v(v:pk(@0/<2;3>/*),older({invalid_timelock})))",
+ keys_info=[
+ "[f5acc2fd/48'/1'/0'/2']tpubDFAqEGNyad35aBCKUAXbQGDjdVhNueno5ZZVEn3sQbW5ci457gLR7HyTmHBg93oourBssgUxuWz1jX5uhc1qaqFo9VsybY1J5FuedLfm4dK",
+ ],
+ ),
+ navigator,
+ testname=test_name
+ )
+ assert DeviceException.exc.get(e.value.status) == NotSupportedError
+ assert len(e.value.data) == 2
+ error_code = int.from_bytes(e.value.data, 'big')
+ assert error_code == EC_REGISTER_WALLET_POLICY_NOT_SANE
+
+
# TODO: we can probably not trigger stack and ops limits with the current limits we have on the
# miniscript policy size; otherwise it would be worth to add tests for them, too.
Why this scored 12/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.