NCSD: Difference between revisions
|  →InitialData:  Fix struct being too large caused by having redundant data | |||
| (29 intermediate revisions by 12 users not shown) | |||
| Line 3: | Line 3: | ||
| == Overview == | == Overview == | ||
| There are two known specialisations of the NCSD container format. The CTR Cart Image (CCI) format and the CTR System Update (CSU) | There are two known specialisations of the NCSD container format. The CTR Cart Image (CCI) format and the 3DS' raw [[Flash Filesystem#NAND structure|NAND format]]. CCI is the format of game ROM images. | ||
| '''CTR System Update (CSU)''' is a variant of CCI, where the only difference is in the file extension. This is used with developer System Updates and associated [[3DS Development Unit Software|Tools]]. | |||
| NCSD images start with a NCSD header, followed by up to a maximum of 8 [[NCCH]] partitions. | NCSD images start with a NCSD header, followed by up to a maximum of 8 [[NCCH]] partitions. | ||
| For CCI | For CCI images, the partitions are reserved as follows: | ||
| {| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
| Line 22: | Line 25: | ||
| | 2 | | 2 | ||
| | [[Download Play]] Child container ([[NCCH#CFA|CFA]]) | | [[Download Play]] Child container ([[NCCH#CFA|CFA]]) | ||
| |- | |||
| | 6 | |||
| | New3DS [[System_Update_CFA|Update Data]] ([[NCCH#CFA|CFA]]) | |||
| |- | |- | ||
| | 7 | | 7 | ||
| Line 54: | Line 60: | ||
| |  0x110 | |  0x110 | ||
| |  8 | |  8 | ||
| |  Partitions FS type | |  Partitions FS type (0=None, 1=Normal, 3=FIRM, 4=AGB_FIRM save) | ||
| |- | |- | ||
| |  0x118 | |  0x118 | ||
| |  8 | |  8 | ||
| |  Partitions crypt type   | |  Partitions crypt type (each byte corresponds to a partition in the partition table) | ||
| |- | |- | ||
| |  0x120 | |  0x120 | ||
| |  0x40=(4+4)*8 | |  0x40=(4+4)*8 | ||
| |  Offset & Length partition table | |  Offset & Length partition table, in media units | ||
| |- | |||
| |  0x160 | |||
| |  0xA0 | |||
| |  ... | |||
| |} | |||
| For carts, | |||
| {| class="wikitable" border="1" | |||
| |- | |||
| !  Offset | |||
| !  Size | |||
| !  Description | |||
| |- | |- | ||
| |  0x160 | |  0x160 | ||
| Line 85: | Line 103: | ||
| |- | |- | ||
| |  0x1D0 | |  0x1D0 | ||
| |   | |  0x20 | ||
| |  Reserved | |  Reserved | ||
| |- | |||
| | 0x1F0 | |||
| | 0xE | |||
| | Reserved? | |||
| |- | |||
| | 0x1FE | |||
| | 0x1 | |||
| | Support for this was implemented with [[9.6.0-24|9.6.0-X]] FIRM. Bit0=1 enables using bits 1-2, it's unknown what these two bits are actually used for(the value of these two bits get compared with some other value during NCSD verification/loading). This appears to enable a new, likely hardware-based, antipiracy check on cartridges. | |||
| |- | |||
| | 0x1FF | |||
| | 0x1 | |||
| | Support for this was implemented with [[9.6.0-24|9.6.0-X]] FIRM, see below regarding save crypto. | |||
| |} | |} | ||
| For NAND, | |||
| {| class="wikitable" border="1" | |||
| |- | |||
| !  Offset | |||
| !  Size | |||
| !  Description | |||
| |- | |||
| |  0x160 | |||
| |  0x5E | |||
| |  Unknown | |||
| |- | |||
| |  0x1BE | |||
| |  0x42 | |||
| |  Encrypted MBR partition-table, for the TWL partitions(key-data used for this keyslot is console-unique). | |||
| |} | |||
| === NCSD Signature === | |||
| The RSA public key used for gamecard NCSD is stored in [[Memory_layout|ITCM]]. The separate public key used for NAND NCSD is stored in Process9 .(ro)data instead of ITCM, and in [[Bootloader|boot ROM]]. | |||
| For the boot ROM check, sighax may be used to fake-sign NAND headers.  Process9's check will fail, however, unless patched. | |||
| === Partition Flags === | === Partition Flags === | ||
| Line 94: | Line 146: | ||
| !  Byte Index | !  Byte Index | ||
| !  Description | !  Description | ||
| |- | |||
| | 0 | |||
| | Backup Write Wait Time (The time to wait to write save to backup after the card is recognized (0-255 seconds)).NATIVE_FIRM loads this flag from the gamecard NCSD header starting with [[6.0.0-11]]. | |||
| |- | |- | ||
| | 3 | | 3 | ||
| Line 116: | Line 171: | ||
| !  Byte Index | !  Byte Index | ||
| !  Description | !  Description | ||
| |- | |- | ||
| | 1 | | 1 | ||
| | Starting with [[6.0.0-11]] NATIVE_FIRM will use this flag to determine the gamecard [[Savegames|savegame]] keyY method, when flag[3] is set. 0 = [[2.0.0-2]] hashed keyY, 1 = [[Savegames|new]] keyY method implemented with [[6.0.0-11]]. | | Starting with [[6.0.0-11]] NATIVE_FIRM will use this flag to determine the gamecard [[Savegames|savegame]] keyY method, when flag[3] is set. 0 = [[2.0.0-2]] hashed keyY, 1 = [[Savegames|new]] keyY method implemented with [[6.0.0-11]]. 0x0A = implemented with [[9.3.0-21|9.3.0-X]]. On Old3DS this is identical to the [[2.2.0-4]] crypto. On New3DS this is identical to the [[2.2.0-4]] crypto, except with New3DS-only gamecard savedata [[AES|keyslots]]. | ||
| Starting with [[9.6.0-24|9.6.0-X]] FIRM, Process9 now sets <savecrypto_stateval> to partitionflag[1] + <the u8 value from NCSD+0x1FF>, instead of just setting it to partitionflag[1]. | |||
| |- | |- | ||
| | 3 | | 3 | ||
| | Support for this flag was implemented in NATIVE_FIRM with [[2.0.0-2]]. When this flag is set the hashed gamecard [[Savegames|savegame]] keyY method is used, this likely still uses the repeating-CTR however. With [[6.0.0-11]] the system will determine the gamecard savegame keyY method via flag[1], instead of just using the hashed keyY via this flag. | | Support for this flag was implemented in NATIVE_FIRM with [[2.0.0-2]]. When this flag is set the hashed gamecard [[Savegames|savegame]] keyY method is used, this likely still uses the repeating-CTR however. With [[6.0.0-11]] the system will determine the gamecard savegame keyY method via flag[1], instead of just using the hashed keyY via this flag. | ||
| |- | |-th | ||
| | 7 | | 7 | ||
| | This flag enables using the hashed gamecard [[Savegames|savegame]] keyY method, support for this flag was implemented in NATIVE_FIRM with [[2.2.0-4]]. All games with the NCSD image finalized since [[2.2.0-4]](and contains [[2.2.0-4]]+ in the system update partition) have this flag set, this flag also enables using new CTR method as well. | | This flag enables using the hashed gamecard [[Savegames|savegame]] keyY method, support for this flag was implemented in NATIVE_FIRM with [[2.2.0-4]]. All games with the NCSD image finalized since [[2.2.0-4]](and contains [[2.2.0-4]]+ in the system update partition) have this flag set, this flag also enables using new CTR method as well. | ||
| |} | |} | ||
| Starting with [[9.6.0-24|9.6.0-X]] FIRM, Process9 will just write val0 to a state field then return 0, instead of returning an error when the save crypto type isn't recognized. This was the *only* actual functionality change in the Old3DS Process9 function for gamecard savedata crypto init. | |||
| == Card Info Header == | == Card Info Header == | ||
| Line 139: | Line 194: | ||
| |  0x200 | |  0x200 | ||
| |  4 | |  4 | ||
| |  Writable Address In Media Units (For 'On-Chip' Savedata) | |  CARD2: Writable Address In Media Units (For 'On-Chip' Savedata). CARD1: Always 0xFFFFFFFF. | ||
| |- | |- | ||
| |  0x204 | |  0x204 | ||
| Line 146: | Line 201: | ||
| |- | |- | ||
| |  0x208 | |  0x208 | ||
| |   | |  0xF8 | ||
| |   | |  Reserved | ||
| |- | |||
| |  0x300 | |||
| |  4 | |||
| |  Filled size of cartridge | |||
| |- | |||
| |  0x304 | |||
| |  0xC | |||
| |  Reserved | |||
| |- | |||
| |  0x310 | |||
| |  2 | |||
| |  Title version | |||
| |- | |||
| |  0x312 | |||
| |  2 | |||
| |  Card revision | |||
| |- | |||
| |  0x314 | |||
| |  0xC | |||
| |  Reserved | |||
| |- | |||
| |  0x320 | |||
| |  8 | |||
| |  Title ID of [[CVer]] in included update partition | |||
| |- | |||
| |  0x328 | |||
| |  2 | |||
| |  Version number of [[CVer]] in included update partition | |||
| |- | |||
| |  0x32A | |||
| |  0xCD6 | |||
| |  Reserved | |||
| |- | |- | ||
| |  0x1000 | |  0x1000 | ||
| |   | |  0x200 | ||
| |   | |  InitialData | ||
| |} | |||
| === InitialData === | |||
| This data is returned by [[Gamecards|16-byte cartridge command]] 0x82. | |||
| {| class="wikitable" border="1" | |||
| |- | |||
| !  OFFSET | |||
| !  SIZE | |||
| !  DESCRIPTION | |||
| |- | |||
| |  0x00 | |||
| |  0x10 | |||
| |  Seed (keyY used to decrypt the title key - keyX is keyslot 0x3B for production cards, or a key of all zeroes for development cards), consisting of the title ID (little-endian) followed by reserved data (normally all-zero) | |||
| |- | |||
| |  0x10 | |||
| |  0x10 | |||
| |  TitleKey (AES-CCM encrypted) | |||
| |- | |- | ||
| |   | |  0x20 | ||
| |   | |  0x10 | ||
| |   | |  AES-CCM MAC | ||
| |- | |- | ||
| |  0x30 | |  0x30 | ||
| |   | |  0xC | ||
| |  AES-CCM nonce | |||
| |- | |- | ||
| |   | |  0x3C | ||
| |   | |  0xC4 | ||
| |  Reserved | |  Reserved (normally all-zero) | ||
| |- | |- | ||
| |  0x100 | |  0x100 | ||
| |   | |  0x100 | ||
| |  NcchHeader (copy of the first NCCH header, excluding the RSA signature) | |||
| |} | |} | ||
| Line 183: | Line 289: | ||
| |  0x1400 | |  0x1400 | ||
| |  0x10 | |  0x10 | ||
| |   | |  TitleKeyData | ||
| |- | |- | ||
| |  0x1410 | |  0x1410 | ||
| |   | |  0x1BF0 | ||
| |  CardDeviceReserved2 | |  CardDeviceReserved2 | ||
| |- | |||
| |  0x3000 | |||
| |  0x1000 | |||
| |  TestData | |||
| |} | |||
| TitleKeyData contains the decrypted version of the title key found in the InitialData. This field appears to be what development--and maybe production?--cards read to know what card encryption seed to use in the CTR protocol. | |||
| The CardDeviceReserved areas have random-looking data whose purpose is unknown, other than perhaps to hide the TitleKey. | |||
| Note that a particular flashcard vendor, namely Gateway, puts what many refer to as "private headers" at CardDeviceReserved1 in place of actual development card information. This header consists of: | |||
| {| class="wikitable" border="1" | |||
| |- | |||
| !  OFFSET | |||
| !  SIZE | |||
| !  DESCRIPTION | |||
| |- | |||
| |  0x0 | |||
| |  0x40 | |||
| |  Unique cartridge ID; only the first 0x10 bytes are meaningful, the rest are 0xff; obtainable using encrypted [[Gamecards|16-byte cartridge command]] 0xc6; the first 0x10 bytes can also be obtained in userland via [[Process_Services_PXI|pxi:ps9::GetRomId]] | |||
| |- | |||
| |  0x40 | |||
| |  0x4 | |||
| |  Cartridge ID1; obtainable using 8-byte cartridge command 0x90 or 16-byte cartridge command 0xa2 | |||
| |- | |||
| |  0x44 | |||
| |  0x4 | |||
| |  Cartridge ID2; obtainable using 8-byte cartridge command 0xa0 or 16-byte cartridge command 0xa4 | |||
| |- | |||
| |  0x48 | |||
| |  0x8 | |||
| |  Padding (all-0xff) | |||
| |} | |} | ||
| The legitimacy of the unique cartridge ID can be validated by online services. | |||
| Some dumping tools, notably GodMode9 as of 2024-05-26, erroneously always write 0x00000000 into the position of the Cartridge ID2. This is presumably because the cartridge ID2 is always zero for retail carts. | |||
| === TestData === | |||
| The test data is the same one encountered in development DS/DSi cartridges. Its layout is as follows: | |||
| {| class="wikitable" border="1" | |||
| |- | |||
| !  OFFSET | |||
| !  SIZE | |||
| !  DESCRIPTION | |||
| |- | |||
| |  0x0 | |||
| |  0x8 | |||
| |  The bytes FF 00 FF 00 AA 55 AA 55. | |||
| |- | |||
| |  0x8 | |||
| |  0x1F8 | |||
| |  An ascending byte sequence equal to the offset mod 256 (08 09 0A ... FE FF 00 01 ... FF). | |||
| |- | |||
| |  0x200 | |||
| |  0x200 | |||
| |  A descending byte sequence equal to 255 minus the offset mod 256 (FF FE FD ... 00 FF DE ... 00). | |||
| |- | |||
| |  0x400 | |||
| |  0x200 | |||
| |  Filled with 00 (0b00000000) bytes. | |||
| |- | |||
| |  0x600 | |||
| |  0x200 | |||
| |  Filled with FF (0b11111111) bytes. | |||
| |- | |||
| |  0x800 | |||
| |  0x200 | |||
| |  Filled with 0F (0b00001111) bytes. | |||
| |- | |||
| |  0xA00 | |||
| |  0x200 | |||
| |  Filled with F0 (0b11110000) bytes. | |||
| |- | |||
| |  0xC00 | |||
| |  0x200 | |||
| |  Filled with 55 (0b01010101) bytes. | |||
| |- | |||
| |  0xE00 | |||
| |  0x1FF | |||
| |  Filled with AA (0b10101010) bytes. | |||
| |- | |||
| |  0xFFF | |||
| |  0x1 | |||
| |  The final byte is 00 (0b00000000). | |||
| |} | |||
| Production cards always return FF when attempting to read 0x1200-0x3FFF. They probably actually have the same data as development cards, but there's no way to read it. | |||
| == Tools == | == Tools == | ||