Skip to content

feat(build): one-command reproducible-build verification script#449

Open
BitHighlander wants to merge 2 commits into
developfrom
feature/verify-repro-script
Open

feat(build): one-command reproducible-build verification script#449
BitHighlander wants to merge 2 commits into
developfrom
feature/verify-repro-script

Conversation

@BitHighlander

Copy link
Copy Markdown
Collaborator

Summary

Adds scripts/build/verify-repro.sh and docs/ReproducibleBuilds.md — a single command that clones a release tag, builds it with the official Docker image, downloads the signed release binary, and compares the device-verifiable payload hashes (sha256 of everything after the 256-byte KPKY header).

Why

Third-party verifiers keep failing reproducibility checks by comparing the wrong hashes: a freshly built binary has zeroed signature-index bytes and signature slots in its 256-byte header (release signing fills them in — nothing else changes), so sha256(built full file) can never equal sha256(signed release file), even for a byte-identical build. Only the payload hash (tail -c +257) is meaningful.

This exact confusion has now hit KeepKey twice: #283 (2021, resolved manually by @reidrankin in a comment) and #433 (2026, WalletScrutiny). We reproduced #433's build today — the reporter's own "Built" hash matches our official binary's full-file hash exactly, meaning their build was byte-for-byte correct; only the final comparison was wrong.

docs/ReproducibleBuilds.md documents the 256-byte header layout, why full-file hashes never match by construction, the manual verification steps, and two on-device cross-checks: Features.revision (embeds the build commit) and Features.firmware_hash (sha256 of the full signed image as installed — explicitly not the payload hash, to avoid propagating the same confusion).

Test plan

  • bash -n + shellcheck clean
  • Ran end-to-end against v7.14.1: built payload 73a880… matches the official release payload hash in HASHES.txtPASS
  • Verified arg handling: valid --local, mistyped --local=x, unknown flags, and trailing garbage after a valid --local all now error instead of silently falling through

Third-party verifiers keep failing KeepKey reproducibility checks by
hashing the freshly built binary (zeroed signature slots in its 256-byte
KPKY header) against the signed release's payload hash — an
apples-to-oranges comparison that can never match (#433, and the same
confusion in 2021's #283).

scripts/build/verify-repro.sh makes the correct comparison a single
command: clean clone at the tag, official Docker build, download of the
signed release asset, and payload-hash (tail -c +257) comparison on both
sides, printing all four hashes and PASS/FAIL.

docs/ReproducibleBuilds.md documents the header layout, why full-file
hashes never match by construction, the manual steps, and the on-device
cross-checks (Features.revision, Features.firmware_hash).
…s-check

- error out on unrecognized/mistyped flags (e.g. --local=x) instead of
  silently ignoring them and falling through to the download-and-compare
  path, which could print a false PASS for a binary that was never read
- docs: Features.firmware_hash covers the full signed file (header +
  payload, per memory_firmware_hash()), not the payload alone — the doc
  previously implied it matched the payload hash, which never matches
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