2020-04-16 01:06:41 +01:00
/*
* Copyright ( c ) 2020 DarkMatterCore
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2020-04-11 06:28:26 +01:00
# pragma once
# ifndef __NCA_H__
# define __NCA_H__
# include <switch.h>
2020-04-20 11:39:41 +01:00
# include "tik.h"
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_HEADER_LENGTH 0x400
# define NCA_FS_HEADER_LENGTH 0x200
# define NCA_FS_HEADER_COUNT 4
# define NCA_FULL_HEADER_LENGTH (NCA_HEADER_LENGTH + (NCA_FS_HEADER_LENGTH * NCA_FS_HEADER_COUNT))
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_NCA0_MAGIC 0x4E434130 /* "NCA0" */
# define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2" */
# define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3" */
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_IVFC_MAGIC 0x49564643 /* "IVFC" */
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR" */
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_FS_ENTRY_BLOCK_SIZE 0x200
# define NCA_FS_ENTRY_BLOCK_OFFSET(x) ((u64)(x) * NCA_FS_ENTRY_BLOCK_SIZE)
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_AES_XTS_SECTOR_SIZE 0x200
# define NCA_NCA0_FS_HEADER_AES_XTS_SECTOR(x) (((x) - NCA_HEADER_LENGTH) >> 9)
2020-04-11 06:28:26 +01:00
2020-04-21 11:23:33 +01:00
# define NCA_IVFC_BLOCK_SIZE(x) (1 << (x))
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaDistributionType_Download = 0 ,
NcaDistributionType_GameCard = 1
} NcaDistributionType ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaContentType_Program = 0 ,
NcaContentType_Meta = 1 ,
NcaContentType_Control = 2 ,
NcaContentType_Manual = 3 ,
NcaContentType_Data = 4 ,
NcaContentType_PublicData = 5
} NcaContentType ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaKeyGenerationOld_100_230 = 0 ,
NcaKeyGenerationOld_300 = 2
} NcaKeyGenerationOld ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaKeyAreaEncryptionKeyIndex_Application = 0 ,
NcaKeyAreaEncryptionKeyIndex_Ocean = 1 ,
NcaKeyAreaEncryptionKeyIndex_System = 2
} NcaKeyAreaEncryptionKeyIndex ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/// 'NcaKeyGeneration_Latest' will always point to the last known key generation value.
2020-04-11 06:28:26 +01:00
typedef enum {
2020-04-15 21:50:07 +01:00
NcaKeyGeneration_301_302 = 3 ,
NcaKeyGeneration_400_410 = 4 ,
NcaKeyGeneration_500_510 = 5 ,
NcaKeyGeneration_600_610 = 6 ,
NcaKeyGeneration_620 = 7 ,
NcaKeyGeneration_700_801 = 8 ,
NcaKeyGeneration_810_811 = 9 ,
NcaKeyGeneration_900_901 = 10 ,
NcaKeyGeneration_910_920 = 11 ,
NcaKeyGeneration_Latest = NcaKeyGeneration_910_920
} NcaKeyGeneration ;
typedef struct {
u32 start_block_offset ; ///< Expressed in NCA_FS_ENTRY_BLOCK_SIZE blocks.
u32 end_block_offset ; ///< Expressed in NCA_FS_ENTRY_BLOCK_SIZE blocks.
u8 enable_entry ;
u8 reserved [ 0x7 ] ;
} NcaFsEntry ;
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u8 hash [ SHA256_HASH_SIZE ] ;
} NcaFsHash ;
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u8 key [ 0x10 ] ;
2020-04-19 23:44:22 +01:00
} NcaKey ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaFsType_RomFs = 0 ,
NcaFsType_PartitionFs = 1
} NcaFsType ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaHashType_Auto = 0 ,
NcaHashType_None = 1 ,
NcaHashType_HierarchicalSha256 = 2 , ///< Used by NcaFsType_PartitionFs.
NcaHashType_HierarchicalIntegrity = 3 ///< Used by NcaFsType_RomFs.
} NcaHashType ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
typedef enum {
NcaEncryptionType_Auto = 0 ,
NcaEncryptionType_None = 1 ,
NcaEncryptionType_AesXts = 2 ,
NcaEncryptionType_AesCtr = 3 ,
2020-04-20 11:39:41 +01:00
NcaEncryptionType_AesCtrEx = 4 ,
2020-04-21 11:23:33 +01:00
NcaEncryptionType_Nca0 = 5 ///< Only used to represent NCA0 AES XTS FS section crypto - not actually used as a possible value for this field.
2020-04-15 21:50:07 +01:00
} NcaEncryptionType ;
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u64 offset ;
u64 size ;
} NcaHierarchicalSha256LayerInfo ;
2020-04-11 06:28:26 +01:00
2020-04-20 11:39:41 +01:00
/// Used for NcaFsType_PartitionFs and NCA0 NcaFsType_RomFsRomFS.
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u8 master_hash [ SHA256_HASH_SIZE ] ;
u32 hash_block_size ;
u32 layer_count ;
NcaHierarchicalSha256LayerInfo hash_data_layer_info ;
NcaHierarchicalSha256LayerInfo hash_target_layer_info ;
} NcaHierarchicalSha256 ;
2020-04-11 06:28:26 +01:00
typedef struct {
u64 offset ;
u64 size ;
2020-04-21 11:23:33 +01:00
u32 block_size ; ///< Use NCA_IVFC_BLOCK_SIZE to calculate the actual block size using this value.
2020-04-15 21:50:07 +01:00
u8 reserved [ 0x4 ] ;
} NcaHierarchicalIntegrityLayerInfo ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/// Used for NcaFsType_RomFs.
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u32 magic ; ///< "IVFC".
u32 version ;
u32 master_hash_size ;
u32 layer_count ;
NcaHierarchicalIntegrityLayerInfo hash_data_layer_info [ 5 ] ;
NcaHierarchicalIntegrityLayerInfo hash_target_layer_info ;
u8 signature_salt [ 0x20 ] ;
u8 master_hash [ 0x20 ] ;
} NcaHierarchicalIntegrity ;
2020-04-11 06:28:26 +01:00
typedef struct {
union {
struct {
2020-04-20 11:39:41 +01:00
///< Used if hash_type == NcaHashType_HierarchicalSha256 (NcaFsType_PartitionFs and NCA0 NcaFsType_RomFs).
2020-04-15 21:50:07 +01:00
NcaHierarchicalSha256 hierarchical_sha256 ;
u8 reserved_1 [ 0xB0 ] ;
2020-04-11 06:28:26 +01:00
} ;
struct {
2020-04-15 21:50:07 +01:00
///< Used if hash_type == NcaHashType_HierarchicalIntegrity (NcaFsType_RomFs).
NcaHierarchicalIntegrity hierarchical_integrity ;
u8 reserved_2 [ 0x18 ] ;
2020-04-11 06:28:26 +01:00
} ;
} ;
2020-04-15 21:50:07 +01:00
} NcaHashInfo ;
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u32 magic ; ///< "BKTR".
u32 bucket_count ;
u32 entry_count ;
u8 reserved [ 0x4 ] ;
} NcaBucketTreeHeader ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/// Only used for NcaEncryptionType_AesCtrEx (PatchRomFs).
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u64 indirect_offset ;
u64 indirect_size ;
NcaBucketTreeHeader indirect_header ;
u64 aes_ctr_ex_offset ;
u64 aes_ctr_ex_size ;
NcaBucketTreeHeader aes_ctr_ex_header ;
} NcaPatchInfo ;
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
/// Format unknown.
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u8 unknown [ 0x30 ] ;
} NcaSparseInfo ;
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-15 21:50:07 +01:00
u16 version ;
u8 fs_type ; ///< NcaFsType.
u8 hash_type ; ///< NcaHashType.
u8 encryption_type ; ///< NcaEncryptionType.
u8 reserved_1 [ 0x3 ] ;
NcaHashInfo hash_info ;
NcaPatchInfo patch_info ;
union {
u8 section_ctr [ 0x8 ] ;
struct {
u32 generation ;
u32 secure_value ;
} ;
} ;
NcaSparseInfo sparse_info ;
u8 reserved_2 [ 0x88 ] ;
} NcaFsHeader ;
typedef struct {
u8 main_signature [ 0x100 ] ; ///< RSA-PSS signature over header with fixed key.
u8 acid_signature [ 0x100 ] ; ///< RSA-PSS signature over header with key in NPDM.
u32 magic ; ///< "NCA0" / "NCA2" / "NCA3".
u8 distribution_type ; ///< NcaDistributionType.
u8 content_type ; ///< NcaContentType.
u8 key_generation_old ; ///< NcaKeyGenerationOld.
u8 kaek_index ; ///< NcaKeyAreaEncryptionKeyIndex.
u64 content_size ;
u64 program_id ;
u32 content_index ;
union {
u32 sdk_addon_version ;
struct {
u8 sdk_addon_revision ;
u8 sdk_addon_micro ;
u8 sdk_addon_minor ;
u8 sdk_addon_major ;
} ;
} ;
u8 key_generation ; ///< NcaKeyGeneration.
u8 main_signature_key_generation ;
u8 reserved_1 [ 0xE ] ;
FsRightsId rights_id ; ///< Used for titlekey crypto.
NcaFsEntry fs_entries [ 4 ] ; ///< Start and end offsets for each NCA FS section.
NcaFsHash fs_hashes [ 4 ] ; ///< SHA-256 hashes calculated over each NCA FS section header.
2020-04-19 23:44:22 +01:00
NcaKey encrypted_keys [ 4 ] ; ///< Only the encrypted key at index #2 is used. The other three are zero filled before the key area is encrypted.
2020-04-15 21:50:07 +01:00
u8 reserved_2 [ 0xC0 ] ;
NcaFsHeader fs_headers [ 4 ] ; /// NCA FS section headers.
} NcaHeader ;
2020-04-11 06:28:26 +01:00
2020-04-20 11:39:41 +01:00
typedef enum {
NcaVersion_Nca0 = 0 ,
NcaVersion_Nca2 = 1 ,
NcaVersion_Nca3 = 2
} NcaVersion ;
2020-04-11 06:28:26 +01:00
2020-04-20 11:39:41 +01:00
typedef enum {
2020-04-22 21:53:20 +01:00
NcaFsSectionType_PartitionFs = 0 , ///< NcaFsType_PartitionFs + NcaHashType_HierarchicalSha256.
NcaFsSectionType_RomFs = 1 , ///< NcaFsType_RomFs + NcaHashType_HierarchicalIntegrity.
NcaFsSectionType_PatchRomFs = 2 , ///< NcaFsType_RomFs + NcaHashType_HierarchicalIntegrity + NcaEncryptionType_AesCtrEx.
NcaFsSectionType_Nca0RomFs = 3 , ///< NcaFsType_RomFs + NcaHashType_HierarchicalSha256 + NcaVersion_Nca0.
NcaFsSectionType_Invalid = 4
} NcaFsSectionType ;
2020-04-11 06:28:26 +01:00
typedef struct {
2020-04-22 21:53:20 +01:00
void * nca_ctx ; ///< NcaContext. Used to perform NCA reads.
2020-04-20 11:39:41 +01:00
u8 section_num ;
2020-04-21 11:23:33 +01:00
u64 section_offset ;
u64 section_size ;
2020-04-22 21:53:20 +01:00
u8 section_type ; ///< NcaFsSectionType.
u8 encryption_type ; ///< NcaEncryptionType.
2020-04-20 11:39:41 +01:00
NcaFsHeader * header ;
2020-04-22 21:53:20 +01:00
u8 ctr [ 0x10 ] ; ///< Used to update the AES CTR context IV based on the desired offset.
2020-04-20 11:39:41 +01:00
Aes128CtrContext ctr_ctx ;
2020-04-22 21:53:20 +01:00
Aes128XtsContext xts_decrypt_ctx ;
Aes128XtsContext xts_encrypt_ctx ;
2020-04-21 11:23:33 +01:00
} NcaFsSectionContext ;
2020-04-15 21:50:07 +01:00
typedef struct {
u8 storage_id ; ///< NcmStorageId.
NcmContentStorage * ncm_storage ; ///< Pointer to a NcmContentStorage instance. Used to read NCA data.
2020-04-16 11:13:11 +01:00
u64 gamecard_offset ; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard.
2020-04-21 11:23:33 +01:00
NcmContentId content_id ; ///< Also used to read NCA data.
char content_id_str [ 0x21 ] ;
u8 hash [ 0x20 ] ; ///< Retrieved from NcmPackagedContentInfo.
2020-04-15 21:50:07 +01:00
char hash_str [ 0x41 ] ;
u8 format_version ; ///< NcaVersion.
2020-04-21 11:23:33 +01:00
u8 content_type ; ///< NcmContentType. Retrieved from NcmPackagedContentInfo.
u64 content_size ; ///< Retrieved from NcmPackagedContentInfo.
2020-04-15 21:50:07 +01:00
u8 key_generation ; ///< NcaKeyGenerationOld / NcaKeyGeneration. Retrieved from the decrypted header.
2020-04-21 11:23:33 +01:00
u8 id_offset ; ///< Retrieved from NcmPackagedContentInfo.
2020-04-15 21:50:07 +01:00
bool rights_id_available ;
2020-04-21 11:23:33 +01:00
u8 titlekey [ 0x10 ] ;
2020-04-15 21:50:07 +01:00
bool dirty_header ;
2020-04-20 11:39:41 +01:00
NcaHeader header ;
2020-04-21 11:23:33 +01:00
NcaFsSectionContext fs_contexts [ 4 ] ;
2020-04-20 11:39:41 +01:00
NcaKey decrypted_keys [ 4 ] ;
2020-04-15 21:50:07 +01:00
} NcaContext ;
2020-04-11 06:28:26 +01:00
2020-04-22 21:53:20 +01:00
/// Functions to control the internal heap buffer used by NCA FS section crypto operations.
2020-04-21 11:23:33 +01:00
/// Must be called at startup.
bool ncaAllocateCryptoBuffer ( void ) ;
void ncaFreeCryptoBuffer ( void ) ;
2020-04-24 10:38:13 +01:00
/// Initializes a NCA context.
2020-04-21 11:23:33 +01:00
/// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' argument must point to a valid NcmContentStorage instance, previously opened using the same NcmStorageId value.
/// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid GameCardHashFileSystemPartitionType value.
2020-04-22 21:53:20 +01:00
/// If the NCA holds a populated Rights ID field, and if the Ticket object pointed to by 'tik' hasn't been filled, ticket data will be retrieved.
bool ncaInitializeContext ( NcaContext * out , u8 storage_id , NcmContentStorage * ncm_storage , u8 hfs_partition_type , const NcmPackagedContentInfo * content_info , Ticket * tik ) ;
2020-04-21 11:23:33 +01:00
2020-04-22 21:53:20 +01:00
/// Reads raw encrypted data from a NCA using an input context, previously initialized by ncaInitializeContext().
bool ncaReadContentFile ( NcaContext * ctx , void * out , u64 read_size , u64 offset ) ;
2020-04-19 23:44:22 +01:00
2020-04-22 21:53:20 +01:00
/// Reads decrypted data from a NCA FS section using an input context.
2020-04-21 11:23:33 +01:00
/// Input offset must be relative to the start of the NCA FS section.
2020-04-22 21:53:20 +01:00
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR subsections.
2020-04-21 11:23:33 +01:00
bool ncaReadFsSection ( NcaFsSectionContext * ctx , void * out , u64 read_size , u64 offset ) ;
2020-04-22 21:53:20 +01:00
/// Returns a pointer to a heap-allocated buffer used to encrypt the input plaintext data, based on the encryption type used by the input NCA FS section, as well as its offset and size.
/// Input offset must be relative to the start of the NCA FS section.
/// Output size and offset are guaranteed to be aligned to the AES sector size used by the encryption type from the FS section.
/// Output offset is relative to the start of the NCA content file, making it easier to use the output encrypted block to replace data in-place while writing a NCA.
void * ncaGenerateEncryptedFsSectionBlock ( NcaFsSectionContext * ctx , void * data , u64 data_size , u64 data_offset , u64 * out_block_size , u64 * out_block_offset ) ;
2020-04-21 11:23:33 +01:00
2020-04-19 23:44:22 +01:00
2020-04-21 11:23:33 +01:00
bool ncaEncryptKeyArea ( NcaContext * nca_ctx ) ;
bool ncaEncryptHeader ( NcaContext * ctx ) ;
2020-04-19 23:44:22 +01:00
2020-04-21 11:23:33 +01:00
/// Miscellanous functions.
2020-04-15 21:50:07 +01:00
static inline void ncaConvertNcmContentSizeToU64 ( const u8 * size , u64 * out )
{
2020-04-24 10:38:13 +01:00
if ( ! size | | ! out ) return ;
* out = 0 ;
memcpy ( out , size , 6 ) ;
2020-04-15 21:50:07 +01:00
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
static inline void ncaConvertU64ToNcmContentSize ( const u64 * size , u8 * out )
{
2020-04-22 21:53:20 +01:00
if ( size & & out ) memcpy ( out , size , 6 ) ;
2020-04-15 21:50:07 +01:00
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
static inline void ncaSetDownloadDistributionType ( NcaContext * ctx )
{
2020-04-24 10:38:13 +01:00
if ( ! ctx | | ctx - > header . distribution_type = = NcaDistributionType_Download ) return ;
ctx - > header . distribution_type = NcaDistributionType_Download ;
ctx - > dirty_header = true ;
2020-04-15 21:50:07 +01:00
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
static inline void ncaWipeRightsId ( NcaContext * ctx )
{
2020-04-24 10:38:13 +01:00
if ( ! ctx | | ! ctx - > rights_id_available ) return ;
memset ( & ( ctx - > header . rights_id ) , 0 , sizeof ( FsRightsId ) ) ;
ctx - > dirty_header = true ;
2020-04-15 21:50:07 +01:00
}
2020-04-11 06:28:26 +01:00
2020-04-15 21:50:07 +01:00
# endif /* __NCA_H__ */