Skip to content

Milestone 1: ingest (containers, keys, NCA, ExeFS, RomFS)

Objective

Open a user-supplied encrypted game (XCI/NSP) with the user's prod.keys and expose the Program NCA's ExeFS (main, rtld, sdk, subsdk*, main.npdm) and RomFS file table. Foundation for analysis/rewrite (milestone 2).

Files

  • Cargo.toml, .gitignore (blocks keys/games/NCAs from being committed)
  • crates/napple-core/src/domain/{error,keys}.rs: errors (never contain key material); KeySet (zeroized on drop, redacted Debug, 1 MiB cap, derives missing header_key/titlekek_XX/key_area_key_*_XX from source keys)
  • crates/napple-core/src/infrastructure/{io,pfs,xci,ticket,nca,romfs}.rs: adapters
  • crates/napple-core/src/application/ingest.rs: ingest use case
  • crates/napple-cli/src/main.rs: napple ingest <game> -k <prod.keys>
  • crates/napple-core/tests/ingest.rs: synthetic-fixture tests

Logic

PFS0/HFS0 parse -> NCA3 header (AES-XTS, Nintendo big-endian sector tweak) -> key area (ECB) or ticket titlekey (titlekek) -> AES-CTR section view with random access (counter = section ctr || offset>>4) -> PFS0 master-hash check (doubles as key-correctness check) -> ExeFS read; IVFC data level -> RomFS dir/file tables.

Decisions

  • Rust (safety on hostile input, speed, static binary, iOS-ready). Readers are Source trait objects with range-checked reads; no unsafe.
  • Hostile-input limits: file-count/strtab caps, name validation (no /, \, NUL, ..), RomFS depth and entry budget.
  • Update-only/unsupported NCAs are skipped, missing keys abort with the key's name.

Risks / pending

  • Validated against one retail NSP (Link's Awakening, keys from firmware 22.1.0): PFS0 master hash verified, 6 ExeFS files and 3836 RomFS entries listed, ~0.1 s. Still unvalidated: XCI, rights-id-less NCAs, other key generations, NSO contents (not yet parsed). The first real run exposed a ticket body-offset bug (signature padding 0x13C, not 0x140) that the synthetic fixtures had mirrored.
  • Synthetic fixtures only prove consistency. Fixtures are built by the same author as the parser, so they prove consistency, not compatibility. Needs a real game + firmware keys (owner will provide). Likeliest fault points: CTR counter layout, key-generation index, derived-key formulas, ticket offsets.
  • Not implemented: NSZ, BKTR/updates, NCA0/2, XTS sections, personalized tickets, IVFC full verification.
  • Whole-file ExeFS reads are in memory (small); RomFS is streamed.

Tests

cargo test: 11 passing (key parsing/redaction; NSP key-area, NSP ticket, XCI; wrong header/area key; tampered hash; missing key; path traversal; truncated/garbage inputs). Recommended next: run against 1 retail NSP and 1 XCI.