2020-10-08 22:52:31 +01:00
/*
* npdm . c
*
2020-12-23 17:48:57 +00:00
* Copyright ( c ) 2020 - 2021 , DarkMatterCore < pabloacurielz @ gmail . com > .
2020-10-08 22:52:31 +01:00
*
* This file is part of nxdumptool ( https : //github.com/DarkMatterCore/nxdumptool).
*
2021-03-25 19:26:58 +00:00
* nxdumptool is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
2020-10-08 22:52:31 +01:00
*
2021-03-25 19:26:58 +00:00
* nxdumptool is distributed in the hope that 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 .
2020-10-08 22:52:31 +01:00
*
* You should have received a copy of the GNU General Public License
2021-03-25 19:26:58 +00:00
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
2020-10-08 22:52:31 +01:00
*/
2021-03-26 04:35:14 +00:00
# include "nxdt_utils.h"
2020-10-08 22:52:31 +01:00
# include "npdm.h"
2020-10-10 22:08:17 +01:00
# include "rsa.h"
2020-10-08 22:52:31 +01:00
2020-10-10 20:29:14 +01:00
bool npdmInitializeContext ( NpdmContext * out , PartitionFileSystemContext * pfs_ctx )
{
NcaContext * nca_ctx = NULL ;
u64 cur_offset = 0 ;
2021-03-07 23:22:49 +00:00
bool success = false , dump_meta_header = false , dump_acid_header = false , dump_aci_header = false ;
2020-10-10 20:29:14 +01:00
if ( ! out | | ! pfs_ctx | | ! pfs_ctx - > nca_fs_ctx | | ! ( nca_ctx = ( NcaContext * ) pfs_ctx - > nca_fs_ctx - > nca_ctx ) | | nca_ctx - > content_type ! = NcmContentType_Program | | ! pfs_ctx - > offset | | ! pfs_ctx - > size | | \
! pfs_ctx - > is_exefs | | pfs_ctx - > header_size < = sizeof ( PartitionFileSystemHeader ) | | ! pfs_ctx - > header )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid parameters! " ) ;
2020-10-10 20:29:14 +01:00
return false ;
}
/* Free output context beforehand. */
npdmFreeContext ( out ) ;
/* Get 'main.npdm' file entry. */
2020-11-08 19:08:30 +00:00
out - > nca_ctx = nca_ctx ;
2020-10-10 20:29:14 +01:00
out - > pfs_ctx = pfs_ctx ;
if ( ! ( out - > pfs_entry = pfsGetEntryByName ( out - > pfs_ctx , " main.npdm " ) ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " 'main.npdm' entry unavailable in ExeFS! " ) ;
2020-10-10 20:29:14 +01:00
goto end ;
}
2021-03-07 23:22:49 +00:00
//LOG_MSG("Found 'main.npdm' entry in Program NCA \"%s\".", nca_ctx->content_id_str);
2020-10-10 20:29:14 +01:00
/* Check raw NPDM size. */
if ( ! out - > pfs_entry - > size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid raw NPDM size! " ) ;
2020-10-10 20:29:14 +01:00
goto end ;
}
/* Allocate memory for the raw NPDM data. */
out - > raw_data_size = out - > pfs_entry - > size ;
if ( ! ( out - > raw_data = malloc ( out - > raw_data_size ) ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Failed to allocate memory for the raw NPDM data! " ) ;
2020-10-10 20:29:14 +01:00
goto end ;
}
/* Read raw NPDM data into memory buffer. */
if ( ! pfsReadEntryData ( out - > pfs_ctx , out - > pfs_entry , out - > raw_data , out - > raw_data_size , 0 ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Failed to read raw NPDM data! " ) ;
2020-10-10 20:29:14 +01:00
goto end ;
}
/* Verify meta header. */
out - > meta_header = ( NpdmMetaHeader * ) out - > raw_data ;
cur_offset + = sizeof ( NpdmMetaHeader ) ;
if ( __builtin_bswap32 ( out - > meta_header - > magic ) ! = NPDM_META_MAGIC )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid meta header magic word! (0x%08X != 0x%08X). " , __builtin_bswap32 ( out - > meta_header - > magic ) , __builtin_bswap32 ( NPDM_META_MAGIC ) ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( ! out - > meta_header - > flags . is_64bit_instruction & & ( out - > meta_header - > flags . process_address_space = = NpdmProcessAddressSpace_AddressSpace64BitOld | | \
out - > meta_header - > flags . process_address_space = = NpdmProcessAddressSpace_AddressSpace64Bit ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid meta header flags! (0x%02X). " , * ( ( u8 * ) & ( out - > meta_header - > flags ) ) ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > meta_header - > main_thread_priority > NPDM_MAIN_THREAD_MAX_PRIORITY )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid main thread priority! (0x%02X). " , out - > meta_header - > main_thread_priority ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > meta_header - > main_thread_core_number > NPDM_MAIN_THREAD_MAX_CORE_NUMBER )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid main thread core number! (%u). " , out - > meta_header - > main_thread_core_number ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > meta_header - > system_resource_size > NPDM_SYSTEM_RESOURCE_MAX_SIZE )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid system resource size! (0x%08X). " , out - > meta_header - > system_resource_size ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( ! IS_ALIGNED ( out - > meta_header - > main_thread_stack_size , NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid main thread stack size! (0x%08X). " , out - > meta_header - > main_thread_stack_size ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > meta_header - > aci_offset < sizeof ( NpdmMetaHeader ) | | out - > meta_header - > aci_size < sizeof ( NpdmAciHeader ) | | ( out - > meta_header - > aci_offset + out - > meta_header - > aci_size ) > out - > raw_data_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACI0 offset/size! (0x%08X, 0x%08X). " , out - > meta_header - > aci_offset , out - > meta_header - > aci_size ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > meta_header - > acid_offset < sizeof ( NpdmMetaHeader ) | | out - > meta_header - > acid_size < sizeof ( NpdmAcidHeader ) | | ( out - > meta_header - > acid_offset + out - > meta_header - > acid_size ) > out - > raw_data_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID offset/size! (0x%08X, 0x%08X). " , out - > meta_header - > acid_offset , out - > meta_header - > acid_size ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > meta_header - > aci_offset = = out - > meta_header - > acid_offset | | \
( out - > meta_header - > aci_offset > out - > meta_header - > acid_offset & & out - > meta_header - > aci_offset < ( out - > meta_header - > acid_offset + out - > meta_header - > acid_size ) ) | | \
( out - > meta_header - > acid_offset > out - > meta_header - > aci_offset & & out - > meta_header - > acid_offset < ( out - > meta_header - > aci_offset + out - > meta_header - > aci_size ) ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " ACI0/ACID sections overlap! (0x%08X, 0x%08X | 0x%08X, 0x%08X). " , out - > meta_header - > aci_offset , out - > meta_header - > aci_size , out - > meta_header - > acid_offset , out - > meta_header - > acid_size ) ;
dump_meta_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
/* Verify ACID section. */
out - > acid_header = ( NpdmAcidHeader * ) ( out - > raw_data + out - > meta_header - > acid_offset ) ;
cur_offset + = out - > meta_header - > acid_size ;
if ( __builtin_bswap32 ( out - > acid_header - > magic ) ! = NPDM_ACID_MAGIC )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID header magic word! (0x%08X != 0x%08X). " , __builtin_bswap32 ( out - > acid_header - > magic ) , __builtin_bswap32 ( NPDM_ACID_MAGIC ) ) ;
dump_meta_header = dump_acid_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > acid_header - > size ! = ( out - > meta_header - > acid_size - sizeof ( out - > acid_header - > signature ) ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID header size! (0x%08X). " , out - > acid_header - > size ) ;
dump_meta_header = dump_acid_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > acid_header - > program_id_min > out - > acid_header - > program_id_max )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID program ID range! (%016lX - %016lX). " , out - > acid_header - > program_id_min , out - > acid_header - > program_id_max ) ;
dump_meta_header = dump_acid_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
2020-10-13 15:00:03 +01:00
if ( out - > acid_header - > fs_access_control_offset < sizeof ( NpdmAcidHeader ) | | out - > acid_header - > fs_access_control_size < sizeof ( NpdmFsAccessControlDescriptor ) | | \
2020-10-10 20:29:14 +01:00
( out - > acid_header - > fs_access_control_offset + out - > acid_header - > fs_access_control_size ) > out - > meta_header - > acid_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID FsAccessControl offset/size! (0x%08X, 0x%08X). " , out - > acid_header - > fs_access_control_offset , out - > acid_header - > fs_access_control_size ) ;
dump_meta_header = dump_acid_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
2020-10-13 15:00:03 +01:00
out - > acid_fac_descriptor = ( NpdmFsAccessControlDescriptor * ) ( out - > raw_data + out - > meta_header - > acid_offset + out - > acid_header - > fs_access_control_offset ) ;
2020-10-10 20:29:14 +01:00
if ( out - > acid_header - > srv_access_control_size )
{
if ( out - > acid_header - > srv_access_control_offset < sizeof ( NpdmAcidHeader ) | | \
( out - > acid_header - > srv_access_control_offset + out - > acid_header - > srv_access_control_size ) > out - > meta_header - > acid_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID SrvAccessControl offset/size! (0x%08X, 0x%08X). " , out - > acid_header - > srv_access_control_offset , out - > acid_header - > srv_access_control_size ) ;
dump_meta_header = dump_acid_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
out - > acid_sac_descriptor = ( NpdmSrvAccessControlDescriptorEntry * ) ( out - > raw_data + out - > meta_header - > acid_offset + out - > acid_header - > srv_access_control_offset ) ;
}
if ( out - > acid_header - > kernel_capability_size )
{
if ( ! IS_ALIGNED ( out - > acid_header - > kernel_capability_size , sizeof ( NpdmKernelCapabilityDescriptorEntry ) ) | | \
out - > acid_header - > kernel_capability_offset < sizeof ( NpdmAcidHeader ) | | \
( out - > acid_header - > kernel_capability_offset + out - > acid_header - > kernel_capability_size ) > out - > meta_header - > acid_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACID KernelCapability offset/size! (0x%08X, 0x%08X). " , out - > acid_header - > kernel_capability_offset , out - > acid_header - > kernel_capability_size ) ;
dump_meta_header = dump_acid_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
out - > acid_kc_descriptor = ( NpdmKernelCapabilityDescriptorEntry * ) ( out - > raw_data + out - > meta_header - > acid_offset + out - > acid_header - > kernel_capability_offset ) ;
}
/* Verify ACI0 section. */
out - > aci_header = ( NpdmAciHeader * ) ( out - > raw_data + out - > meta_header - > aci_offset ) ;
cur_offset + = out - > meta_header - > aci_size ;
if ( __builtin_bswap32 ( out - > aci_header - > magic ) ! = NPDM_ACI0_MAGIC )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACI0 header magic word! (0x%08X != 0x%08X). " , __builtin_bswap32 ( out - > aci_header - > magic ) , __builtin_bswap32 ( NPDM_ACI0_MAGIC ) ) ;
dump_meta_header = dump_acid_header = dump_aci_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > aci_header - > program_id ! = nca_ctx - > header . program_id )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " ACI0 program ID mismatch! (%016lX != %016lX). " , out - > aci_header - > program_id , nca_ctx - > header . program_id ) ;
dump_meta_header = dump_acid_header = dump_aci_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
if ( out - > aci_header - > program_id < out - > acid_header - > program_id_min | | out - > aci_header - > program_id > out - > acid_header - > program_id_max )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " ACI0 program ID out of ACID program ID range! (%016lX, %016lX - %016lX). " , out - > aci_header - > program_id , out - > acid_header - > program_id_min , out - > acid_header - > program_id_max ) ;
dump_meta_header = dump_acid_header = dump_aci_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
2020-10-13 15:00:03 +01:00
if ( out - > aci_header - > fs_access_control_offset < sizeof ( NpdmAciHeader ) | | out - > aci_header - > fs_access_control_size < sizeof ( NpdmFsAccessControlData ) | | \
2020-10-10 20:29:14 +01:00
( out - > aci_header - > fs_access_control_offset + out - > aci_header - > fs_access_control_size ) > out - > meta_header - > aci_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACI0 FsAccessControl offset/size! (0x%08X, 0x%08X). " , out - > aci_header - > fs_access_control_offset , out - > aci_header - > fs_access_control_size ) ;
dump_meta_header = dump_acid_header = dump_aci_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
2020-10-13 15:00:03 +01:00
out - > aci_fac_data = ( NpdmFsAccessControlData * ) ( out - > raw_data + out - > meta_header - > aci_offset + out - > aci_header - > fs_access_control_offset ) ;
2020-10-10 20:29:14 +01:00
if ( out - > aci_header - > srv_access_control_size )
{
if ( out - > aci_header - > srv_access_control_offset < sizeof ( NpdmAciHeader ) | | \
( out - > aci_header - > srv_access_control_offset + out - > aci_header - > srv_access_control_size ) > out - > meta_header - > aci_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACI0 SrvAccessControl offset/size! (0x%08X, 0x%08X). " , out - > aci_header - > srv_access_control_offset , out - > aci_header - > srv_access_control_size ) ;
dump_meta_header = dump_acid_header = dump_aci_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
out - > aci_sac_descriptor = ( NpdmSrvAccessControlDescriptorEntry * ) ( out - > raw_data + out - > meta_header - > aci_offset + out - > aci_header - > srv_access_control_offset ) ;
}
if ( out - > aci_header - > kernel_capability_size )
{
if ( ! IS_ALIGNED ( out - > aci_header - > kernel_capability_size , sizeof ( NpdmKernelCapabilityDescriptorEntry ) ) | | \
out - > aci_header - > kernel_capability_offset < sizeof ( NpdmAciHeader ) | | \
( out - > aci_header - > kernel_capability_offset + out - > aci_header - > kernel_capability_size ) > out - > meta_header - > aci_size )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid ACI0 KernelCapability offset/size! (0x%08X, 0x%08X). " , out - > aci_header - > kernel_capability_offset , out - > aci_header - > kernel_capability_size ) ;
dump_meta_header = dump_acid_header = dump_aci_header = true ;
2020-10-10 20:29:14 +01:00
goto end ;
}
out - > aci_kc_descriptor = ( NpdmKernelCapabilityDescriptorEntry * ) ( out - > raw_data + out - > meta_header - > aci_offset + out - > aci_header - > kernel_capability_offset ) ;
}
/* Safety check: verify raw NPDM size. */
if ( out - > raw_data_size < cur_offset )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid raw NPDM size! (0x%lX < 0x%lX). " , out - > raw_data_size , cur_offset ) ;
2020-10-10 20:29:14 +01:00
goto end ;
}
success = true ;
end :
2021-03-07 23:22:49 +00:00
if ( ! success )
{
if ( dump_aci_header ) LOG_DATA ( out - > aci_header , sizeof ( NpdmAciHeader ) , " NPDM ACI0 Header dump: " ) ;
if ( dump_acid_header ) LOG_DATA ( out - > acid_header , sizeof ( NpdmAcidHeader ) , " NPDM ACID Header dump: " ) ;
if ( dump_meta_header ) LOG_DATA ( out - > meta_header , sizeof ( NpdmMetaHeader ) , " NPDM Meta Header dump: " ) ;
npdmFreeContext ( out ) ;
}
2020-10-10 20:29:14 +01:00
return success ;
}
2020-10-10 22:08:17 +01:00
2020-10-22 05:38:14 +01:00
bool npdmGenerateNcaPatch ( NpdmContext * npdm_ctx )
2020-10-10 22:08:17 +01:00
{
2020-11-08 19:08:30 +00:00
if ( ! npdmIsValidContext ( npdm_ctx ) | | npdm_ctx - > nca_ctx - > content_type ! = NcmContentType_Program )
2020-10-10 22:08:17 +01:00
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Invalid parameters! " ) ;
2020-10-10 22:08:17 +01:00
return false ;
}
2020-11-08 19:08:30 +00:00
NcaContext * nca_ctx = npdm_ctx - > nca_ctx ;
2020-10-22 05:38:14 +01:00
/* Check if we really need to generate this patch. */
if ( ! ncaIsHeaderDirty ( nca_ctx ) )
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Skipping NPDM patching - NCA header hasn't been modified. " ) ;
2020-10-22 05:38:14 +01:00
return true ;
}
2020-10-10 22:08:17 +01:00
/* Update NPDM ACID public key. */
memcpy ( npdm_ctx - > acid_header - > public_key , rsa2048GetCustomPublicKey ( ) , RSA2048_PUBKEY_SIZE ) ;
2020-10-22 05:38:14 +01:00
/* Generate Partition FS entry patch. */
if ( ! pfsGenerateEntryPatch ( npdm_ctx - > pfs_ctx , npdm_ctx - > pfs_entry , npdm_ctx - > raw_data , npdm_ctx - > raw_data_size , 0 , & ( npdm_ctx - > nca_patch ) ) )
2020-10-10 22:08:17 +01:00
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Failed to generate Partition FS entry patch! " ) ;
2020-10-10 22:08:17 +01:00
return false ;
}
2020-10-22 05:38:14 +01:00
/* Update NCA ACID signature. */
2021-05-21 14:34:43 +01:00
if ( ! rsa2048GenerateSha256BasedPssSignature ( nca_ctx - > header . acid_signature , & ( nca_ctx - > header . magic ) , NCA_SIGNATURE_AREA_SIZE ) )
2020-10-21 05:27:48 +01:00
{
2021-03-07 23:22:49 +00:00
LOG_MSG ( " Failed to generate RSA-2048-PSS NCA ACID signature! " ) ;
2020-10-21 05:27:48 +01:00
return false ;
}
/* Update NCA content type context patch status. */
nca_ctx - > content_type_ctx_patch = true ;
2020-10-10 22:08:17 +01:00
return true ;
}
2020-10-21 05:27:48 +01:00
void npdmWriteNcaPatch ( NpdmContext * npdm_ctx , void * buf , u64 buf_size , u64 buf_offset )
{
NcaContext * nca_ctx = NULL ;
2020-11-08 19:08:30 +00:00
NcaHierarchicalSha256Patch * nca_patch = ( npdm_ctx ? & ( npdm_ctx - > nca_patch ) : NULL ) ;
2020-10-21 05:27:48 +01:00
/* Using npdmIsValidContext() here would probably take up precious CPU cycles. */
2020-11-08 19:08:30 +00:00
if ( ! nca_patch | | nca_patch - > written | | ! ( nca_ctx = npdm_ctx - > nca_ctx ) | | nca_ctx - > content_type ! = NcmContentType_Program | | ! nca_ctx - > content_type_ctx_patch ) return ;
2020-10-21 05:27:48 +01:00
2020-10-28 22:48:46 +00:00
/* Attempt to write Partition FS entry patch. */
2020-11-08 19:08:30 +00:00
pfsWriteEntryPatchToMemoryBuffer ( npdm_ctx - > pfs_ctx , nca_patch , buf , buf_size , buf_offset ) ;
2020-10-21 05:27:48 +01:00
/* Check if we need to update the NCA content type context patch status. */
2020-11-08 19:08:30 +00:00
if ( nca_patch - > written )
2020-10-21 05:27:48 +01:00
{
nca_ctx - > content_type_ctx_patch = false ;
2021-03-07 23:22:49 +00:00
LOG_MSG ( " NPDM Partition FS entry patch successfully written to NCA \" %s \" ! " , nca_ctx - > content_id_str ) ;
2020-10-21 05:27:48 +01:00
}
}