Skip to content

feat(cargo-nx): read, verify, and extract an NCA or NSP - #78

Merged
LNSD merged 1 commit into
mainfrom
lnsd/hactool-subcommand
Aug 18, 2026
Merged

LNSD merged 1 commit into
mainfrom
lnsd/hactool-subcommand

Conversation

@LNSD

@LNSD LNSD commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

The workspace could pack a title but never open one, so nothing checked that what hacbrewpack sealed could be read back. This adds the inverse path on top of nx-object's new read modules, and keeps the crypto on this side of that boundary, since a keyset is secret material the crate deliberately refuses to hold.

  • Add unpack::nca, decrypting a header with AES-XTS, unwrapping the key area at the generation the archive itself names, and applying AES-CTR to the sections the header marks
  • Add unpack::verify, checking the header signature, each FS header's recorded hash, and each section's contents against the PFS0 hash table or IVFC tree covering them
  • Add the hactool subcommand, reporting an archive and extracting its executable partition, filesystem, or raw sections; entry names are rejected before they reach the filesystem so a crafted image cannot write outside the output directory
  • Report an unsigned header as absent rather than failed, because only a title's program archive is signed and calling the others broken misreads a correctly packed title
  • Move keyset::file and signing to the crate root, now that packing and reading share the keyset search path and the built-in keypair

The workspace could pack a title but never open one, so nothing checked that what `hacbrewpack` sealed could be read back. This adds the inverse path on top of `nx-object`'s new read modules, and keeps the crypto on this side of that boundary, since a keyset is secret material the crate deliberately refuses to hold.

- Add `unpack::nca`, decrypting a header with AES-XTS, unwrapping the key area at the generation the archive itself names, and applying AES-CTR to the sections the header marks
- Add `unpack::verify`, checking the header signature, each FS header's recorded hash, and each section's contents against the PFS0 hash table or IVFC tree covering them
- Add the `hactool` subcommand, reporting an archive and extracting its executable partition, filesystem, or raw sections; entry names are rejected before they reach the filesystem so a crafted image cannot write outside the output directory
- Report an unsigned header as absent rather than failed, because only a title's program archive is signed and calling the others broken misreads a correctly packed title
- Move `keyset::file` and `signing` to the crate root, now that packing and reading share the keyset search path and the built-in keypair

Signed-off-by: Lorenzo Delgado <lnsdev@proton.me>
@LNSD
LNSD added this pull request to the merge queue Aug 18, 2026
Merged via the queue into main with commit 61cdc9b Aug 18, 2026
3 checks passed
@LNSD
LNSD deleted the lnsd/hactool-subcommand branch August 18, 2026 07:56
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