Line 1: |
Line 1: |
− | '''Title metadata''' is a format used to store information about a title (a single standalone game, channel, etc.) and all its installed contents, including which contents they consist of and their SHA1 hashes. | + | [[Category:File formats]] |
| + | '''Title metadata''' is a format used to store information about a title (installed title, DLC, etc.) and all its installed contents, including which contents they consist of and their SHA256 hashes. |
| | | |
− | [http://git.daifukkat.su/?p=3dshax.git Code is available] by trap15 to parse the available information from the 3DS format of TMDs. | + | [https://bitbucket.org/trap15/3dshax Code is available] by trap15 to parse the available information from the 3DS format of TMDs. |
| | | |
| == Structure == | | == Structure == |
| + | |
| + | All the data in the file represented in Big Endian, unless otherwise noted. |
| + | |
| + | {| class="wikitable" |
| + | | align="center" style="background:#f0f0f0;"|'''Offset''' |
| + | | align="center" style="background:#f0f0f0;"|'''Size''' |
| + | | align="center" style="background:#f0f0f0;"|'''Description''' |
| + | |- |
| + | | 0x0||Y||Signature Data |
| + | |- |
| + | | Y ||0xC4||Header |
| + | |- |
| + | | 0xC4 + Y||0x24*64||Content Info Records. |
| + | |- |
| + | | 0x9C4 + Y||0x30*ContentCount||Content Chunk Records. |
| + | |} |
| + | |
| + | Y denotes the total size of the "Signature Data" section and depends on the signature type. |
| + | |
| + | === Signature Data === |
| + | The signature is of the header of the TMD. |
| + | {| class="wikitable" |
| + | | align="center" style="background:#f0f0f0;"|'''Offset''' |
| + | | align="center" style="background:#f0f0f0;"|'''Size''' |
| + | | align="center" style="background:#f0f0f0;"|'''Description''' |
| + | |- |
| + | | 0x0||0x4||Signature Type |
| + | |- |
| + | | 0x4 ||X||Signature |
| + | |- |
| + | | 0x4 + X|| ||Padding Aligning the signature data to 0x40 bytes |
| + | |} |
| + | |
| + | ==== Signature Type ==== |
| + | {{Signature Types}} |
| + | The hash for the signature, is calculated over the header of the TMD |
| + | |
| === Header === | | === Header === |
| + | |
| {| class="wikitable" | | {| class="wikitable" |
− | |- style="background-color: #ddd;" | + | | align="center" style="background:#f0f0f0;"|'''Offset''' |
− | ! Start
| + | | align="center" style="background:#f0f0f0;"|'''Size''' |
− | ! Length
| + | | align="center" style="background:#f0f0f0;"|'''Description''' |
− | ! Description
| + | |- |
| + | | 0x0||0x40||Signature Issuer |
| |- | | |- |
− | | 0x000 | + | | 0x40||0x1||Version |
− | | 4 | |
− | | Signature type | |
| |- | | |- |
− | | 0x004 | + | | 0x41||0x1||ca_crl_version |
− | | * | |
− | | Signature | |
| |- | | |- |
− | | 0x104 | + | | 0x42||0x1||signer_crl_version |
− | | 60 | |
− | | Padding modulo 64 | |
| |- | | |- |
− | | 0x140 | + | | 0x43||0x1||Reserved |
− | | 64 | |
− | | Issuer | |
| |- | | |- |
− | | 0x180 | + | | 0x44||0x8||System Version |
− | | 1 | |
− | | Version | |
| |- | | |- |
− | | 0x181 | + | | 0x4C||0x8||Title ID |
− | | 1 | |
− | | ca_crl_version | |
| |- | | |- |
− | | 0x182 | + | | 0x54||0x4||Title Type |
− | | 1 | |
− | | signer_crl_version | |
| |- | | |- |
− | | 0x183 | + | | 0x58||0x2||Group ID |
− | | 1 | |
− | | Padding modulo 64 | |
| |- | | |- |
− | | 0x184 | + | | 0x5A||0x4||Save Data Size in Little Endian (Bytes) (Also SRL Public Save Data Size) |
− | | 8 | |
− | | System Version | |
| |- | | |- |
− | | 0x18C | + | | 0x5E||0x4||SRL Private Save Data Size in Little Endian (Bytes) |
− | | 8 | |
− | | Title ID | |
| |- | | |- |
− | | 0x194 | + | | 0x62||0x4||Reserved |
− | | 4 | |
− | | Title type | |
| |- | | |- |
− | | 0x198 | + | | 0x66||0x1||SRL Flag |
− | | 2 | |
− | | Group ID | |
| |- | | |- |
− | | 0x19A | + | | 0x67||0x31||Reserved |
− | | 62 | |
− | | reserved | |
| |- | | |- |
− | | 0x1D8 | + | | 0x98||0x4||Access Rights |
− | | 4 | |
− | | Access rights | |
| |- | | |- |
− | | 0x1DC | + | | 0x9C||0x2||Title Version |
− | | 2 | |
− | | Title version | |
| |- | | |- |
− | | 0x1DE | + | | 0x9E||0x02||Content Count |
− | | 2 | |
− | | Number of contents | |
| |- | | |- |
− | | 0x1E0 | + | | 0xA0||0x2||Boot Content |
− | | 64 | |
− | | Content Records | |
| |- | | |- |
− | | 0x220 | + | | 0xA2||0x2||Padding |
− | | 40 | |
− | | Padding modulo 64 | |
| |- | | |- |
− | | 0x248 | + | | 0xA4||0x20||SHA-256 Hash of the Content Info Records |
− | | 4 | + | |} |
− | | Boot content | + | |
| + | === Content Info Records === |
| + | |
| + | There are 64 of these records, usually only the first is used. |
| + | |
| + | {| class="wikitable" |
| + | |- |
| + | ! Offset |
| + | ! Size |
| + | ! Description |
| |- | | |- |
− | | 0x252 | + | | 0x00 |
− | | 4 | + | | 2 |
− | | Banner content | + | | Content index offset |
| |- | | |- |
− | | 0x256 | + | | 0x02 |
− | | 4 | + | | 2 |
− | | Banner size | + | | Content command count [k] |
| |- | | |- |
− | | 0x260 | + | | 0x04 |
− | | 32 | + | | 0x20 |
− | | Hash | + | | SHA-256 hash of the next k content records that have not been hashed yet |
| + | |} |
| | | |
− | |}
| + | === Content chunk records === |
| + | There is one of these for each content contained in this title. (Determined by "Content Count" in the TMD Header). |
| | | |
− | === Content Records ===
| |
| {| class="wikitable" | | {| class="wikitable" |
| |- | | |- |
− | ! Start | + | ! Offset |
− | ! Length | + | ! Size |
| ! Description | | ! Description |
| |- | | |- |
| | 0x00 | | | 0x00 |
| | 4 | | | 4 |
− | | Content ID | + | | Content id |
| |- | | |- |
| | 0x04 | | | 0x04 |
− | | 32 | + | | 2 |
− | | SHA-256 Hash | + | | Content index |
| + | |- |
| + | | 0x06 |
| + | | 2 |
| + | | Content type |
| + | |- |
| + | | 0x08 |
| + | | 8 |
| + | | Content size |
| + | |- |
| + | | 0x10 |
| + | | 0x20 |
| + | | SHA-256 hash |
| |} | | |} |
| + | ==== Content Index ==== |
| | | |
− | === Certificates === | + | This indicates the content type: |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Index |
| + | ! Content Type |
| + | |- |
| + | | 0000 |
| + | | Main Content (.[[NCCH#CXI|CXI]] for 3DS executable content/.[[NCCH#CFA|CFA]] for 3DS Data Archives/.SRL for TWL content) |
| + | |- |
| + | | 0001 |
| + | | Home Menu Manual (.[[NCCH#CFA|CFA]]) |
| + | |- |
| + | | 0002 |
| + | | DLP Child Container (.[[NCCH#CFA|CFA]]) |
| + | |} |
| + | |
| + | This does not apply to DLC. |
| + | |
| + | ==== Content Type flags ==== |
| {| class="wikitable" | | {| class="wikitable" |
| |- | | |- |
− | ! Start | + | ! Flags |
− | ! Length
| |
| ! Description | | ! Description |
| |- | | |- |
− | | 0x000 | + | | 1 |
| + | | Encrypted |
| + | |- |
| + | | 2 |
| + | | Disc |
| + | |- |
| | 4 | | | 4 |
− | | Signature type | + | | CFM (abbreviation for?) |
| |- | | |- |
− | | 0x004 | + | | 0x4000 |
− | | * | + | | Optional |
− | | Signature
| |
| |- | | |- |
− | | 0x104 | + | | 0x8000 |
− | | 64 | + | | Shared |
− | | Issuer | + | |} |
| + | |
| + | == Certificate Chain == |
| + | If the TMD file is obtained from Nintendo's CDN, then it will have two [[Certificates|certificates]] appended at the end of the file: |
| + | |
| + | {| class="wikitable" border="1" |
| |- | | |- |
− | | 0x124
| + | ! CERTIFICATE |
− | | 4
| + | ! SIGNATURE TYPE |
− | | Tag
| + | ! RETAIL CERT NAME |
| + | ! DEBUG CERT NAME |
| + | ! DESCRIPTION |
| |- | | |- |
− | | 0x128 | + | | TMD |
− | | 64 | + | | RSA-2048 |
− | | Name | + | | CP0000000b |
| + | | CP0000000a |
| + | | Used to verify the TMD signature |
| |- | | |- |
− | | 0x168 | + | | CA |
− | | | + | | RSA-4096 |
− | | Key | + | | CA00000003 |
| + | | CA00000004 |
| + | | Used to verify the TMD Certificate |
| |} | | |} |
| + | |
| + | The CA certificate is issued by 'Root', the public key for which is stored in NATIVE_FIRM. |
| | | |
| == Example code application == | | == Example code application == |
| <source lang="c"> | | <source lang="c"> |
− | typedef struct {
| |
− | u32 cid; // content id
| |
− | u8 hash[0x20]; // SHA-256 hash
| |
− | } content_record; // size: 0x24 bytes
| |
− |
| |
| enum sig_type { | | enum sig_type { |
| RSA_2048_SHA256 = 0x00010004, | | RSA_2048_SHA256 = 0x00010004, |
Line 162: |
Line 219: |
| RSA_4096_SHA1 = 0x00010000 | | RSA_4096_SHA1 = 0x00010000 |
| }; | | }; |
− |
| |
− | typedef struct {
| |
− | u32 sig_type;
| |
− | u8 sig[*]; // * Signature size
| |
− | u8 fill1[60];
| |
− | u8 issuer[64]; // Root-CA%08x-CP%08x
| |
− | u8 version;
| |
− | u8 ca_crl_version;
| |
− | u8 signer_crl_version;
| |
− | u8 fill2;
| |
− | u64 sys_version;
| |
− | u64 title_id;
| |
− | u32 title_type;
| |
− | u16 group_id; // publisher
| |
− | u8 reserved[62];
| |
− | u32 access_rights;
| |
− | u16 title_version;
| |
− | u16 num_contents;
| |
− | content_record contents[0x40];
| |
− | u8 padding[0x28];
| |
− | u32 boot_content;
| |
− | u32 banner_content;
| |
− | u32 banner_size;
| |
− | u8 hash[0x20]; /* Huh? */
| |
− | } tmd;
| |
| | | |
− | //The tmd is then followed by a chain of certificates, where each certificate is of the general form
| + | // Sorry I removed the example struct because it is wrong. |
− | typedef struct {
| |
− | u32 sig_type;
| |
− | u8 sig[*]; // * Signature size
| |
− | u8 issuer[64];
| |
− | u32 tag; // identifies what is being signed
| |
− | u8 name[64]; // name of thing being signed
| |
− | u8 key[...];
| |
− | } certificate;
| |
| </source> | | </source> |