Skip to content

feat(libgpod): sign iPod nano 6G/7G databases with a vendored HashAB signer - #77

Open
Pred8or wants to merge 6 commits into
jvgomg:mainfrom
Pred8or:pr/2-nano-hashab
Open

Pred8or wants to merge 6 commits into
jvgomg:mainfrom
Pred8or:pr/2-nano-hashab

Conversation

@Pred8or

@Pred8or Pred8or commented Oct 28, 2026 •

Copy link
Copy Markdown

Part 2 of 8 for iPod nano 7th generation support (overview: #75). Stacked on #76: until that merges, this PR also shows its commits; only the commits named below are new here.

Branch pr/2-nano-hashab (3 commits: 22cad5a, 3031e58, 534cc74). Review after PR 1.

The nano 6G/7G only accept a database signed with HashAB. libgpod 0.8.3 computes that signature only through an external blob from LIBGPOD_BLOB_DIR, which podkit doesn't ship, so every write to these models failed.

  • Vendors dstaley/hashab (Unlicense), unchanged, at a pinned commit (tools/prebuild/vendor/hashab, UPSTREAM.md).
  • A small libgpod patch calls it directly instead of loading the blob. It is built as an internal library with -fwrapv, and its symbols are renamed and hidden.
  • Prebuilds: cache keys include the patch and vendored files; Linux static links group the archives.
  • tools/nano7/: a read-only probe that checks the signatures of a copied library, tests against the upstream test vectors, and a libgpod round trip that the prebuild workflow runs. ADR-030 records the decision.

Both models stay read-only in this PR. Signing alone is not enough: the nano 7G firmware still shows no music. PR 3 and 4 fix that.

Testing: tools/nano7/test-libgpod.sh (probe and round trip); unit tests in devices-ipod, libgpod-node, core and cli. The signatures match libraries written by macOS on my nano 7G. The six-target prebuild workflow (which also checks the Linux static link and runs the round trip on every target) passed on my fork for this branch (run) and for the top of the stack (run).

…an invalid signature

On macOS 27 (arm64, Bun 1.3.13) the compiled binary keeps the linker's
ad-hoc signature after Bun appends the payload. codesign --verify reports
"code or signature have been modified" and the kernel kills the binary on
launch with status 137. compile.sh now verifies the signature on darwin and
re-signs ad hoc only when verification fails.
A Nano 7 synced by macOS carries iTunes Library.itlp and iTunesCDB but no
iTunesDB; libgpod parses the CDB, yet IpodDatabase.hasDatabase only probed
iTunesDB. device scan reported "iTunesDB not found" and sync refused with
IPOD_NEEDS_INIT, pointing at device init for a device with 21 tracks.
hasDatabase now accepts either file.
…line

runRepair ran its own refusal check with the injected assessIpodIdentity
but called runRepairPipeline without deps, so the pipeline's second
check assessed the real device path. The supported-device test therefore
failed on any host without an iPod at /Volumes/iPod. The assessor is
now forwarded through the pipeline's existing deps; both checks stay,
and the test counts that both used the injected assessor.
…abases

The nano 6th and 7th generation sign iTunesCDB and Locations.itdb.cbk
with HashAB. libgpod 0.8.3 only computes it through an external blob
loaded from LIBGPOD_BLOB_DIR, which podkit does not ship. dstaley/hashab
is an Unlicense C implementation of the same calcHashAB function.

The C sources, headers, licence and the 100 upstream reference vectors
are copied byte for byte from the pinned commit; UPSTREAM.md records the
source. Nothing builds them yet.
…B signer

libgpod 0.8.3 loads calcHashAB from an external hashab module in
LIBGPOD_BLOB_DIR and fails nano 6G/7G writes without it. A small patch
replaces that loader with a direct call into the vendored sources, which
prepare-libgpod-nano.sh copies into the libgpod tree before autoreconf.
They are built as an internal convenience library with -fwrapv, and
their global symbols are renamed and hidden, so that they neither clash
with other code linked with libgpod nor become part of its interface.
The macOS developer build and the static prebuild dependencies both
apply the patch. The signer only accepts an eight-byte FireWire GUID, so
the patch checks for 16 hex digits first and fails the write otherwise.

The static dependency cache now records a fingerprint of the patch and
the vendored sources, and the CI cache keys hash all of tools/prebuild,
so either change rebuilds libgpod. macOS static links name the system
sqlite3 and libxml2 that libgpod already uses, and Linux static links
group the archives, so standalone executables link too.

tools/nano7 holds the checks: a read-only probe that verifies the
signatures of a copied Locations.itdb/.cbk pair and iTunesCDB, including
the hashing scheme libgpod writes, tests against the upstream vectors
and a reference checksum book, and a libgpod roundtrip for nano 6G/7G
that the prebuild workflow runs. ADR-030 records the decision. Both
generations stay read-only.
The support notes, unsupported-device reasons and docs still said
libgpod cannot produce the nano 6G database format or needs an external
hashab blob. With the signer linked in, the remaining blocker is
different: on a nano 7G the firmware shows no music for a library libgpod
writes, even though its signatures verify. The nano 6G write path is
covered only by synthetic tests.

Both generations stay read-only. The notes and reasons now say that
syncing is not supported yet and, for the nano 7G, what the firmware
shows; the generated support matrix, the supported-devices page and the
device notes follow, the generation notes in docs/formats/generations.md
and docs/test-devices.md point to ADR-030, and the classify/identify
tests track the new reason. The generation comments drop the test
devices' serial numbers.

This branch has not been deployed

No deployments
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