Skip to content

test(7.15): hive signing suites (1614–1617) + clearsign v2 harness + chain test updates#27

Draft
BitHighlander wants to merge 21 commits into
reconcile/upstream-syncfrom
feat/hive-sign-tests-715
Draft

test(7.15): hive signing suites (1614–1617) + clearsign v2 harness + chain test updates#27
BitHighlander wants to merge 21 commits into
reconcile/upstream-syncfrom
feat/hive-sign-tests-715

Conversation

@BitHighlander

@BitHighlander BitHighlander commented Jul 16, 2026

Copy link
Copy Markdown
Owner

⚠️ Fix-before-merge / do-not-promote

device-protocol is pinned to a fork commit (7182973, BitHighlander) and .gitmodules points at BitHighlander/device-protocol. This does not satisfy the #36 → #111 → master → #196 promotion contract, and mirrors the gate upstream keepkey#196 already holds itself draft for.

The fork pin is currently unavoidable: the release protos (LoadClearsignSigner icon fields, Hive 1614–1617) exist on no canonical keepkey/device-protocol master commit until device-protocol#111 merges. The bindings here were regenerated from that pin.

Required before promoting keepkey#196 — once keepkey#111 lands on keepkey/device-protocol:master:

  1. set .gitmodules url back to https://github.com/keepkey/device-protocol.git
  2. pin the gitlink to the resulting canonical master commit
  3. re-run docker_build_pb.sh, confirm bindings are byte-identical
  4. only then undraft / promote

(The fork url in .gitmodules is pre-existing — set by e88ff15, already present at keepkey#196's head e728e311 — not introduced here. But this PR keeps it alive, so the gate applies.)


Stacks into reconcile/upstream-sync — the branch that PRs to master as keepkey#196. Per SOP the master merge becomes the canonical pin for firmware work, so the harness firmware runs against must ride it.

Companion to BitHighlander/device-protocol#36 (protos) and BitHighlander/keepkey-firmware#310 (icon hardening).

Why

Firmware fork develop pins python-keepkey 15d95ec; reconcile/upstream-sync was at e728e31 — 17 commits behind, leaving the merged Hive work with no upstream test coverage.

Review fixes applied

  • Stale bindings — root cause: this repo's own device-protocol submodule was pinned at 2ec999a9, which lacks the icon fields and 1614–1617, so build_pb.sh regenerated from a protocol that never had them. Re-pinned + regenerated: LoadClearsignSigner now exposes icon/icon_width/icon_height/persist (constructing with icon= no longer raises ValueError); messages_pb2.py carries real 1614–1617 constants (mapping.py's manual table was masking their absence); client.load_clearsign_signer() accepts icon/dims/persist.
  • Icon RLE reference decoder — now mirrors firmware exactly, including rejecting 0x80 (n = −128), which it previously accepted and thereby masked a real incompatibility. Golden vector + RUN/LITERAL + boundary/reject tests.
  • Maya signature verification — both EVM tests now recover the signer from the EIP-155 sighash and assert it equals the device's own address, replacing the len(r)==32 structural asserts (which predate 560b897). Verified offline: tampered calldata does not recover to the signer.

Offline suites: 30 passed.

BitHighlander and others added 18 commits July 3, 2026 00:14
Tied to the 7.15 release (bitcoin-only + Zcash-privacy split, PR #282):

1. Zcash section prose claimed shielded FVK/PCZT/unified-address are "all live";
   they are WITHHELD on the default build (KK_ZCASH_PRIVACY off pending audit).
   Retitled to "Zcash Shielded (Orchard)" and rewrote the background to say the
   tests skip BY DESIGN (build-flag gated), not for missing/broken support.
2. Skip accounting conflated design-skips with "pending" and labelled a
   supported-but-withheld chain "no firmware support yet". Now counts skip vs
   missing separately, a build with 0 failures/0 missing headlines GREEN
   ("N/M PASSED, K skipped (withheld)"), and withheld sections get their own
   "Withheld on this build (build-flag gated; skipped by design)" header.
3. Added a "Zcash Transparent" section (Y) for the t-address path that actually
   ships and passes (test_msg_signtx_zcash); removed the duplicate B28 that had
   it buried under Bitcoin.
4. Device-Specs appendix: added FIRMWARE VARIANTS (KeepKeyBTC/EmulatorBTC) and
   SEED LOCK blocks; qualified the curve list (Pallas only on KK_ZCASH_PRIVACY=ON).
5. _pick_best_frame now falls back to a readable frame instead of an "OLED
   needed" placeholder when nothing lands in the density band (single dense
   QR/address frames were dropped); removed dead _is_setup_frame.
6. _lookup dropped its bare-method fallback (a cross-module method-name
   collision could render a never-run test as PASS, defeating --validate-junit);
   all SECTIONS modules are test_msg_* so mod::meth always resolves.
7. Non-Latin-1 punctuation (em dashes etc.) was rendering as '?'; added an
   _ascii sanitizer at the PDF content-stream boundary.
8. Removed stale "deferred to 7.15+" copy from the TRON/TON sections of a report
   that IS firmware 7.15.0.
9. ver_t() tolerates pre-release tags (7.15.0-rc3) and short versions instead of
   crashing.
10. Replaced an unprofessional test-fixture memo rendered on an OLED screenshot
    with a neutral value (recomputed the expected signature).

Verified: emulator report run green (475 passed, 43 skipped, 0 failed);
generated report shows 18 sections, 199 passed / 20 skipped (withheld) / 0
pending, --validate-junit passes.
Adds the python-keepkey test-framework support for firmware v2 (static schema,
METADATA_VERSION_SCHEMA): a blob that attests only the decode schema (no tx_hash,
no arg values) so it can be signed once offline — the device decodes the arg
values from the calldata it signs.

- keepkeylib/signed_metadata.py: serialize_schema_metadata() (v2 wire format) +
  schema_calldata() (ABI fixed-word calldata a v2 schema decodes).
- tests: TestClearSignV2SchemaOffline (7 offline byte-format tests incl. a frozen
  body snapshot as a drift gate) + TestClearSignV2Device (on-device decode+sign+
  recover, gated requires_firmware('7.16.0') so it skips until a v2 firmware ships).
- report: VS1-VS5 in the EVM Clear-Signing section documenting v2 (no tx_hash,
  static decimals/symbol, frozen format, fixed-word scope, on-device round-trip).

Mirrors firmware feat/clearsign-static-schema (keepkey-firmware PR #284).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
test_tron_sign_transfer_legacy_raw_data / test_tron_sign_deterministic /
test_tron_sign_different_accounts use a hand-rolled, truncated raw_data
blob that was never a fully valid TransferContract — it exercised the old
unconditional blind-sign path. Firmware's new raw_data clear-sign parser
correctly fails to decode it and falls back to the opaque blind-sign path,
which now requires AdvancedMode. Enable the policy for these three tests;
they're testing signature plumbing (shape/determinism/per-account
uniqueness), not contract-content parsing.
…que cases

The old test_solana_sign_versioned_v0_opaque built a v0 tx whose
instruction only touched static accounts (no address table lookups) and
asserted it must be rejected without AdvancedMode. That's the pre-clearsign
assumption; firmware now treats static-only v0 messages as fully verifiable
(same as legacy) and clear-signs them directly. Split into:
- test_solana_sign_versioned_v0_static_verified: static-only v0 clear-signs
  without AdvancedMode.
- test_solana_sign_versioned_v0_opaque: a v0 tx whose instruction resolves
  an account via the address lookup table (genuinely unverifiable) still
  requires AdvancedMode.
…gaps

Audit of fork develop CI run 28679110349 (PR #284 clearsign v2 static-
schema, 526 junit tests) against the who/what/why standard for every
major daily-driver EVM tx format. Ten fixes:

1. Lower 8 stale requires_firmware("7.15.1") gates to 7.15.0 (signing
   guards x5, blind_sign_blocked/allowed, bip39-word-reject) — the
   behaviors ship in the 7.15.0 line under test; the gate zeroed out
   the EIP-1559 wrong-signer regression suite and the blind-sign
   policy negative path on every 7.15.0 CI run.
2. Document the ETH-section amount-unit rule (Wei below 1 gwei, scaled
   ETH/ticker above) so toy conformance-vector screenshots aren't
   misread as a "never wei" violation.
3. Disclose EIP-712 typed-data hash-signing as a known gap (E16): the
   device signs two host-computed hashes with zero readable domain/
   message display — a daily-driver format with no who/what/why today.
4. Fix the ERC-4337 handleOps flow's overclaiming "innerCall: decoded
   separately, not raw" placeholder — the representative UserOp's
   inner callData is actually empty; the claim is now honest about
   what this flow does and does not prove (regenerated its frozen
   reference-blob snapshot to match).
5. Add 2 new on-device tests for the v2 static-schema headline security
   property, previously unit-tested but never proven on real device
   behavior: calldata/schema length-mismatch falls back to the
   blind-sign gate (VS6), and an unsupported dynamic arg format is
   independently rejected as MALFORMED by the device parser (VS7).
6. Surface the 3 silently-skipped Uniswap V2 liquidity tests (E17-E19)
   with their documented emulator-limitation reason instead of leaving
   them absent from SECTIONS entirely.
7. Surface the 3 passing-but-invisible THORChain-router EVM deposit
   tests (E20-E22), including the router-pin blind-sign-gate proof
   that closed a CRITICAL bypass.
8. Surface 6 passing-but-invisible malformed-blob rejection tests
   (V7a-V7f: empty/truncated/trailing-bytes/wrong-version/zero-sig/
   empty-slot) — the WHY = fail-closed story had no rejection-path
   evidence in the release-gate document.
9. Fix the stale "7.15.1 NEW FEATURES" banner comment and the V8
   context text that claimed "covered in 7.15.0+" while pointing at a
   test gated to 7.15.1 (a self-contradiction in the PDF's own prose,
   resolved by fix #1).
10. Fix print_clearsign_flows() (--flows external-signer reference
    dump): KeyError on flow['shows'] (no catalog flow has that key —
    regression from the move to the shared catalog module) and a
    missing required key_id arg to flow_blob().

Verified: all 6 edited files compile; SECTIONS loaded in the real
firmware-checkout env (245 unique entries, 224->245, zero ID
collisions); every new/changed (module, method) SECTIONS reference
resolves to a real test function; all 51 REFERENCE_BLOB_SNAPSHOTS
still match (only the ERC-4337 entry's hash/length changed, matching
the ARG_FORMAT_STRING value edit in fix #4).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…1 cap

CI on the previous commit failed: test_clearsign_erc4337_entrypoint_v0_7_handleops
and test_clearsign_batch_all_payloads both got MALFORMED instead of VERIFIED
(AssertionError: 2 != 1). Root cause: legacy v1 ARG_FORMAT_STRING values are
hard-capped at 32 bytes (arg_value_ok(), lib/firmware/signed_metadata.c:138 —
"legacy formats keep their original 32-byte cap"). My disclosure string from
the previous commit ("empty in this representative UserOp", 35 bytes) exceeded
it; the device rejected the whole blob at parse time.

Shortened to "empty (representative)" (22 bytes) — same honest disclosure,
fits the cap. Regenerated the flow's frozen reference-blob snapshot and
verified all 51 REFERENCE_BLOB_SNAPSHOTS match, plus added a blanket check
that no catalog STRING arg exceeds 32 bytes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…7.15 canonical

reconcile/upstream-sync was missing the entire v2 clear-signing test harness
(serialize_schema_metadata, 51-flow catalog, report generator, solana/tron/
erc4337 fixes) that develop actually pins via c101137. Merge it in so the
canonical 7.15 branch is complete, keeping this branch's bip85-gate fix.
Conflict-free — the two lines touched disjoint files.
Solana versioned (v0) transaction parsing (solana_parseVersionedTx) landed in
the 7.15 line. The two v0 tests asserted against it unconditionally, so on a
pre-7.15 firmware (e.g. a stacked release PR that hasn't yet added v0 support)
the 0x80 version prefix is rejected as a legacy tx (Failure_SyntaxError) and
the tests FAIL instead of skipping. Add the same requires_firmware(7.15.0) gate
the bip85 tests use so they skip cleanly on <7.15 and run on 7.15+.
Firmware now gates length-only blind TON transaction signing behind the
AdvancedMode policy (same fence as TonSignMessage and Solana/TRON opaque
signing).
…y tests

Firmware MAYA_ROUTER was corrected from the dead d89dce57.. to the
Etherscan-verified v4 e3985e6b..46d, so these ETH swaps must target the
real router to be clear-signed (else they fall to the AdvancedMode
blind-sign gate). Assertions are structural; no exact-vector regen needed.
- reject foreign path (BIP-44), wrong network (3054'), unassigned role
- memo >440 fails with the specific error; 440 boundary still signs+recovers
- config.py: KK_FORCE_UDP=1 local-only escape hatch to reach the UDP
  emulator with a real device plugged in (not for CI)
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).
…tract tests

7 new tests: posting login recovery, all-4-roles, non-printable buffer,
1024 boundary, oversize reject, bad-path fence, chain-id-prefix collision
reject.
… printable boundary

- all_roles now covers the three Keychain-exposed roles (posting/active/memo)
- owner' path moved to the reject fence
- new: 300-byte printable message signs via the hex-preview confirm
…ps test suite

Own Graphene mini-serializer (dhive-equivalent) so parser and serializer
bugs can't cancel out. 9 new tests: vote/downvote (+default chain id),
unicode-body post, custom_json posting+active, 2/9/10 permanent exclusion,
malformed structure (op count 0/5, extensions, trailing, overlong varint,
weight range), role fences (active/memo/owner on posting tx, posting on
active tx, mixed-tier, both-auths), oversize.
…rify Maya signatures

Addresses three review findings on this stack.

1) Bindings were generated from a STALE protocol. The device-protocol submodule
   was pinned at 2ec999a9 — the same commit that lacks LoadClearsignSigner's
   icon fields and Hive 1614–1617 — so build_pb.sh regenerated incomplete
   bindings. Bump the pin to f7b4580 and regenerate:
     - messages_ethereum_pb2.py: LoadClearsignSigner now exposes icon,
       icon_width, icon_height, persist (was key_id/pubkey/alias only, so
       constructing with icon raised ValueError).
     - messages_pb2.py: real MessageType_HiveSign{Message,Operations} /
       Signed{Message,Operations} = 1614–1617 constants. mapping.py's manual
       wire-id table was masking their absence.
   client.load_clearsign_signer() now accepts icon/icon_width/icon_height/
   persist and documents the RLE contract (was RAM-only, no icon).

2) The icon wire contract was mis-documented (device-protocol#36 fixes the
   proto). Adds TestClearsignSignerIcon: a reference RLE decoder traced from
   draw_bitmap_mono_rle(), the published golden vector (03 FF FF 00, w=2 h=2
   -> FF FF FF 00), RUN/LITERAL packets, n==0 and truncation rejects, the
   icon/dims/persist round-trip, and text-only identities. Also asserts the
   arithmetic that forces RLE: a packed 1bpp 64x64 needs 512 bytes > the
   384-byte cap, so the previously documented packed format was impossible.

3) The two Maya EVM tests asserted only v and 32-byte r/s lengths, so a wrong
   digest, calldata or key would still pass. (The weakening predates 560b897 —
   its parent already had the structural asserts; the router change inherited
   them.) Replace with recover_eth_signer(): rebuild the EIP-155 sighash from
   the exact tx fields, recover the signer from (v,r,s), and assert it equals
   the device's own address for the path. Verified offline: a good signature
   recovers to the signer, and tampered calldata does NOT — so the tests now
   actually fail on a wrong digest. Recovery keeps them correct across router
   changes without re-freezing r/s vectors.

Offline suites: 26 passed.
@BitHighlander

Copy link
Copy Markdown
Owner Author

Thanks — all three findings confirmed and fixed in a322ef1.

#2 (Medium) — incomplete bindings. Root cause found: this repo's own device-protocol submodule was pinned at 2ec999a9 — the same stale commit that lacks the icon fields and 1614–1617 — so build_pb.sh was regenerating from a protocol that never had them. Bumped the pin to f7b4580 and regenerated:

  • messages_ethereum_pb2.py: LoadClearsignSigner now exposes icon, icon_width, icon_height, persist. Verified the exact symptom is gone — constructing with icon= no longer raises ValueError.
  • messages_pb2.py: real MessageType_HiveSign{Message,Operations} / Signed{...} = 1614–1617 constants. You were right that mapping.py's manual table was masking the omission.
  • client.load_clearsign_signer() now accepts icon/icon_width/icon_height/persist and documents the RLE contract (was RAM-only).
  • New TestClearsignSignerIcon covers icon/dimensions/persist round-trip + text-only identities.

#1 (High) — icon wire contract. Fixed in device-protocol#36: the proto documented a packed 1bpp bitmap while firmware ran an RLE decoder. The RLE turned out to be load-bearing — a packed 1bpp 64×64 needs 512 bytes vs the 384-byte cap, so the documented format was impossible at the dimensions firmware accepts. The grammar + golden vector (03 FF FF 00FF FF FF 00) are now specified there and enforced here as a runnable test with a reference decoder traced from draw_bitmap_mono_rle().

#3 (Medium) — Maya tests didn't verify signatures. Correct, and worth noting the weakening predates 560b897 (its parent already had the structural asserts; the router change inherited them). Rather than re-freeze r/s, both sites now use recover_eth_signer(): rebuild the EIP-155 sighash from the exact tx fields, recover the signer from (v,r,s), assert it equals the device's own address for the path. Verified offline: a good signature recovers to the signer, and tampered calldata does not — so a wrong digest/calldata/key now fails, and the tests stay correct across future router changes.

Offline suites: 26 passed.

…rrected proto

The reference RLE decoder accepted a 128-byte literal (n = -128 / 0x80) because
Python ints don't overflow — masking a real incompatibility: firmware's counter
is int8_t, so -(-128) wraps to -128 and the packet is undecodable (assert on
debug/emulator; negative-counter signed-overflow UB under NDEBUG). A decoder
that accepts what the device cannot is worse than no reference at all.

Mirror firmware exactly: reject n == -128 and n == 0. Adds the boundary tests
the review asked for — 0x80 literal rejected, and the valid -127/127 boundaries
still decode — plus a guard that the icon_width cap is the 40px text column
(LEFT_MARGIN_WITH_ICON), not the 64px height, so the two can't be conflated.

Re-pins device-protocol to 7182973 (the corrected contract: literals [-127,-1],
0x80 invalid, icon_width 1..40). Comment-only upstream, so the regenerated
bindings are byte-identical — pb2 files unchanged.

Firmware enforcement: BitHighlander/keepkey-firmware#310.
Offline suites: 30 passed.
@BitHighlander
BitHighlander marked this pull request as draft July 16, 2026 21:20
@BitHighlander

Copy link
Copy Markdown
Owner Author

All three confirmed. Fixed in 03af8d1; PR converted to draft with an explicit fix-before-merge gate.

Reference decoder masked the 0x80 incompatibility — correct, and the most important catch

My decoder accepted a 128-byte literal because Python ints don't overflow, hiding that firmware's int8_t counter cannot represent it. A reference decoder that accepts what the device cannot is worse than no reference at all. It now mirrors firmware exactly — rejects n == -128 and n == 0 — with the boundary tests you asked for: 0x80 rejected, -127/127 still decode. Contract fixed in device-protocol#36, firmware enforcement in keepkey-firmware#310 (which also rejects 0x80 in the decoder, since a proto comment can't bind a hostile host).

Fork pin — correct, and now gated

You're right that this doesn't satisfy #36 → #111 → master → #196, and that keepkey#196 holds itself draft for exactly this. Two notes:

So rather than pretend otherwise, #27 is now draft with a fix-before-merge section mirroring keepkey#196's, listing the required sequence: after keepkey#111 lands → url back to keepkey/device-protocol → pin the canonical master SHA → re-run docker_build_pb.sh → confirm bindings byte-identical → only then promote.

Re-pinned to 7182973 (the corrected contract) in the meantime; that change was comment-only upstream, so the regenerated bindings are byte-identical.

Offline suites: 30 passed.

…ware validator

The reference decoder repeated the drawing path's leniency: it filled w*h pixels
and returned, so it accepted a final run that straddles the image (05ff at 2x2)
and silently ignored trailing packets. A reference that accepts streams the
device rejects is the same class of bug as the 0x80 case — it hands client
authors an encoder that produces invalid icons.

Mirror firmware's draw_bitmap_mono_rle_valid(): reject leftover run counters and
any unconsumed input. Adds the straddling-run and trailing-packet tests.

Firmware side: BitHighlander/keepkey-firmware#310.
@BitHighlander

Copy link
Copy Markdown
Owner Author

Fixed in 25df24c.

Reference decoder repeated the leniency — correct. It filled w*h and returned, so it accepted a straddling run (05ff at 2×2) and silently ignored trailing packets. Same class of bug as the 0x80 case: a reference that accepts what the device rejects hands client authors an encoder that produces invalid icons. It now mirrors firmware's draw_bitmap_mono_rle_valid() exactly — rejects leftover run counters and any unconsumed input — with straddling-run and trailing-packet tests added. Offline suites: 32 passed.

On the CircleCI finding (#3): this one is expected rather than a gap. python-keepkey's CircleCI can't clone fork branches, but these tests don't get their coverage there — they run in the firmware repo's CI (python-integration-tests / python-dylib-tests), which pins the pyk commit and runs the suite against the built firmware. That's the gate that matters for this stack, and it's green on BitHighlander/keepkey-firmware#310. Leaving the CircleCI config alone rather than changing shared CI for a fork-only workflow.

Firmware fixes for the icon findings: BitHighlander/keepkey-firmware#310 (full suite 262/262).

BitHighlander added a commit to BitHighlander/keepkey-firmware that referenced this pull request Jul 16, 2026
Bumps the integration pins to the heads of the two companion PRs
(BitHighlander/device-protocol#36, BitHighlander/python-keepkey#27) so firmware
CI actually exercises them.

Review correctly pointed out that the earlier "the pyk tests run in firmware CI"
answer did not hold at the old pin: firmware CI runs whatever commit firmware
PINS, and 15d95ec predates the regenerated bindings + Maya signature recovery,
the 0x80 correction, and the exact-decoder tests. Those three commits had no CI
anywhere. This pin closes that — python-integration-tests now runs pyk@25df24c
against the emulator built from this tree, and check-submodules covers dp@7182973.

Local verification at these pins: firmware-unit 262/262.
…tests

Three review points, all confirmed against a full integration run.

1) The Maya tests were BROKEN, not merely unverified. Running rc10 against these
   pins surfaced it: `TypeError: assertEqual() takes 3 positional arguments but
   4 were given` — common.KeepKeyTest overrides assertEqual with no msg param
   (the same trap 591dba8 already fixed once). Both sites failed, so the
   signature verification never actually ran. Drop the msg argument, and reuse
   the recovery helper already proven in test_msg_ethereum_clear_signing.py
   (same signature, hashfunc=None) instead of the bespoke one I added.
   Integration now: 546 passed, 0 failed (was 544 passed, 2 failed).

2) The public contract was contradictory. client.py still documented all
   negative counts (including 0x80) and both dimensions as 1..64. Corrected to
   literals [-127,-1] with 0x80 invalid, width 1..40 (LEFT_MARGIN_WITH_ICON),
   height 1..64, plus the exactness rule (no straddling run, whole input
   consumed) and an accurate persist description.

3) The "packed 64x64 needs 512 bytes" rationale is obsolete and is removed.
   64px width is no longer legal, and a packed icon at the legal maximum
   (40x64) is 320 bytes — it WOULD fit the 384-byte cap. RLE is the format
   because draw_bitmap_mono_rle() is the decoder of record and every bundled
   image already uses it, not because packed wouldn't fit. The test now asserts
   that honestly rather than encoding dead arithmetic.

Offline suites: 32 passed.
@BitHighlander

Copy link
Copy Markdown
Owner Author

All three points confirmed and fixed in c475482. Your pin finding was not academic — it was hiding a real breakage.

The pin waiver was wrong, and running rc10 against these pins proved it

You were right that firmware CI runs whatever firmware pins, so the last three commits had no coverage anywhere. We ran rc10 against these exact pins before upstreaming, and the Maya tests failed:

TypeError: assertEqual() takes 3 positional arguments but 4 were given
FAILED test_msg_mayachain_signtx.py::test_sign_eth_add_liquidity
FAILED test_msg_mayachain_signtx.py::test_sign_eth_btc_swap
=========== 2 failed, 544 passed ===========

common.KeepKeyTest overrides assertEqual with no msg parameter (commit 591dba8 fixed this exact trap once before). My failure message made it 4 args — so both tests errored and the signature verification never actually ran. The "Maya tests now verify signatures" claim was false at that pin. Fixed, and the bespoke helper is replaced with the one already proven in test_msg_ethereum_clear_signing.py (same signature, hashfunc=None).

Now: 546 passed, 0 failed. Firmware #310's pin is bumped to c475482 so CI runs it.

The contract was contradictory — fixed

client.py now says: literals [-127,-1], 0x80 invalid, width 1..40 (LEFT_MARGIN_WITH_ICON), height 1..64, plus the exactness rule (no straddling run, whole input consumed) and an accurate persist description.

The "64×64 can't fit" rationale is dead — you're right, and I've removed it

Once width was capped at 40, my own argument collapsed: a packed icon at the legal maximum is 40×64/8 = 320 bytes, which fits the 384-byte cap. The honest reason for RLE is that draw_bitmap_mono_rle() is the decoder of record and every bundled image already uses it — not that packed wouldn't fit. The test now asserts that instead of encoding dead arithmetic.

#36 gets the same treatment (stale arithmetic + the RAM-only line that contradicts persist) in the next push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant