mirror of
https://github.com/manos555555/PS5-Suite.git
synced 2026-10-06 16:00:32 +02:00
53 KiB
53 KiB
Implementation Status
This document describes the current LibProsperoPkg package-building and reading capabilities. It is a public technical status file: it lists implemented behavior, known limits, and remaining work without process notes.
Implemented
Container format
- Builds the outer PFS plus the
\x7FCNTmetadata container with big-endian header, entry table, and entry-name table. - Reads
\x7FCNTand finalized\x7FFIHpackages throughProsperoPkgReader, including header fields, content id, entry table, and entry names. - Produces containers that parse back with the expected PS5 stamping.
ProsperoPackageBuilder.Buildruns end to end from a source folder through inner PFS image creation, the data-first inner image, AES-XTS outer PFS,\x7FCNT, metadata signature, and\x7FFIHfinalization.
Inner PFS image
- Lays out a prepared folder into a plaintext inner PFS image that round-trips through the reader. The superblock version is always 2 (PS5).
- Supports AES-XTS encryption over 0x1000-byte sectors using SHA3-256 EKPFS derivation. Tweak and data keys are derived from EKPFS plus the image header seed. The header block remains plaintext, and encrypted images decrypt to the original image.
- The package build path always stores the inner
pfs_image.datas the data-first image: a raw concatenation of per-file payloads (raw or headerless Kraken) with the geometry described by a generatednaps_pkg_layout.dat. Application payloads compress to compact Kraken blocks; keystone and executable modules are stored raw, as is any file the Kraken result does not shrink. - The PFSC container codec (
LibProsperoPkg.PFS.Compression.ProsperoCompressedPfsImage, with pack, unpack, format-check and validation helpers) is used to compress the inner metadata block and by the standalone PFSC pack/unpack tool.
Outer PFS encryption and signing
- Implements the PS5 finalized-image key schedule for
nwonly: SHA3-256 EKPFS plusnew_crypttweak/data keys over 0x10000-byte sectors numbered by image block. Public API:PFS.ProsperoPfsKeys.DeriveEkpfs,DeriveImageEncryptionKeys, andDeriveImageSignKey. - Decrypts an outer image to coherent plaintext and re-encrypts it deterministically across the encrypted blocks.
- Uses AES-128-XTS with one 0x10000-byte block per XTS unit. File-data blocks use the block index as the sector number; signed metadata blocks use the bit-47 sector flag; the superblock block remains plaintext.
- Implements per-block and dinode integrity hashes as
SHA3-256(plaintext block). Dinodes store the 32-byte hash and owning block index; the super-root inode stores the same shape for the inode-table block. - Implements the superblock
icvasSHA3-256(superblock[0:0x5a0])with the 32-byteicvfield zeroed during the computation. - Implements the data-first outer-PFS structure generator in
PFS/ProsperoOuterPfsBuilder.cs. It builds file-data blocks, a plaintext superblock, inode table, super-root directory entries,\x7fFLTflat-path table, andurootdirectory entries. - The generated plaintext and encrypted output follow the 11-block layout.
Metadata signing
- Signs package metadata with RSA-3072, PKCS#1 v1.5, and SHA-256.
- Performs EKPFS and PFS key derivation as part of signing.
- Verifies the published key fingerprint and a sign/verify round-trip.
Finalized debug image and FIH
- Wraps a
\x7FCNTcontainer into a finalized debug\x7FFIHimage with signed byte0x00. - Writes the structural fields: magic, signed byte, PFS image offset and size, embedded CNT offset and size.
- Produces a reader-round-trippable
FullDebugimage with signed byte0x00, PFS image offset0x10000, block-aligned PFS image size, and embedded CNT offset inside the file. - Supports the PS5 data-first finalized layout: FIH header, outer PFS image, plaintext superblock at a non-zero image block, CNT body, and optional install-metadata archive.
- Uses the trailing metadata archive as optional debug install metadata. The debug variant is a plain ZIP with stored entries; the encrypted retail variant is not produced.
Digests
- Uses
SHA3-256for finalized-image and CNT digest values listed here. - Computes the
game-digest/ innersblock-digestasSHA3-256of the plaintext outer superblock block at the offset stored in FIH. - Computes
package-digestasSHA3-256(CNT[0:0xFE0])and writes it atCNT+0xFE0. - Computes the CNT-header rollup as
SHA3-256(CNT[off:off+size]), whereoff = BE64(CNT+0x20)andsize = BE32(CNT+0x1c). - Computes
body-digestasSHA3-256(CNT body)andfixed-info-digestasSHA3-256(FIH block). - Builds the per-entry digest table (entry
0x0001) asSHA3-256(entry payload)for each entry, with the digest table's own slot left all-zero. This covers all 13 entries. - Computes the CNT GeneralDigests block (entry
0x0080,set_digests = 0x10DE, length0x1E0):content-digest = SHA3-256(CNT[0x40:0x78] || game-digest || major-param(32 zeros)).header-digest = SHA3-256(CNT[0:0x40] || CNT[0x400:0x480]), withCNT+0x410forced to the FIH-relative0x10000value.system-digestandplaygo-digestasSHA3-256of concatenated per-entry digests for the matching CNT entries in ascending id order.param-digest = SHA3-256(param.json payload).targetas a copy ofgame-digest.
- Computes FIH
0xB0nested-image-content digest asSHA3-256of the uncompressed inner PFS image at its logical size. The CNT build path threads this preimage through finalization, so the FIH value and CNTpfs_signed_digestare mutually consistent. The standalone finalization path without an inner image falls back to an outer-image hash. - Computes
imagedigs.dat(CNT entry0x040A, unnamed) as anN * 32byte table, one digest per 64 KiB outer-image block. It is stored as an outer-CNT body entry, not as an innersce_sysfile. Each stored 32-byte digest is written in opposite byte order. The build patches the captured per-block descriptor digests afterWriteImage. - All populated digest slots are generated from finished on-disk CNT and image data. An independently built package remains internally self-consistent; compressed inner-image bytes can vary by input and layout choices.
sce_sys files
- Injects
about/right.sprxinto the inner PFS. Aright.sprxsupplied in the source tree is packed verbatim; an embedded debug module is injected only when the source provides none.ProsperoPkgBuilder.EnsureAboutRightSprx. - Reads and produces UCP archives (
trophy2/trophyNN.ucp,uds/udsNN.ucp) throughContent.ProsperoUcp. The codec parses and rebuilds both archive kinds while preserving payloads and digests, including the SHA-1 integrity digest. Public API covers reading, building from entries, building from a directory, structural validation, digest verification, and digest repair. During a build,ProsperoPkgBuilder.EnsureUcpArchivesrepairs a stale digest on a supplied.ucpfile but never synthesizes archive contents. - Validates backend-signed system files before packing them, through
PKG.ProsperoSystemFiles.npbind.dat(532 bytes, magic0xD294A018) is checked and its communication id extracted from the TLV chain;nptitle.dat(160 bytes, magicNPTD) is checked and its title id extracted;license.dat/license.inforequire a non-empty payload. Invalid inputs stop the build with a descriptive error. - Emits
playgo-chunk.dat(CNT entry0x1001),playgo-hash-table.dat(0x2010), andplaygo-ficm.dat(0x2011) as outer-CNT body entries. - Builds
playgo-hash-table.datas a content-independent constant structure with size0x38 + n * 8, wheren = ficmCount / 2. - Converts source icon/picture PNGs to
icon0.dds,pic0.dds,pic1.dds, andpic2.ddsas BC7_UNORM DX10 textures throughPKG.ProsperoDdsEncoder.PKG.ProsperoPngDecoderdecodes the PNG (all color types, bit depths 1/2/4/8/16, every scanline filter, Adam7 interlace, andtRNS) to an 8-bit RGBA surface, which is block-compressed to BC7 behind the 148-byte DX10 header. The 148-byte header and the output file size follow the DX10 layout; the BC7 payload is a valid re-encoding of the same surface. - Packs any backend-authored system file supplied under
sce_sys/whose relative path maps to a known CNT id as an outer-CNT body entry:license.dat/license.info(0x0400/0x0401),nptitle.dat(0x0402),npbind.dat(0x0403),selfinfo.dat(0x0404),origin-deltainfo.dat/target-deltainfo.dat(0x0408/0x0407),pubtoolinfo.dat(0x1007),pronunciation.xml/.sig(0x1004/0x1005),changeinfo/changeinfo*.xml(0x1260+), thekeymap_rp/image set (0x1600+), andtrophy/archives. These files are excluded from the inner PFS and stored verbatim; the library never fabricates them.CollectMediaEntriesinProsperoPkgBuilder.
Application type and generated param.json
- Models the application type through
ProsperoApplicationType(NotSpecified= 0,PaidStandaloneFullApp= 1,UpgradableApp= 2,DemoApp= 3,FreemiumApp= 4).ProsperoApplicationTypesmaps each type onto the PS5sce_sys/param.jsonapplicationDrmTypebucket (free/standard/freemium) and exposesDisplayNameand a case-insensitiveParse. - When the builder generates a minimal
param.json(source folder has none),ProsperoBuildOptions.ApplicationTypeselects the emittedapplicationDrmType;ApplicationDrmTypeandContentBadgeTypeallow explicit overrides. An existingsce_sys/param.jsonis always used verbatim. Thepfsimage.xml<application-type>mirrors the resolvedapplicationDrmType.
param.json document model
Metadata.ProsperoParamreads, edits, creates, and writes the fullsce_sys/param.jsondocument through a liveJsonObjectso unrecognised keys and property order survive a round-trip.Parse/Loadaccept an existing document;Createstarts an empty one;Save/ToJsonserialize as UTF-8 without BOM at 2-space indent.ToJson()preserves parsed property order;ToJson(sorted: true)emits a canonical key order for a fresh document. Non-ASCII values are written as literal UTF-8.- Typed accessors cover the 34 recognised top-level keys (
ProsperoParamKeys): the identifiers (titleId,contentId,conceptId,masterVersion,contentVersion,versionFileUri), the localized-title block (localizedParameterswith per-languagetitleNameobjects anddefaultLanguage), the age/country map (ageLevel), the DRM/type buckets (applicationDrmType,applicationCategoryType,contentBadgeType,attribute,attribute2,attribute3,attributeInternal), the sizing hints (downloadDataSize,applicationDataSize), the deep-link and service keys, and thepubtoolinfo/kernel/localizedParametersnested objects. Metadata.ProsperoParamEnumsexposes the constrained vocabularies for validated fields: the threeapplicationDrmTypetokens (standard/free/freemium), the two deep-link intent tokens (launchActivity/joinSession), the 30 language codes used bylocalizedParametersanddefaultLanguage, and the 70 country codes used byageLevel.
manifest.json document model
Metadata.ProsperoManifestreads, edits, creates, and writes the framework-applicationmanifest.jsondocument through a liveJsonObjectthat preserves property order and unrecognised keys.Parse/Load/Create/Save/ToJsonmirror the param model; the document serializes as UTF-8 without BOM at 4-space indent, andToJson(canonical: true)emits the fixed field order a fresh document uses.- Typed accessors cover the recognised keys (
ProsperoManifestKeys):applicationName,applicationVersion,commitHash,bootAnimation,titleId,repositoryUrl,reactNativePlaystationVersion,twinTurbo, and the optionalenableHttpCache/enableAccessibilityflags, plus the nestedapplicationDatablock with itsbranchType.
SELF container
- Parses the SELF (Signed ELF) container through
Content.ProsperoFself: header, segment table, embedded ELF header and program headers, and the extended-info block (authority id, program type, app and firmware version, digest). - Generates a fake-self from any 64-bit ELF with
MakeFself. A digest/data segment pair is emitted for each program header whose file size is non-zero and whose type isPT_LOAD, module-data (0x61000000), relro (0x61000010), or comment (0x6FFFFF00), in program-header index order. Header size, metadata size, segment layout, and 16-byte data padding preserve the source module's field layout. - Sets the extended-info digest to
SHA-256of the input ELF and derives the authority id and program type from the ELF type and the byte at file offset0x3f00. Digest and signature slots on the fake path are zero-filled. - Normalizes the input module header before wrapping, on by default (
FselfOptions.NormalizeHeader).MakeFselfworks on a private copy and callsProsperoElfHeader.NormalizeForModule, so a self-authored module built by an ordinary toolchain wraps without a manual header fix-up and the caller's buffer stays unchanged. The step is a no-op for a module whose header is already correct, and it can be turned off. - Round-trips through the container parser: the type-based segment selection preserves each module's content-segment set, and every data segment matches the source program-header payload.
IsSelf,IsElf,Parse,Validate, andMakeFselfform the public API. Package builds continue to embed a fixedright.sprxasset when the source provides none; the generator is a standalone capability for arbitrary ELF input.- The build pipeline can fake-sign the source tree before packing, producing an installable fake package (fPKG).
ProsperoBuildOptions.FakeSignSelfModulesconverts every raw 64-bit ELF module in the source folder (eboot.binand*.elf/*.prx/*.sprx) to a debug fake-self throughMakeFself, driven by an optionalFselfOptions(app/firmware version, authority-id override). Files that are already SELF are left untouched. The conversion is non-destructive: the original module bytes are saved and restored after packing (in afinallyblock), so the source tree is unchanged when the build finishes or throws.ProsperoPackageBuilder.PrepareFakeSelfModules/RestoreFakeSelfModules.
SELF authentication-info sidecar
- Parses the SELF authentication-info sidecar (
*.auth_info) throughContent.ProsperoSelfAuthInfo: a fixed 0x88-byte record holding the program authority id (paid, offset0x00), four 64-bit capability words (0x08), four 64-bit attribute words (0x28), and a 0x40-byte reserved tail (0x48), all little-endian. This is the on-disk form of the record that the privileged auth-info query fills at runtime; it is separate from the authority id in the SELF extended-info block. Parse/Read/ReadFiledecode a record and reject any buffer shorter than 0x88.Paid/AuthorityId,Capabilities,Attributes, andReservedexpose the decoded fields.Category(withIsFakeAuthority/IsGenuineAuthority/IsPrivilegedSystem) reads the top-byte category (0x31fake,0x45genuine,0x48privileged) consistent with the authority-id model.Create(paid, capabilities, attributes, reserved)builds a record from supplied material, zero-extending short inputs;ToBytes/Write/WriteFileserialize back to the 0x88-byte form. Grant words are copied verbatim — the builder never fabricates capability or attribute bits.- Every sidecar record round-trips through the parser and writer, and a record rebuilt from decoded fields via
Createrestores the original bytes.
ELF header editor
- Reads and edits the 64-bit ELF header through
Content.ProsperoElfHeader: the identification bytes (class, data, OS/ABI, ABI version),e_type,e_machine,e_version,e_entry, the program- and section-header table fields, and the flags.Read/ReadFileparse the header; the typed properties and theIsExecutable/IsDynamic/IsModuleType/IsModuleReadypredicates report its shape. SetOsAbi,SetAbiVersion,SetType, andSetMachinerewrite single header fields in place.NormalizeForModuleapplies the minimum edits a module ELF needs before fake-signing: machine to x86-64, OS/ABI from System V or GNU to FreeBSD, and a placeholder type to executable, keeping any existing executable or dynamic type. It reports which fields changed throughElfNormalizeResult.- The editor touches only the 0x40-byte header; program headers, section headers, and segment data are unchanged, so a normalized module still hashes into the same extended-info digest
MakeFselfcomputes over the file body. It accepts 64-bit little-endian ELF input only and rejects anything else.ProsperoElfHeader.NormalizeForModuleis the preparation step ahead ofProsperoFself.MakeFself.
Keystone
- Computes the 96-byte
sce_sys/keystonefrom the passcode for version 2 and version 3. - Uses deterministic chained HMAC-SHA256:
KeyBlock1 = HMAC-SHA256(seed1, passcode_ascii)at0x20, thenKeyBlock2 = HMAC-SHA256(seed2, keystone[0x00:0x40])at0x40, with seed pairs selected by version. - The version-3 seed pair differs from the version-2 seed pair.
PlayGo
- Generates PlayGo and about-file outputs used by package builds.
- Builds
playgo-chunk.crcas CRC-32C (Castagnoli), reflected polynomial0x82F63B78, init/xorout0xFFFFFFFF, over each 64 KiB block of the finalized image from offset 0. Each checksum is serialized as little-endianuint32in block order. The trailing partial block containing the metadata archive and CRC file is excluded, avoiding self-dependency. Implemented byProsperoCrc32CandProsperoPlayGo.BuildChunkCrc.
NAPS streaming and Kraken inner compression
- Implements
ProsperoNapsLayoutas a parser and serializer fornaps_pkg_layout.dat. - The layout serializer round-trips a 544-byte record: 533 bytes of section content plus 11 trailing zero pad bytes.
- Implements the 16-byte layout header bit packing: file count, compression type, key count, shuffle-pattern count, uncompressed-block count, outer-block count, and compressed-block-info count.
- Implements the section order and strides: outer block digest (8 bytes), shuffle pattern (8 bytes), uncompressed offset by file index (6 bytes), compressed-info offset by uncompressed-block index (10 bytes), and compressed-block info (9 bytes).
- Implements both 9-byte compressed-block-info record formats and all bit offsets used by the 45-record layout.
BuildLayoutdefaults to 16-byte alignment.- Data-dependent NAPS record value generation is self-consistent for the library's own inner-image compressor output. Package-specific NAPS values depend on exact inner-image block sizes, so remaining differences are tied to inner-PFS layout generation rather than the compression codec.
- The data-first inner image is assembled by
ProsperoPs5InnerImageAssemblerfrom the file tree: inode table, afid assignment, dirents, both flat-path tables, per-file logical offsets, the data-first on-disk layout, and the inner-mount geometry.ProsperoNwonlyNapsGeneratorderives a validnaps_pkg_layout.datfor that image, and the builder places it as an outer-PFS file alongsidepfs_image.dat. - Implements a Kraken encoder and
KrakenDecoderunderPFS/Compression, plusProsperoCompressedPfsFileWriterandProsperoCompressedPfsFilefor the PFSC container. ProsperoCompressedPfsFileWriter.WriteCompressed(payload)output is accepted by a conformant decoder and round-trips for the covered cases: single chunk, multi-block payloads over 256 KiB, the exact 0x40000 boundary, two internal chunks per block with cross-chunk back-matches, and stored fallback for incompressible chunks.ProsperoCompressedPfsFile.Parse(pfs).Decompress()reconstructs the original payload in process for the same cases.- The encoder implements the excess mode for single long matches, including the control byte high bit and forward excess substream. Periodic-tile cases produce the expected output, while chunks with multiple over-long matches split them into valid shorter matches.
- The encoder enforces the newLZ rule that a match may not start in the last 16 bytes of a chunk. It caps match starts at
chunkEnd - 16, flushes the remainder as trailing literals, and falls back to stored chunks if needed. - Huffman entropy arrays are implemented and enabled by default through
KrakenHuffmanArrayEncoder. The literal, command, length, and offset streams are each Huffman-coded as type-2 arrays with the internal three-stream split, and fall back to raw when the Huffman form is not smaller. The offset array is written in single-table offset mode. KrakenDecoderreads raw and Huffman-coded literal/command/offset/length arrays, both code-length encodings, the 3-stream split, excess framing, both literal models, multi-chunk and multi-block payloads, and stored fallback. It decodes the embedded verification vectors and checks SHA3-256 of the decoded payloads.- Kraken
nwonlyinner compression is implemented and produces valid output that a conformant decoder accepts. The windowed Optimal3 parse uses best-of greedymml=4/3/8and seeded forward-DP by emitted size for single- and multi-chunk blocks, including cross-chunk back-references. The level-7 forward-DP implements sublength fill for matches below 128 bytes, thelongestReach >= 128lrl-loop exit, long-match parse-position skipping, and frontier commits for matches of 128 bytes or longer. The productionPackpath produces compact blocks for covered payloads. Any residual sub-optimality on some inputs is isolated to match-finder, seed, or code-cost selection rather than DP parse logic. - The
nwonlyinnerpfs_image.datdescribed bysce_suppl/.../pfsimage.xml<nested-image>contains metadata tables, executable modules (keystone,right.sprx,.bundle) stored raw, and compressed application payloads. Files such asicon0.dds,param.json, PlayGo files, andimagedigs.datare SC/container entries, not inner-image files. The inner block format uses a 16-byte PFSv3 boundary table, even/odd chunks up to 128 KiB, optional shuffle, and header-stripped Kraken blocks.ProsperoCompressedPfsFileWriterimplements the compress-vs-store rule inKeepCompressed: keep a compressed block only whencomp <= (uncomp * 15) >> 4(at least 6.25% saved).
Reader and writer support
ProsperoPkgReaderdetects both\x7FCNTand\x7FFIH, resolves embedded CNT data, and reports finalized debug images.ProsperoPkgReaderreads the finalized-image format-version field (FIH offset0x06, little-endian) and exposes it asProsperoFihHeader.FormatVersion, withIsSupportedFormatVersiontesting the value the mount path requires (3).ProsperoCntWriter,ProsperoPkgBuilder,ProsperoFihBuilder, and related builders write the package structures described above.ProsperoSiArchivegenerates the debug install-metadata ZIP container with stored entries, member paths,playgo-chunk.dat, structuralpfsimage.xmlfields, andplaygo-chunk.crc.- The SI segment (the trailing
sce_supplZIP) is emitted automatically by thenwonlybuild.ProsperoPkgBuildercaptures the deterministicpfsimage.xmloptions, the CNTplaygo-chunk.dat, and the block-aligned inner-image size during the CNT build;ProsperoPackageBuilderthen passes them toProsperoFihBuilder.BuildFromCntthrough ansiArchiveFactorythat callsProsperoSiArchive.BuildDebugSiSegmenton the finalized mount image. The produced segment carriespfsimage.xml(with the build's own self-consistent digests, entries and geometry), the fournaps_meta_300/301/302/308.datrecords (R = alignUp(pfs_image.dat) - 0x10000, captured at build time), the copiedplaygo-chunk.dat, and a deterministicconfig/<content-id>/playgo-chunk.crc(CRC-32C). Thenaps_meta_18.datmetric blob is built byProsperoNapsMeta.BuildMeta18over the finalized image and its content-file table and emitted in the segment. ProsperoSiArchive.BuildPfsImageXmlbuilds the descriptor structure through<config>,<digests>framing,<params>,<container>,<mount-image>, and<entries>. It includes derived long name, version constants, container geometry, extended mount-image fields, thepfs-image-seedblock, and CNT entries. Keyed digest rows that are not supplied remain zero placeholders with warnings.BuildPfsImageXmlalso emits the deep introspection trees<chunkinfo>,<pfs-image>(outer PFS), and<nested-image>(inner PFS).ProsperoPkgBuildercaptures the outer and inner inode layouts and the chunk geometry during the CNT build (ProsperoPfsBuilder.CaptureImageTree) and passes them into the SI options. The walk reflects only inodes actually materialized into each image: innersce_sysfiles that are packed as outer CNT entries (for exampleicon0.png) receive no inner inode and are correctly excluded from the<nested-image>tree.- The GP5 project model is parsed and emitted for both root-directory-walked and flat files/folders layouts.
License (rif)
- Reads, writes, and creates the per-title license file (
license/rif) throughLicense.ProsperoRif. Each record is a fixed0x400-byte structure with a big-endian header (magicRIF\0, version0x0002, flags, theQPaCformat tag, expiry, a 36-byte content id, an 8-byte format descriptor, an entry-count field) and a 448-byte encrypted key blob at offset0x240. Parse/Readdecode a single record;ReadAlldecodes a multi-title file (one record per sub-title) andWriteAllre-emits it.ToBytes/Writeserialize a record, andCreatebuilds a structural record for a content id, copying a supplied 448-byte key blob verbatim or leaving it zero.- Exposes
ContentId,TitleId(parsed from the content id),Expiry/IsNonExpiring,HasKeyBlob, and a structuralValidate. - Single-title records and multi-title files parse, validate, and round-trip through
ToBytes. - The 448-byte key blob is encrypted with per-console material and cannot be produced off-console, so
Createcovers the fake/debug path or templating from an existing blob; an existing entitlement blob is copied verbatim, never synthesized. ProsperoRif.ServiceLabelexposes the per-record service token — the content-id prefix before the first-— the value the console verify path reports as each record'sServiceID.
Content key and multi-content license set
License.ProsperoEntitlementKeymodels the 128-bit content key (entitlement_key) that the builder accepts as an alternative to a passcode. It carries the 16-byte value with hex parse/format (ParseHex/ToHex, optional0xprefix), a zero check, andValidate. It is a validated carrier for supplied material and never derives or forges a license key body.ProsperoEntitlementKey.ResolveMode(passcode, entitlementKey, out mode, out error)implements the builder's mutual-exclusivity rule: exactly one of a 32-character passcode or a content key must be supplied. Supplying both is rejected (entitlement_key must not be specified when a passcode is used), supplying neither is rejected, and a wrong-length passcode is rejected. The result selects the fake/debug schedule (passcode) or the finalized/keyed schedule (content key).License.ProsperoRifSetmodels a whole license file as the ordered set of0x400records (one per sub-title, concatenated with no container header) and models the console verify-path report: the record count (n_rif), the per-recordServiceID, the distinct service labels / title ids / content ids, and the whole-file-size rule (a positive multiple of0x400, guarded byunexpected ac_rif_file_size).ReadFile/Readdecode a file or buffer,FromRecordswraps an existing list, andValidateenforces a non-empty count, the size rule, and per-record validity.Summarize(appTitleId)produces the compactServiceID / rif_size(exp/act) / has_app / n_ac / n_rifprojection. Because the app-versus-additional-content split (n_ac) is not derivable from RIF fixed fields alone, the caller supplies the application title id (fromapp.json/param.json); records matching it count as the app and the remainder as additional content.- Single-title files report
n_rif=1/has_app=true/n_ac=0, and a three-record multi-title file reportsn_rif=3/has_app=true/n_ac=2at exactly3 × 0x400 = 3072bytes.
Debug license grant
License.ProsperoDebugLicensestates the debug grant for a content id + passcode in one place. A debug image derives its mount key from public inputs only, so the grant needs no license record:RequiresRifis always false.Create(contentId, passcode=null)builds the grant with the all-zero default passcode when none is supplied and validates the content id (1..0x24ASCII bytes) and passcode (32 characters).DeriveEkpfsreturns the 32-byte image key;DeriveKeySet(seed)returns the fullProsperoDebugKeySetfor a 16-byte superblock seed (EKPFS, AES-XTS tweak and data keys, and the 32-byte sign key);DeriveImageEncryptionKeys(seed)andDeriveImageSignKey(seed)expose the parts. All derivation delegates toPFS.ProsperoPfsKeys.ToStructuralRif(expiry)emits a zero-blob record for pipelines that expect a license file present. It holds no device secret.ProsperoBuildOptions.LicenseFreebuilds a DRM-free, license-free package in one option. It fake-signs raw ELF modules (asFakeSignSelfModulesdoes) and constructs theProsperoDebugLicensegrant for the content id and passcode, so the debug mount key is recomputed rather than granted and no rif is written.param.jsonapplicationDrmTypeis descriptive metadata and is not rewritten. The fake-sign step restores the original source bytes after the build.ProsperoBuildResult.LicenseFreeechoes the option andProsperoBuildResult.DebugLicensereports the grant. The output is a debug finalized image.ProsperoBackupConverter.Convertrepackages a decrypted application backup into a debug fPKG. It copies the app tree (excluding thedecrypted/mirror), substitutes each signed executable with its decrypted raw ELF at the same relative path, then builds a debug image with fake-signing on so the mount key derives from the content id and passcode. The fake-self wrap is the only transform applied: content segments and the dynamic-linker tables are carried unchanged, and the executables carry no in-module DRM boot gate. The backup is untouched; the converter works from a staging copy.ProsperoBackupConversionResultreports the output path, the debug grant, the substituted / plaintext / unresolved module lists, and aLaunchReadinessreport (below) over the assembled tree.
Launch-readiness inspection (ProsperoLaunchReadiness)
ProsperoLaunchReadiness.InspectAppRootreads an assembled application root and reports whether it meets the debug-launch conditions the console enforces: every executable module is a plaintext module the loader accepts (a fake-authority SELF or a raw ELF the builder fake-signs),eboot.binis present, and the metadata is aparam.jsonrather than a PS4param.sfo, which the launch service refuses. It classifies each module (InspectModule) by magic and authority id intoModuleAuthorityKind(RawElf,FakeAuthoritySelf,GenuineAuthoritySelf,UnknownAuthoritySelf,SignedEncrypted,NotExecutable), reportsWillRunOnDebugConsoleper module, and aggregatesIssuesplus anIsLaunchReadyverdict onProsperoLaunchReadinessReport. A signed/encrypted module, a missingeboot.bin, a missingparam.json, or a presentparam.sfoeach block readiness.RequiresDebugConsolerecords the one condition outside the package: a debug workspace mounts only on a debug-enabled console. The inspector reads only; it never signs, mounts, or launches.
Homebrew packaging (ProsperoHomebrewPackager)
ProsperoHomebrewPackager.Packageturns a compiled homebrew folder into an installable debug fPKG. It validates the module folder and the module file, reads the content id and version from the homebrew'ssce_sys/param.json(options override), constructs the debug grant for the content id and passcode up front so a malformed id fails before any file work, assembles a clean source tree (the module lands aseboot.bin, thesce_sys/tree is copied), and builds a finalized debug image throughProsperoPackageBuilder.BuildwithLicenseFree = trueand the data-first inner image. It reads the assembled tree back withProsperoLaunchReadiness.InspectAppRootand returns the report alongside the output path, the debug grant (RequiresRiffalse), the packed module path, and any warnings. The temporary tree is removed unlessKeepStagingis set.ProsperoHomebrewPackageOptionsneeds onlyHomebrewFolderandOutputFolder; the rest default.- The homebrew build produces an installable finalized image end to end: the
\x7FFIHheader with its accounting fields populated (inner-image and metadata block counts, data-region block count, content-version echo, outer file and flat-path-table counts), the\x7FCNTmetadata container, the data-first inner-image assembly, thenaps_pkg_layout.datouter file, thenaps_meta_18andnaps_meta_300/301/302/308metric records, and the trailingsce_supplinstall-metadata archive.
Disc-backup packages (app_0 / app_sc)
- Opens a split disc-backup package described by an
app.jsonmanifest throughDiscBackup.ProsperoDiscBackup.DiscBackup.ProsperoDiscBackupManifestparses the manifest (numberOfSplitFiles,originalFileSize,packageDigest, the orderedpieces[]withfileOffset/fileSize/url, and the PlayGo chunk-CRC pointer). - Reassembles the pieces on the fly with
DiscBackup.ProsperoConcatStream, a read-only seekable stream that concatenates the piece windows without materializing a temporary file.OpenPackageStreampresents the whole package as one stream;ReassembleTowrites it to a file or stream. - Reads the reassembled finalized (
\x7FFIH) image and its embedded\x7FCNTthrough the existingProsperoPkgReaderover the concat stream, including entries that straddle the split boundary.FindImageKeyEntrylocates the EEKPFS key entry (id0x20), andExtractEntry/ExtractEntryBytescopy any entry's stored bytes (encrypted entries stay encrypted). - Verifies integrity three ways:
VerifyPackageDigestrecomputes the reassembledSHA-256and compares it topackageDigest;VerifyChunkCrcHashchecks theSHA-256of the chunk-CRC file againstplaygoChunkCrcHashValue;VerifyChunkCrcsrecomputes every 64 KiB CRC-32C and reports the first mismatch.DiscBackup.ProsperoPlaygoChunkCrcparses the headerless little-endian CRC-32C array (one value per 64 KiB chunk). - For a split disc backup, the reassembled length equals
originalFileSize, the image begins with\x7FFIH, the embedded\x7FCNTis found across the piece split, the EEKPFS key entry is present and extractable, the chunk-CRC file hash matches the manifest, the chunk count equalsceil(originalFileSize / 64 KiB), and recomputed chunk CRC-32C values match the table.
Split-package merge (*_0 / *_1 / *_sc)
PKG.ProsperoPkgMergerreassembles a distribution-split package back into one finalized image.MergeDirectorydiscovers the split set in a folder, groups pieces by base name, orders the numbered pieces_0 .. _Nascending, and appends the_scmetadata piece last;Mergetakes an explicit ordered piece list. The merged output is the byte concatenation of the ordered pieces, streamed throughDiscBackup.ProsperoConcatStreamso no oversized temporary buffer is materialized.Validatechecks the split-set invariants before writing: the leading piece begins with a finalized (\x7FFIH) header whose format version is3, the signed byte selects the image type (0x80retail /0x00debug), the embedded-subcontainer offset in the header equals the shared PFS offset plus PFS size and equals the summed size of the numbered pieces, and the metadata piece carries the\x7FCNTsubcontainer the header locates.ProsperoPkgMergeValidationreports the resolved image type, offsets, and per-piece sizes;ProsperoPkgMergeResultreports the written length and optionalSHA-256.- Cutting a finalized image at its embedded-subcontainer offset and merging the pieces back reproduces the original image (
SHA-256match); an update split set validates with all invariants satisfied and the image type reported as full retail.
Acceptance-gate validation
PKG.ProsperoPkgValidatorchecks a parsed package against the structural preconditions the console mount path enforces. It returns aProsperoAcceptanceReportof namedPass/Warning/Failchecks with anAcceptedroll-up and aHasWarningsflag.- Checks: a finalized (
\x7FFIH) image is present (a bare\x7FCNTis not mountable), the FIH format version is3(the value the key-derivation path requires), the shared PFS image begins at0x10000and is non-empty, the embedded\x7FCNTparses, the EEKPFS key entry (0x20) is present, and the content id is present and — when an expected value such as ariforparam.jsoncontent id is supplied — matches. - This validates the structural gate only; it does not perform the console's cryptographic checks. Reports
Acceptedfor split disc-backup packages, with the embedded content id matching the correspondingrifcontent id. - The report also includes a
Content-infoline projected throughNpDrm.ProsperoNpDrmContentInfo(see below): the derived title id, drm/content type, content flags, patch kind, and nested-image flag.
NpDrm content-info
NpDrm.ProsperoNpDrmContentInfoprojects a package header into the compact classification the mount path consumes before it accepts an image. It exposes the container offset, content id, derived title id,DrmType(0x70),ContentType(0x74),ContentFlags(0x78),IsNestedImage,IsFinalized, and the decodedPatchKind(None/First/Subsequent/Delta/Cumulative) with anIsPatchroll-up.Read(path)/Read(stream)parse and project in one call;FromPackageprojects an already-parsedProsperoPkg.ResolveContainerOffsetmirrors the console's container-offset switch on the raw magic and version (\x7FCNT→0,\x7FLIH→ u640x30,\x7FFIH→ u640x58), andDeriveTitleIdextracts the title id from a content id.ProsperoPkgReadernow readsContentFlags(0x78, big-endian) intoProsperoPkgHeader, andDiscBackup.ProsperoDiscBackup.ReadContentInfoprojects the content-info from the reassembled image (the CNT metadata is carried by the tail piece, so this uses the full reassembled stream).- For every reassembled image the projected content id and title id match the manifest, the container offset equals the embedded-CNT offset,
IsNestedImageis set, and the patch kind is decoded from the content flags (base images reportNone; images carrying the subsequent-patch flag reportSubsequent).
Package extraction
PKG.ProsperoPackageExtractorextracts the application filesystem from a finalized image end to end. It reads the FIH header for the outer-PFS offset/size and signed byte, opens the AES-XTS outer PFS, locates the nestedpfs_image.dat, PFSC-decodes it to the inner image, opens the inner PFS, and writes every file with per-file PFSC decompression, confining all writes inside the output directory.PKG.ProsperoExtractionKeymodels the key material:FromPasscode(passcode)/FromPasscode(contentId, passcode)derive the outer EKPFS from public inputs (it materializes both the SHA-256 and SHA3-256 candidates and the extractor auto-selects whichever opens the outer PFS),FromEkpfs(bytes)takes a supplied 32-byte image key directly, andNoneattempts a plaintext outer PFS. It never derives or forges a retail image key.PFS.ProsperoPfsExtractoris the reusable single-image half: given an already-opened (and, if encrypted, decrypted)ProsperoPfsReader, it walks theuroottree and writes the files. The package extractor composes two of these around the middle PFSC decode.Inspect(path)reports the package type, retail flag, outer-PFS offset/size, whether the outer PFS is encrypted, and whether a supplied key is required — without needing any key.ListFiles(path, key)enumerates the inner filesystem without writing.Extract(...)returns a manifest (package type, retail flag, content id, EKPFS fingerprint, outer file count, inner-image-compressed flag, and the written entries).- A build/extract round trip holds: a debug image built from a known folder extracts to files with matching SHA-256 hashes, and the auto-selected EKPFS fingerprint matches the fingerprint the build pipeline reports for the same content id + passcode.
- A finalized retail image (signed byte
0x80) encrypts the whole outer PFS including block 0, so its superblock is unreadable without the console-provisioned image key.InspectreportsIsRetail = true/RequiresSuppliedKey = true, andExtractthrows a clear message naming the supplied-key requirement rather than returning a fabricated result.
Known gaps / not implemented
- Retail finalized images with signed byte
0x80are not implemented. They require console-side finalization material that the library does not have. - Retail install-metadata archives are not implemented. The retail variant is encrypted and is not produced by the library.
- On-console installation acceptance is not guaranteed. Library code verifies structure and round-tripping; acceptance depends on console mode and firmware.
- The
rifkey blob (offset0x240, 448 bytes) is encrypted with per-console material and cannot be produced off-console.ProsperoRif.Createbuilds a structural record and copies a supplied blob verbatim; it does not derive a retail entitlement blob. - The finalized/keyed image-key schedule is not implemented. The content key (
entitlement_key) is modeled as a validated carrier and the finalize sc encryption is a standard AES-128-CBC pass (deterministic given key and IV), but the step that seals the content key into the 448-byte license key body and the retail image-key unwrap both use per-device material absent from any host binary.ProsperoEntitlementKeytherefore carries supplied material only and never fabricates a seal. - The EEKPFS key entry (
0x20) extracted from a disc-backup package is returned as stored (encrypted). Off-console PFS key derivation from that entry is not implemented; extraction preserves the bytes for inspection only. - Package extraction of a finalized retail image is console-gated. The outer PFS of a retail image is encrypted at block 0 with the image key delivered through the entitlement/kernel path, which is absent from any host binary and is neither derivable from public inputs nor brute-forceable (AES-128 / RSA-2048 wrap).
ProsperoPackageExtractortherefore extracts debug/keyed images and any image whose 32-byte image key is supplied, and refuses a retail image without a supplied key with a clear message. - The data-first outer generator,
naps_pkg_layout.datemission,naps_meta_*generation, and\x7FFIHassembly are implemented for the data-first path. The full streaming outer generator for arbitrary inputs is not complete: remaining pieces include rolling/weak/strong deduplication, block shuffle, per-outer-block encryption/CRC/digest integration, and fullpfsimage.xmlnamed-digest population for all package shapes. - NAPS layout record values are not fully generated from arbitrary input. The format parser and serializer are implemented, but values derived from exact compression bookkeeping are only self-consistent for this library's own compressor output.
ProsperoNapsMetabuildsnaps_meta_300/301/302/308.dat(48-byte records) from the build's own inner-image size andnaps_meta_18.dat(the AES-128-XTS TLV metric blob) from the finalized image and its content-file table, and emits both in the SI segment automatically.- The
pfsimage.xml<chunkinfo>,<pfs-image>, and<nested-image>introspection trees are emitted from the build's own captured outer/inner-PFS inode layout. The outer superblock<icv>is the captured superblock HMAC and the<seed>is all-zero. Because the builder writes a superblock-first outer PFS, reported block indices and metadata offsets reflect that layout. The nested<metadata>pseudo-element and per-filepoffsetare intentionally omitted because compressed inner content does not provide stable values. These sections live in the supplementalsce_supplZIP that the console loader does not read, so they do not affect installability. - Keyed or console-produced
pfsimage.xmldigest members in the install-metadata archive are supplied by the caller or left as placeholders; they are not fabricated. - The Kraken inner compression codec implements level-7 block generation for covered
nwonlyinner-image files. Application payloads compress to compact blocks, and incompressible or executable modules are stored raw when compression is not beneficial. - Whole
nwonlypackage generation depends on non-Kraken factors: the inner PFS layout, suppliedsce_sysinputs, and the recorded PFS build timestamp. Only a subset ofsce_sysentries is derivable from the loose source folder and passcode; caller-supplied files are copied as provided. - The data-first inner PFS format stores per-file Kraken-compressed data in a single PFS with the superblock at block
Ndblock-1. Each compressible file's inode carries thecompressedflag and stores standalone Kraken block data at the file's data offset; incompressible files are stored raw.ProsperoPs5InnerImageAssemblerassembles the image: per-file compression, data-first block ordering, compressed-inode block tables, and the per-file compress-vs-store decision. naps_pkg_layout.datis generated for the data-first build path (ProsperoNwonlyNapsGenerator) and placed as an outer-PFS file; it describes the outer download-stream block layout. The generated layout is valid for inputs whose compression schedule the emitted layout describes; arbitrary-input dedup/shuffle bookkeeping is not yet generated.ProsperoNapsLayoutremains the round-trip parser/serializer.
Summary table
| Capability | Status |
|---|---|
\\x7FCNT build |
Implemented |
\\x7FCNT / \\x7FFIH read |
Implemented |
| End-to-end debug package build | Implemented |
| Inner PFS layout | Implemented |
| Inner PFS AES-XTS encryption | Implemented |
| Kraken PFSC v3 container codec | Implemented; produces compact level-7 blocks for covered inputs |
Data-first inner image + naps_pkg_layout.dat emission |
Implemented; ProsperoPs5InnerImageAssembler assembles the per-file data-first image and ProsperoNwonlyNapsGenerator emits the layout |
| Kraken decoder | Implemented for the covered blocks |
| Kraken Huffman encoder arrays | Implemented |
| PS5 outer-image key derivation | Implemented |
| PS5 outer-image AES-XTS encryption | Implemented |
PS5 outer-PFS signing hashes and icv |
Implemented |
| PS5 outer-PFS data-first structure generator | Implemented |
| Metadata signing | Implemented |
Finalized debug image (\\x7FFIH) |
Implemented |
Finalized digest table: game-digest / superblock digest |
Implemented |
| CNT per-entry, body, fixed-info, param, package, and header-rollup digests | Implemented |
| CNT GeneralDigests block | Implemented and self-consistent |
FIH 0xB0 nested-image-content digest |
Implemented; self-consistent, exact value depends on exact inner compression bytes |
imagedigs.dat CNT entry |
Implemented |
Fake-self generation (MakeFself) |
Implemented |
SELF authentication-info sidecar (ProsperoSelfAuthInfo: read / validate / build / write *.auth_info) |
Implemented; round-trip verified, grant words supplied verbatim |
ELF header editor (ProsperoElfHeader: read / edit OS/ABI, type, machine; normalize a module ELF) |
Implemented; round-trip verified |
Fake-sign build option (FakeSignSelfModules, fPKG) |
Implemented; non-destructive in-place conversion |
Application type / generated param.json app-type |
Implemented; ProsperoApplicationType maps to applicationDrmType |
param.json document model (ProsperoParam: read / edit / create / write) |
Implemented; order-preserving round-trip, canonical order for fresh documents, typed accessors and constrained vocabularies |
manifest.json document model (ProsperoManifest: read / edit / create / write) |
Implemented; order-preserving round-trip at 4-space indent, typed accessors and nested applicationData |
Supplied sce_sys system files (license, np, self, delta-info, keymap_rp, changeinfo, pronunciation, trophy) |
Implemented; packed verbatim as outer CNT entries when present |
playgo-chunk.dat, playgo-hash-table.dat, playgo-ficm.dat |
Implemented |
UCP archives (trophy2/*.ucp, uds/*.ucp) |
Implemented, round-trip and digest verified |
npbind.dat / nptitle.dat structural validation |
Implemented; validated and identifiers extracted, packed verbatim |
playgo-chunk.crc |
Implemented |
| Debug install-metadata (SI) archive | Implemented; produces naps_meta_18 / naps_meta_300…, pfsimage.xml, playgo-chunk.dat and playgo-chunk.crc |
pfsimage.xml structural descriptor |
Implemented, including <chunkinfo>/<pfs-image>/<nested-image> trees; self-consistent, supplied keyed digest rows remain placeholders |
Keystone (sce_sys/keystone) |
Implemented from passcode for version 2 and version 3 |
PNG to BC7 DX10 DDS conversion (icon0 / pic0 / pic1 / pic2) |
Implemented; 148-byte DX10 header and file size follow the DX10 layout, BC7 payload re-encoded from the same surface |
| GP5 project model | Implemented |
| NAPS layout parser and serializer | Implemented; the data-first build path emits a valid layout via ProsperoNwonlyNapsGenerator |
| NAPS streaming outer generator | Data-first outer generation implemented; full streaming dedup/shuffle for arbitrary inputs incomplete |
NAPS metric metadata (naps_meta_18 / naps_meta_300/301/302/308) |
Implemented; built by ProsperoNapsMeta and emitted in the SI segment |
| Retail install-metadata archive | Not implemented |
Retail finalized image (0x80) |
Not implemented |
| On-console acceptance guarantee | Not implemented; depends on console mode and firmware |
License (rif) read / write / create |
Implemented; validated and round-trip verified for single- and multi-title files |
Disc-backup open / reassemble (app_0 + app_sc) |
Implemented |
| Disc-backup digest and chunk-CRC verification | Implemented; SHA-256 package digest, chunk-CRC file hash, and 64 KiB CRC-32C recompute |
| Disc-backup embedded-CNT entry extraction | Implemented; stored bytes copied, encrypted entries stay encrypted |
Split-package merge (*_0 + *_1 + *_sc) |
Implemented; invariant-validated |
FIH format-version read (ProsperoFihHeader.FormatVersion) |
Implemented |
NpDrm content-info projection (ProsperoNpDrmContentInfo) |
Implemented |
Content-flags read (ProsperoPkgHeader.ContentFlags, 0x78) |
Implemented |
Patch-kind classification (None / First / Subsequent / Delta / Cumulative) |
Implemented |
Acceptance-gate structural validation (ProsperoPkgValidator) |
Implemented; structural gate only, not console cryptographic checks |
Content key model (ProsperoEntitlementKey) + passcode/entitlement-key mode rule |
Implemented; validated carrier for supplied material, never forged |
Multi-content license set (ProsperoRifSet: n_rif / ServiceID / has_app / n_ac / size) |
Implemented |
Debug license grant (ProsperoDebugLicense: derived key set, no rif required, structural zero-blob rif) |
Implemented; derivation and round-trip verified |
DRM/license-free build option (LicenseFree: fake-sign + derived mount key, no rif) |
Implemented; round-trip verified, source restored |
Decrypted-backup to debug fPKG conversion (ProsperoBackupConverter: substitute executables, fake-sign, derive mount key) |
Implemented; round-trip verified across the backups |
Launch-readiness inspection (ProsperoLaunchReadiness: module authority classes, eboot presence, param.json-not-param.sfo, debug-console requirement) |
Implemented; round-trip verified |
Homebrew module to installable debug fPKG (ProsperoHomebrewPackager: assemble source tree, license-free build, launch-readiness check) |
Implemented; end-to-end verified |
Per-record service label (ProsperoRif.ServiceLabel) |
Implemented |
| Finalized/keyed license key-body seal + retail image-key unwrap | Not implemented; console-gated, supply verbatim |
Package extraction (debug/keyed image → outer PFS → pfs_image.dat PFSC → inner PFS → files) |
Implemented; verified by build → extract round trip |
Package extraction key model (ProsperoExtractionKey: passcode / supplied EKPFS / none) |
Implemented; derives debug EKPFS from public inputs, never forges a retail key |
Package inspection without a key (ProsperoPackageExtractor.Inspect) |
Implemented; type / retail flag / outer-PFS offset+size / encrypted / key-required |
| Package extraction of a finalized retail image (block-0 encrypted) | Not implemented; console-gated, refused cleanly unless the image key is supplied |