XCI: Difference between revisions

No edit summary
mNo edit summary
 
(29 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Known internally as "XCI" (NX Card Image), this is the format used for storing the contents of a Nintendo Switch Gamecard.  
This is the format used for storing the contents of a Nintendo Switch Gamecard.


= Gamecard Header =
= Structure =
This header is 0x200 bytes and is located at offset 0 in the Gamecard.
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x1000
| [[#CardKeyArea]]
|-
| 0x1000
| 0x200
| [[#CardHeader]]
|-
| 0x1200
| 0x200
| [11.0.0+] [[#NewCardHeader]]
|-
| 0x1400
| 0x400
| [11.0.0+] [[#NewCardHeaderCertArea]]
|-
| 0x1800
| 0x100
| [11.0.0+] NewCardHeaderCertAreaModulus
|-
| 0x1900
| 0x6700
| Reserved
|-
| 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
| 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"
{| class="wikitable" border="1"
|-
|-
Line 16: Line 142:
| 0x100
| 0x100
| 0x4
| 0x4
| Magicnum "HEAD"
| Magic ("HEAD")
|-
|-
| 0x104
| 0x104
| 0x4
| 0x4
| Secure Area Start Address (in Media Units which are 0x200 bytes)
| RomAreaStartPageAddress (in Gamecard page units, which are 0x200 bytes)
|-
|-
| 0x108
| 0x108
| 0x4
| 0x4
| Backup Area Start Address (always 0xFFFFFFFF)
| BackupAreaStartPageAddress (always 0xFFFFFFFF)
|-
|-
| 0x10C
| 0x10C
| 0x1
| 0x1
| TitleKeyDec Index (high nibble) and KEK Index (low nibble)
| TitleKeyDecIndex (high nibble) and KekIndex (low nibble)
|-
|-
| 0x10D
| 0x10D
| 0x1
| 0x1
| Gamecard Size (0xFA = 1GB, 0xF8 = 2GB, 0xF0 = 4GB, 0xE0 = 8GB, 0xE1 = 16GB, 0xE2 = 32GB)
| [[#RomSize]]
|-
|-
| 0x10E
| 0x10E
| 0x1
| 0x1
| Gamecard Header Version
| [[#Version]]
|-
|-
| 0x10F
| 0x10F
| 0x1
| 0x1
| Gamecard Flags (bit0 = AutoBoot, bit1 = HistoryErase, bit2 = RepairTool)
| [[#Flags]]
|-
|-
| 0x110
| 0x110
| 0x8
| 0x8
| Package ID (used for challenge–response authentication)
| PackageId (used for challenge–response authentication)
|-
|-
| 0x118
| 0x118
| 0x8
| 0x4
| Valid Data End Address (in Media Units which are 0x200 bytes)
| ValidDataEndAddress (in Gamecard page units, which are 0x200 bytes)
|-
| 0x11C
| 0x4
| Reserved
|-
|-
| 0x120
| 0x120
| 0x10
| 0x10
| Gamecard Info IV (reversed)
| Iv (reversed)
|-
|-
| 0x130
| 0x130
| 0x8
| 0x8
| HFS0 partition offset
| PartitionFsHeaderAddress
|-
|-
| 0x138
| 0x138
| 0x8
| 0x8
| HFS0 header size
| PartitionFsHeaderSize
|-
|-
| 0x140
| 0x140
| 0x20
| 0x20
| SHA-256 hash of the [[#HFS0 Header|HFS0 Header]]
| PartitionFsHeaderHash (SHA-256 hash of the [[#PartitionFsHeader]])
|-
|-
| 0x160
| 0x160
| 0x20
| 0x20
| SHA-256 hash of the [[#Initial Data|Initial Data]]
| InitialDataHash (SHA-256 hash of the [[#InitialData]])
|-
|-
| 0x180
| 0x180
| 0x4
| 0x4
| Security Mode (0x01 = T1, 0x02 = T2)
| [[#SelSec]]
|-
|-
| 0x184
| 0x184
| 0x4
| 0x4
| T1 Key Index (always 2)
| SelT1Key (always 2)
|-
|-
| 0x188
| 0x188
| 0x4
| 0x4
| Key Index (always 0)
| SelKey (always 0)
|-
|-
| 0x18C
| 0x18C
| 0x4
| 0x4
| Normal Area End Address (in Media Units which are 0x200 bytes)
| LimArea (in Gamecard page units, which are 0x200 bytes)
|-
|-
| 0x190
| 0x190
| 0x70
| 0x70
| [[#Gamecard Info|Gamecard Info]] (AES-128-CBC encrypted)
| [[#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
|}
 
=== Version ===
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 0
| Default
|-
| 1
|
|-
| 2
|
|-
| 3
| [20.0.0+] T2Supported
|}
|}


== Gamecard Info ==
=== Flags ===
When decrypted, this 0x70 byte region is as follows:
[[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+] CardHeaderSignKey
|}
 
=== SelSec ===
{| class="wikitable" border="1"
|-
! Value
! Description
|-
| 1
| T1
|-
| 2
| T2
|}
 
=== CardHeaderEncryptedData ===
This region is stored encrypted (AES-128-CBC).


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 102: Line 318:
| 0x0
| 0x0
| 0x8
| 0x8
| Firmware Version (0x00 = Development, 0x01 = Retail, 0x02 = Retail on 4.0.0+)
| [[#FwVersion]]
|-
|-
| 0x8
| 0x8
| 0x4
| 0x4
| Access Control (0x00A10011 = 25MHz access, 0x00A10010 = 50MHz access)
| [[#AccCtrl1]]
|-
|-
| 0xC
| 0xC
| 0x4
| 0x4
| Wait1 Time Read (always 0x1388)
| Wait1TimeRead (always 0x1388)
|-
|-
| 0x10
| 0x10
| 0x4
| 0x4
| Wait2 Time Read (always 0)
| Wait2TimeRead (always 0)
|-
|-
| 0x14
| 0x14
| 0x4
| 0x4
| Wait1 Time Write (always 0)
| Wait1TimeWrite (always 0)
|-
|-
| 0x18
| 0x18
| 0x4
| 0x4
| Wait2 Time Write (always 0)
| Wait2TimeWrite (always 0)
|-
|-
| 0x1C
| 0x1C
| 0x4
| 0x4
| Firmware Mode
| FwMode (the current SdkAddonVersion)
|-
|-
| 0x20
| 0x20
| 0x4
| 0x4
| CUP Version
| UppVersion
|-
|-
| 0x24
| 0x24
| 0x4
| 0x1
| Empty
| [9.0.0+] [[#CompatibilityType]]
|-
| 0x25
| 0x3
| Reserved
|-
|-
| 0x28
| 0x28
| 0x8
| 0x8
| Update Partition Hash
| UppHash (SHA-256 hash of the [[#UpdatePartition]])
|-
|-
| 0x30
| 0x30
| 0x8
| 0x8
| CUP ID (always 0x0100000000000816, which is the title-listing data archive's title ID)
| UppId (always 0x0100000000000816)
|-
|-
| 0x38
| 0x38
| 0x38
| 0x38
| Empty
| Reserved
|}
|}


= Gamecard Certificate =
==== FwVersion ====
This is the Gamecard's unique certificate and is located at offset 0x7000.
{| 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
|}


[[Filesystem_services|FS]] IDeviceOperator cmd 206 "GetGameCardDeviceCertificate" retrieves this data.
==== 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"
{| class="wikitable" border="1"
|-
|-
Line 162: Line 429:
| 0x0
| 0x0
| 0x100
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 to 0x200
| RSA-2048 PKCS #1 signature over the header (data from 0x100 to 0x200)
|-
|-
| 0x100
| 0x100
| 0x4
| 0x4
| Magicnum "CERT"
| Magic ("HEAD")
|-
|-
| 0x104
| 0x104
| 0x4
| 0x4
| Empty
| RomAreaStartPageAddress (in Gamecard page units, which are 0x200 bytes)
|-
|-
| 0x108
| 0x108
| 0x4
| BackupAreaStartPageAddress (always 0xFFFFFFFF)
|-
| 0x10C
| 0x1
| 0x1
| KEK Index
| TitleKeyDecIndex (high nibble) and KekIndex (low nibble)
|-
|-
| 0x109
| 0x10D
| 0x7
| 0x1
| Empty
| [[#RomSize]]
|-
| 0x10E
| 0x1
| [[#Version]]
|-
| 0x10F
| 0x1
| [[#Flags]]
|-
|-
| 0x110
| 0x110
| 0x10
| 0x8
| Device ID
| PackageId (used for challenge–response authentication)
|-
| 0x118
| 0x4
| ValidDataEndAddress (in Gamecard page units, which are 0x200 bytes)
|-
| 0x11C
| 0x1
| [20.0.0+] CardHeaderSignKeyIndex ([1.0.0-19.0.1] Reserved)
|-
| 0x11D
| 0x1
| [18.0.0+] [[#Flags2]] ([1.0.0-17.0.1] Reserved)
|-
| 0x11E
| 0x2
| [19.0.0+] [[#NumberOfApplicationIds]] ([1.0.0-18.1.0] Reserved)
|-
|-
| 0x120
| 0x120
| 0x10
| 0x10
| Unknown
| Iv (reversed)
|-
|-
| 0x130
| 0x130
| 0xD0
| 0x8
| Encrypted data
| PartitionFsHeaderAddress
|-
| 0x138
| 0x8
| PartitionFsHeaderSize
|-
| 0x140
| 0x20
| PartitionFsHeaderHash (SHA-256 hash of the [[#PartitionFsHeader]])
|-
| 0x160
| 0x20
| InitialDataHash (SHA-256 hash of the [[#InitialData]])
|-
| 0x180
| 0x4
| [[#SelSec]]
|-
| 0x184
| 0x4
| SelT1Key (always 2)
|-
| 0x188
| 0x4
| SelKey (always 0)
|-
| 0x18C
| 0x4
| LimArea (in Gamecard page units, which are 0x200 bytes)
|-
| 0x190
| 0x70
| [[#NewCardHeaderEncryptedData]]
|}
|}


The data between the Gamecard Certificate and the start of the HFS0 region is all 0xFF.
=== Flags2 ===
[[Filesystem_services|FS]] retrieves this data as [[Filesystem_services#GameCardAttribute2|GameCardAttribute2]].


= Initial Data =
=== NumberOfApplicationIds ===
This data is used for challenge–response authentication when changing to the Gamecard's secure mode.
This is the number of entries in the ApplicationIdList located right before ValidDataEndAddress.


[[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 [[#Gamecard Header|Gamecard Header]].
=== NewCardHeaderEncryptedData ===
This region is stored encrypted (AES-128-CBC).


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 208: Line 537:
| 0x0
| 0x0
| 0x8
| 0x8
| Package ID from [[#Gamecard Header|Gamecard Header]] at offset 0x110
| [[#FwVersion]]
|-
|-
| 0x8
| 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
| [[#CompatibilityType]]
|-
| 0x25
| 0x3
| Reserved
|-
| 0x28
| 0x8
| 0x8
| Empty
| UppHash (SHA-256 hash of the [[#UpdatePartition]])
|-
|-
| 0x30
| 0x8
| UppId (always 0x0100000000000816)
|-
| 0x38
| 0x8
| Reserved
|-
| 0x40
| 0x20
| RelatedCardHeaderHash (SHA-256 hash of [[#CardHeader]])
|-
| 0x60
| 0x10
| 0x10
| Reserved
|}
== NewCardHeaderCertArea ==
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 to 0x400
|-
| 0x100
| 0x4
| Magic
|-
| 0x104
| 0x4
| Version
|-
| 0x108
| 0x8
|
|-
| 0x110
| 0x1
| CardHeaderSignKeyIndex
|-
| 0x111
| 0x1F
| Reserved
|-
| 0x130
| 0x100
| Modulus
|-
| 0x230
| 0x3
| PublicExponent
|-
| 0x233
| 0x1CD
| Reserved
|}
== CertArea ==
This is the Gamecard's unique certificate.
[[Filesystem_services|FS]] retrieves this data with [[Filesystem_services#GetGameCardDeviceCertificate|GetGameCardDeviceCertificate]].
{| class="wikitable" border="1"
|-
! Offset
! Size
! Description
|-
| 0x0
| 0x100
| RSA-2048 PKCS #1 signature over the data from 0x100 to 0x200
|-
| 0x100
| 0x4
| Magic ("CERT")
|-
| 0x104
| 0x4
| Version
|-
| 0x108
| 0x1
| KekIndex
|-
| 0x109
| 0x7
| Reserved
|-
| 0x110
| 0x10
| 0x10
| Challenge–response authentication data
| T1CardDeviceId
|-
| 0x120
| 0x10
| Iv
|-
|-
| 0x20
| 0x130
| 0x10
| 0x10
| Challenge–response authentication MAC
| 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"
|-
! Offset
! Size
! Description
|-
|-
| 0x30
| Variable
| 0xC
| Variable
| Challenge–response authentication Nonce
| [[#PartitionFsHeader|SecurePartitionHeader]]
|-
|-
| 0x3C
| Variable
| 0x1C4
| Variable
| Reserved (must be empty)
| [[#SecurePartition|SecurePartition]]
|}
|}


= HFS0 =
=== 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.
 
== PartitionFs ==
This is the Gamecard file system which starts with magicnum "HFS0".
This is the Gamecard file system which starts with magicnum "HFS0".


== Header ==
=== PartitionFsHeader ===
The "SHA-256 File System" or "HFS0" starts at offset 0xF000 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).
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 [[#Gamecard Header|Gamecard Header]].
A hash for this header is stored at offset 0x140 in the [[#CardHeader]].
 
== File System ==
The actual file system is as follows (also valid for the root partition):


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 250: Line 778:
| 0x0
| 0x0
| 0x4
| 0x4
| Magicnum "HFS0"
| 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 311: Line 838:


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?).
= Cartridge Layout =
Observed gamecards 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.
[4.0.0+] The "normal" partition is now empty and a new partition "logo" was added.