Line 5: |
Line 5: |
| When a NAX0 file is recreated, the header and raw content in NAX0 are completely different except for the magicnum and size-field. This is the case when a title is redownloaded/reinstalled, and when "save/" files are recreated after manual deletion. | | When a NAX0 file is recreated, the header and raw content in NAX0 are completely different except for the magicnum and size-field. This is the case when a title is redownloaded/reinstalled, and when "save/" files are recreated after manual deletion. |
| | | |
− | [[Error_codes|Error]] 0x250E02 is returned by NCM when attempting to access a NcaId where the NAX0 header was manually modified, for example: offset 0x0, 0x10, 0x28, 0x38, each with size 0x1. | + | [[Error_codes|Error]] 0x250E02 is returned by NCM when attempting to access a NcaId where the NAX0 header HMAC validation fails. |
| | | |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
Line 15: |
Line 15: |
| | 0x0 | | | 0x0 |
| | 0x20 | | | 0x20 |
− | | Data appears to be random. | + | | Header HMAC |
| |- | | |- |
| | 0x20 | | | 0x20 |
Line 23: |
Line 23: |
| | 0x28 | | | 0x28 |
| | 0x20 | | | 0x20 |
− | | Data appears to be random. | + | | Encrypted AES-XTS Keys |
| |- | | |- |
| | 0x48 | | | 0x48 |
− | | 0x8? | + | | 0x8 |
| | Size of the file relative to offset 0x4000. | | | Size of the file relative to offset 0x4000. |
| + | |- |
| + | | 0x50 |
| + | | 0x30 |
| + | | Padding |
| |} | | |} |
| + | |
| + | = Encryption = |
| + | |
| + | The actual content uses AES-XTS with the same non-standard tweak (endianness swap) as usual, and sector size 0x4000. |
| + | |
| + | Key derivation is as follows: |
| + | |
| + | First, FS retrieves the 16-byte console-unique seed that NS registered (data originates from the ns_systemseed:/ [[Flash_Filesystem#System_Savegames|system savegame]]). Next, FS calculates encryptedSeedKeys = <0x20 hardcoded keydata depending on if this NAX0 is a save or an NCA> XORed with the system seed, calculates kek = [[SPL_services#GenerateAesKek|GenerateAesKek]](<hardcoded keydata>, 0, 0), and then calculates decryptedSeedKeys by doing decryptedSeedKeys[i] = [[SPL_services#GenerateAesKey|GenerateAesKey]](kek, encryptedSeedKeys[i]); for both keys. |
| + | |
| + | |
| + | Once the decryptedSeedKeys are calculated, FS calculates naxSpecificKeys = hmac-sha256(key=decryptedSeedKeys[0], data=<NAX0 path, relative to /Nintendo/Contents>); (example path: '/registered/000000FF/cafebabecafebabecafebabecafebabe.nca'). Then, FS does the following decryptions in place: aes128_ecb_decrypt(key=naxSpecificKeys[0], data=Header + 0x28); aes128_ecb_decrypt(key=naxSpecificKeys[1], data=Header + 0x38); Finally, hmac-sha256(key=Header[0x20:0x60], data=decryptedSeedKeys[1]) is calculated, and compared using a constant-time memcmp to the hmac stored at Header + 0x0. If it matches, decryption proceeds using the two decrypted keys at Header + 0x28 as an AES-XTS keypair. |