XCI: Difference between revisions

PKCS1 signatures
No edit summary
(34 intermediate revisions by 7 users not shown)
Line 1: Line 1:
=Header=
This is the format used for storing the contents of a Nintendo Switch Gamecard.
The header is 0x200-bytes, at Gamecard+0.


= Structure =
{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
Line 9: Line 9:
|-
|-
| 0x0
| 0x0
| 0x1000
| [[#CardKeyArea]]
|-
| 0x1000
| 0x200
| [[#CardHeader]]
|-
| 0x1200
| 0x200
| [11.0.0+] [[#NewCardHeader]]
|-
| 0x1400
| 0x400
| [11.0.0+] [[#NewCardHeaderCertArea]]
|-
| 0x1800
| 0x6800
| ReservedArea
|-
| 0x8000
| 0x8000
| [[#CertArea]]
|-
| 0x10000
| Variable
| [[#NormalArea]]
|-
| Variable
| Variable
| [[#RomArea]]
|-
| Invalid
| Invalid
| BackupArea
|}
== CardKeyArea ==
This region cannot be read directly once written to the Gamecard. Therefore, it is hidden away during read/write operations on the raw Gamecard data.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x200
| [[#InitialData]]
|-
| 0x200
| 0xD00
| [[#TitleKeyArea]]
|-
| 0xF00
| 0x100
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 - 0x200.
| Reserved
|}
 
=== InitialData ===
This region is used for challenge–response authentication when changing to the Gamecard's secure mode.
 
[[Filesystem_services|FS]] calculates a SHA-256 hash over the whole 0x200 bytes and compares it with the hash stored at offset 0x160 in the [[#CardHeader]].
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| Package ID from [[#CardHeader]] at offset 0x110
|-
| 0x8
| 0x8
| Reserved
|-
| 0x10
| 0x10
| Challenge–response authentication data
|-
| 0x20
| 0x10
| Challenge–response authentication MAC
|-
| 0x30
| 0xC
| Challenge–response authentication Nonce
|-
| 0x3C
| 0x1C4
| Reserved (must be empty)
|}
 
=== TitleKeyArea ===
This region is stored encrypted and contains the title keys used by the [[#InitialData]].
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| TitleKey1
|-
| 0x8
| 0x8
| TitleKey2
|-
| 0x10
| 0xCF0
| Reserved
|}
 
== CardHeader ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x100
| RSA-2048 PKCS #1 signature over the header (data from 0x100 to 0x200)
|-
|-
| 0x100
| 0x100
| 0x4
| 0x4
| Magicnum "HEAD"
| Magic ("HEAD")
|-
|-
| 0x104
| 0x104
| 0x4
| 0x4
| Offset of Secure partition (Size of non-secure data?), in Media Units (0x200 bytes for switch gamecarts)
| RomAreaStartPageAddress (in Gamecard page units, which are 0x200 bytes)
|-
|-
| 0x108
| 0x108
| 0x4
| 0x4
| 0xFFFFFFFF
| BackupAreaStartPageAddress (always 0xFFFFFFFF)
|-
|-
| 0x10C
| 0x10C
| 0x1
| 0x1
| ?
| TitleKeyDecIndex (high nibble) and KekIndex (low nibble)
|-
|-
| 0x10D
| 0x10D
| 0x1
| 0x1
| Cartridge Size. 0xF8 = 2 GB, 0xF0 = 4 GB, 0xE0 = 8 GB, 0xE1 = 16 GB
| [[#RomSize]]
|-
|-
| 0x10E
| 0x10E
| 0x1
| 0x1
| ?
| CardHeaderVersion
|-
|-
| 0x10F
| 0x10F
| 0x1
| 0x1
| ?
| [[#Flags]]
|-
|-
| 0x110
| 0x110
| 0x8
| 0x8
| ?
| PackageId (used for challenge–response authentication)
|-
|-
| 0x118
| 0x118
| 0x8
| 0x4
| Size of the Gamecart, in Media Units
| ValidDataEndAddress (in Gamecard page units, which are 0x200 bytes)
|-
| 0x11C
| 0x1
| Reserved
|-
| 0x11D
| 0x1
| [18.0.0+] [[#Flags2]] ([1.0.0-17.0.1] Reserved)
|-
| 0x11E
| 0x2
| Reserved
|-
|-
| 0x120
| 0x120
| 0x10
| 0x10
| ?
| Iv (reversed)
|-
|-
| 0x130
| 0x130
| 0x8
| 0x8
| Offset of HFS0 FS partition
| PartitionFsHeaderAddress
|-
|-
| 0x138
| 0x138
| 8
| 0x8
| HFS0 Header size
| PartitionFsHeaderSize
|-
|-
| 0x140
| 0x140
| 0x20
| 0x20
| SHA256 hash of the HFS0 Header
| PartitionFsHeaderHash (SHA-256 hash of the [[#PartitionFsHeader]])
|-
|-
| 0x160
| 0x160
| 0x20
| 0x20
| SHA256 hash of the crypto header
| InitialDataHash (SHA-256 hash of the [[#InitialData]])
|-
|-
| 0x180
| 0x180
| 0x4
| 0x4
| 1?
| [[#SelSec]]
|-
|-
| 0x184
| 0x184
| 0x4
| 0x4
| 2?
| SelT1Key (always 2)
|-
|-
| 0x188
| 0x188
| 0x4
| 0x4
| 0?
| SelKey (always 0)
|-
|-
| 0x18C
| 0x18C
| 0x4
| 0x4
| Offset of Secure partition (Size of non-secure data?), in Media Units, again.
| LimArea (in Gamecard page units, which are 0x200 bytes)
|-
| 0x190
| 0x70
| [[#CardHeaderEncryptedData]]
|}
 
=== RomSize ===
[[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardSize|GameCardSize]].
 
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 0xFA
| 1GB
|-
| 0xF8
| 2GB
|-
| 0xF0
| 4GB
|-
| 0xE0
| 8GB
|-
| 0xE1
| 16GB
|-
| 0xE2
| 32GB
|}
 
=== Flags ===
[[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardAttribute|GameCardAttribute]].
 
{| class="wikitable" border="1"
|-
! Bits
! Description
|-
| 0
| AutoBoot
|-
| 1
| HistoryErase
|-
| 2
| [4.0.0+] RepairTool
|-
| 3
| [9.0.0+] DifferentRegionCupToTerraDevice
|-
| 4
| [9.0.0+] DifferentRegionCupToGlobalDevice
|-
| 7
| [11.0.0+] HasNewCardHeader
|}
 
=== Flags2 ===
[[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardAttribute2|GameCardAttribute2]].
 
=== SelSec ===
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 1
| T1
|-
| 2
| T2
|}
 
=== CardHeaderEncryptedData ===
This region is stored encrypted (AES-128-CBC).
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x8
| [[#FwVersion]]
|-
| 0x8
| 0x4
| [[#AccCtrl1]]
|-
| 0xC
| 0x4
| Wait1TimeRead (always 0x1388)
|-
| 0x10
| 0x4
| Wait2TimeRead (always 0)
|-
| 0x14
| 0x4
| Wait1TimeWrite (always 0)
|-
| 0x18
| 0x4
| Wait2TimeWrite (always 0)
|-
| 0x1C
| 0x4
| FwMode (the current SdkAddonVersion)
|-
| 0x20
| 0x4
| UppVersion
|-
| 0x24
| 0x1
| [9.0.0+] [[#CompatibilityType]]
|-
| 0x25
| 0x3
| Reserved
|-
| 0x28
| 0x8
| UppHash (SHA-256 hash of the [[#UpdatePartition]])
|-
| 0x30
| 0x8
| UppId (always 0x0100000000000816)
|-
| 0x38
| 0x38
| Reserved
|}
 
==== FwVersion ====
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 0
| Development
|-
| 1
| Retail
|-
| 2
| [4.0.0+] Retail
|-
| 3
| [11.0.0+] Development
|-
| 4
| [11.0.0+] Retail
|-
| 5
| [12.0.0+] Retail
|}
 
==== AccCtrl1 ====
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 0x00A10011
| 25MHz
|-
| 0x00A10010
| 50MHz
|}
 
==== CompatibilityType ====
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 0
| Normal
|-
| 1
| Terra
|}
 
== NewCardHeader ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 to 0x200
|-
| 0x100
| 0x90
|
|-
|-
| 0x190
| 0x190
| 0x70
| 0x70
| Encrypted data/hashes of some kind
| [[#NewCardHeaderEncryptedData]]
|}
|}


=== NewCardHeaderEncryptedData ===
This region is stored encrypted (AES-128-CBC).


= Cert =
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x40
|
|-
| 0x40
| 0x20
| SHA-256 hash of the [[#CardHeader]]
|-
| 0x60
| 0x10
| Reserved
|}


This is for the CERT, located at Gamecard + 0x7000 (always?). This matches exactly the output from fsp-srv IDeviceOperator cmd 206 "GetGameCardDeviceCertificate".
== NewCardHeaderCertArea ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 to 0x300
|-
| 0x100
| 0x30
|
|-
| 0x130
| 0x100
| [[#NewCardHeader]] modulus
|-
| 0x230
| 0x4
| [[#NewCardHeader]] exponent
|-
| 0x234
| 0x1CC
|
|}
 
== CertArea ==
This is the Gamecard's unique certificate.
 
[[Filesystem_services|FS]] retrieves this data with [[Filesystem_services#GetGameCardDeviceCertificate|GetGameCardDeviceCertificate]].


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 102: Line 491:
| 0x0
| 0x0
| 0x100
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 - 0x200.
| RSA-2048 PKCS #1 signature over the data from 0x100 to 0x200
|-
|-
| 0x100
| 0x100
| 0x4
| 0x4
| Magicnum "CERT"
| Magic ("CERT")
|-
| 0x104
| 0x4
| Version
|-
| 0x108
| 0x1
| KekIndex
|-
| 0x109
| 0x7
| Reserved
|-
|-
| 0x110
| 0x110
| 0x10
| 0x10
| ?
| T1CardDeviceId
|-
| 0x120
| 0x10
| Iv
|-
| 0x130
| 0x10
| HwKey (encrypted)
|-
| 0x140
| 0xC0
| Reserved (encrypted)
|-
| 0x200
| 0x7E00
| Reserved
|}
 
== NormalArea ==
This region contains all non-secure partitions of the Gamecard file system.
 
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| Variable
| Variable
| [[#PartitionFsHeader|RootPartitionHeader]]
|-
| Variable
| Variable
| [[#PartitionFsHeader|UpdatePartitionHeader]]
|-
| Variable
| Variable
| [[#UpdatePartition|UpdatePartition]]
|-
| Variable
| Variable
| [4.0.0+] [[#PartitionFsHeader|LogoPartitionHeader]]
|-
| Variable
| Variable
| [4.0.0+] [[#LogoPartition|LogoPartition]]
|-
| Variable
| Variable
| [[#PartitionFsHeader|NormalPartitionHeader]]
|-
| Variable
| Variable
| [[#NormalPartition|NormalPartition]]
|}
 
=== UpdatePartition ===
This partition contains .cnmt.nca + .nca files for the entire system update required to play the game. Launch day carts contain a full copy of 1.0 ncas, newer carts contain newer sysupdate NCAs etc.
 
=== NormalPartition ===
This partition contains the .cnmt.nca and the game icondata nca. This is presumably for future compatibility so that if a future update changes the cryptographic protocol for the secure partition. Game icon data can still be shown in the home menu on old firmwares.
 
[4.0.0+] This partition is now empty.
 
=== LogoPartition ===
[4.0.0+] This partition now contains the contents of the [[#NormalPartition]].
 
== RomArea ==
This region contains all secure partitions of the Gamecard file system.
 
{| class="wikitable" border="1"
|-
|-
| 0x12A
! Offset
| 0xD6
! Size
| Encrypted data. Some kind of key?
! Description
|-
| Variable
| Variable
| [[#PartitionFsHeader|SecurePartitionHeader]]
|-
| Variable
| Variable
| [[#SecurePartition|SecurePartition]]
|}
|}


The data between the CERT and the start of the HFS0 is all 0xFF.
=== SecurePartition ===
This partition contains an identical copy of the .cnmt.nca and game icondata nca, as well as all other ncas required for the game.


= HFS0 =
== PartitionFs ==
This is the FS which has magicnum "HFS0" at header+0.
This is the Gamecard file system which starts with magicnum "HFS0".
 
=== PartitionFsHeader ===
The "SHA-256 File System" or "HFS0" starts at offset 0x10000 in the Gamecard. The first 0x200 bytes act as a global header and represent the root partition which points to the other partitions ("normal", "logo", "update" and "secure").
 
A hash for this header is stored at offset 0x140 in the [[#CardHeader]].


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 130: Line 616:
| 0x0
| 0x0
| 0x4
| 0x4
| HFS0 Magic
| Magic ("HFS0")
|-
|-
| 0x4
| 0x4
| 0x4
| 0x4
| Number of files
| FileCount
|-
|-
| 0x8
| 0x8
| 0x4
| 0x4
| Size of the string table
| StringTableSize
|-
|-
| 0xC
| 0xC
| 0x4
| 0x4
| Zero/Reserved
| Reserved
|-
|-
| 0x10
| 0x10
| X
| X
| File Entry Table
| [[#FileEntryTable]]
|-
|-
| 0x10 + X
| 0x10 + X
| Y
| Y
| String Table
| StringTable
|-
|-
| 0x10 + X + Y
| 0x10 + X + Y
| Z
| Z
| Raw File Data
| RawFileData
|}
|}


Where File Entry Table consists of Number of Files FileEntries:
==== FileEntryTable ====
 
{| class="wikitable" border="1"
{| class="wikitable" border="1"
|-
|-
Line 179: Line 664:
| 0x14
| 0x14
| 0x4
| 0x4
| Size of Hashed region of file (for HFS0s, this is the size of the pre-filedata portion, for NCAs this is usually 0x200).
| Size of Hashed region of file (for HFS0s, this is the size of the pre-filedata portion, for NCAs this is usually 0x200)
|-
|-
| 0x18
| 0x18
Line 187: Line 672:
| 0x20
| 0x20
| 0x20
| 0x20
| SHA256 hash of the first (size of hashed region) bytes of filedata.
| SHA-256 hash of the first (size of hashed region) bytes of filedata
|}
|}


The string table is 00-padded to align the start of raw filedata with a sector/media unit boundary (usually?).
The string table is 00-padded to align the start of raw filedata with a sector/media unit boundary (usually?).
=Typical Cartridge Layout=
Observed gamecarts contain three partitions: "update", "normal", and "secure".
The update partition (Gamecard partition 0 for fsp-srv cmd 31) contains .cnmt.nca + .nca files for the entire system update required to play the game. Launch day carts contain a full copy of 1.0 ncas, newer carts contain newer sysupdate NCAs etc.
The normal partition contains the .cnmt.nca and the game icondata nca. This is presumably for future compatibility so that if a future update changes the cryptographic protocol for the secure partition, Game icon data can still be shown in the home menu on old firmwares.
The secure partition contains an identical copy of the .cnmt.nca and game icondata nca, as well as all other ncas required for the game.
The entire rest of the gamecard after the secure partition ends is all FF padding.