Skip to content

Commit 35555d7

Browse files
committed
test(hive): role-exact path enforcement per operation
Review follow-up on keepkey-firmware#305: transfers must reject owner/ memo/posting paths (post-HF28 hived drops higher-role substitution; the cold owner key must never sign a transfer), and account_create/update must reject non-owner paths (the attestation contract recovers to the device owner key).
1 parent 02fa3ea commit 35555d7

1 file changed

Lines changed: 43 additions & 6 deletions

File tree

‎tests/test_msg_hive.py‎

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -351,16 +351,53 @@ def test_hive_sign_transfer_rejects_wrong_network(self):
351351
address_n=[h + 48, h + 3054, h + ROLE_ACTIVE, h, h],
352352
)
353353

354-
def test_hive_sign_transfer_rejects_wrong_role(self):
355-
"""Role index outside {owner, active, memo, posting} must be rejected."""
354+
def test_hive_sign_transfer_rejects_non_active_roles(self):
355+
"""Transfers must sign with the active key ONLY. Post-HF28 hived no
356+
longer accepts higher-role substitution, so an owner/memo/posting
357+
signature would be rejected at broadcast — and the cold owner key
358+
must never be spent on a transfer. Unassigned roles reject too."""
356359
self.requires_firmware("7.15.0")
357360
self.requires_message("HiveSignTx")
358361
self.setup_mnemonic_nopin_nopassphrase()
359-
h = 0x80000000
360-
self._assert_sign_tx_fails(
361-
"Invalid Hive SLIP-0048 path",
362-
address_n=[h + 48, h + 13, h + 2, h, h], # role 2' is unassigned
362+
for role in (ROLE_OWNER, ROLE_MEMO, ROLE_POSTING, 2): # 2' unassigned
363+
self._assert_sign_tx_fails(
364+
"Invalid Hive SLIP-0048 path",
365+
address_n=hive_path(role),
366+
)
367+
368+
def test_hive_sign_account_ops_reject_non_owner_roles(self):
369+
"""account_create/account_update must sign with the owner key ONLY —
370+
the sponsor's attestation check recovers to the device OWNER key, and
371+
account_update replaces the owner authority itself."""
372+
self.requires_firmware("7.15.0")
373+
self.requires_message("HiveSignAccountCreate")
374+
self.requires_message("HiveSignAccountUpdate")
375+
self.requires_message("HiveGetPublicKeys")
376+
self.setup_mnemonic_nopin_nopassphrase()
377+
from keepkeylib.client import CallException
378+
keys = hive.get_public_keys(self.client, account_index=0, show_display=False)
379+
tx_kw = dict(
380+
chain_id=HIVE_CHAIN_ID,
381+
ref_block_num=12345,
382+
ref_block_prefix=67890,
383+
expiration=1700000000,
363384
)
385+
with self.assertRaises(CallException) as ctx:
386+
hive.sign_account_create(
387+
self.client, address_n=hive_path(ROLE_ACTIVE),
388+
creator="kksponsor", new_account_name="kktestacct",
389+
fee_amount=3000, owner_key=keys.owner_key,
390+
active_key=keys.active_key, posting_key=keys.posting_key,
391+
memo_key=keys.memo_key, **tx_kw)
392+
self.assertIn("Invalid Hive SLIP-0048 path", str(ctx.exception))
393+
with self.assertRaises(CallException) as ctx:
394+
hive.sign_account_update(
395+
self.client, address_n=hive_path(ROLE_ACTIVE),
396+
account="kktestacct", new_owner_key=keys.owner_key,
397+
new_active_key=keys.active_key,
398+
new_posting_key=keys.posting_key,
399+
new_memo_key=keys.memo_key, **tx_kw)
400+
self.assertIn("Invalid Hive SLIP-0048 path", str(ctx.exception))
364401

365402
def test_hive_sign_transfer_rejects_long_memo(self):
366403
"""Memo over the 440-byte serialization limit must fail with a

0 commit comments

Comments
 (0)