mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 04:02:11 +00:00
More NCA changes.
* Made ncaGenerateEncryptedFsSectionBlock() entirely private. There's no point in keeping it public. * Moved NCA FS section context initialization into its own function, ncaInitializeFsSectionContext(). * Hash data boundaries are now checked while initializing each NCA FS section context, using ncaFsSectionValidateHashDataBoundaries(). Both ncaValidateHierarchicalSha256Offsets() and ncaValidateHierarchicalIntegrityOffsets() have been removed. * Improved hash region access detection in _ncaReadFsSection() by implementing ncaFsSectionCheckHashRegionAccess(). * ncaGetFsSectionHashTargetProperties() is now used in pfs.c, romfs.c and bktr.c to retrieve the properties from the target hash layer. * Updated sanity checks in pfsInitializeContext(), romfsInitializeContext() and bktrInitializeContext().
This commit is contained in:
parent
d722683a77
commit
c1e3dc719f
6 changed files with 519 additions and 392 deletions
|
@ -361,31 +361,32 @@ typedef enum {
|
|||
/// Unlike NCA contexts, we don't need to keep a hash for the NCA FS section header in NCA FS section contexts.
|
||||
/// This is because the functions that modify the NCA FS section header also update the NCA FS section header hash stored in the NCA header.
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
bool enabled; ///< Set to true if this NCA FS section has passed all validation checks and can be safely used.
|
||||
void *nca_ctx; ///< NcaContext. Used to perform NCA reads.
|
||||
NcaFsHeader header; ///< Plaintext NCA FS section header.
|
||||
NcaFsHeader encrypted_header; ///< Encrypted NCA FS section header. If the plaintext NCA FS section header is modified, this will hold an encrypted copy of it.
|
||||
///< Otherwise, this holds the unmodified, encrypted NCA FS section header.
|
||||
u8 section_num; ///< Index within [0 - 3].
|
||||
u64 section_offset; ///< Relative to the start of the NCA content file.
|
||||
u64 section_size;
|
||||
u8 section_idx; ///< Index within [0 - 3].
|
||||
u64 section_offset; ///< Relative to the start of the NCA content file. Placed here for convenience.
|
||||
u64 section_size; ///< Placed here for convenience.
|
||||
u8 hash_type; ///< NcaHashType.
|
||||
u8 encryption_type; ///< NcaEncryptionType.
|
||||
u8 section_type; ///< NcaFsSectionType.
|
||||
bool skip_hash_layer_crypto; ///< Set to true if hash layer encryption should be skipped while reading section data.
|
||||
u64 last_layer_offset; ///< Relative to the start of the FS section.
|
||||
u64 last_layer_size;
|
||||
u8 ctr[AES_BLOCK_SIZE]; ///< Used to update the AES CTR context IV based on the desired offset.
|
||||
Aes128CtrContext ctr_ctx;
|
||||
Aes128XtsContext xts_decrypt_ctx;
|
||||
Aes128XtsContext xts_encrypt_ctx;
|
||||
NcaRegion hash_region; /// Holds the properties for the full hash layer region that precedes the actual FS section data.
|
||||
|
||||
///< Crypto-related fields.
|
||||
u8 ctr[AES_BLOCK_SIZE]; ///< Used internally by NCA functions to update the AES-128-CTR context IV based on the desired offset.
|
||||
Aes128CtrContext ctr_ctx; ///< Used internally by NCA functions to perform AES-128-CTR crypto.
|
||||
Aes128XtsContext xts_decrypt_ctx; ///< Used internally by NCA functions to perform AES-128-XTS decryption.
|
||||
Aes128XtsContext xts_encrypt_ctx; ///< Used internally by NCA functions to perform AES-128-XTS encryption.
|
||||
|
||||
///< SparseInfo-related fields.
|
||||
bool has_sparse_layer;
|
||||
u64 sparse_table_offset; ///< header.sparse_info.physical_offset + header.sparse_info.bucket.offset. Placed here for convenience.
|
||||
bool has_sparse_layer; ///< Set to true if this NCA FS section has a sparse layer.
|
||||
u64 sparse_table_offset; ///< header.sparse_info.physical_offset + header.sparse_info.bucket.offset. Relative to the start of the NCA content file. Placed here for convenience.
|
||||
u64 sparse_table_size; ///< header.sparse_info.bucket.size. Placed here for convenience.
|
||||
u8 sparse_ctr[AES_BLOCK_SIZE];
|
||||
Aes128CtrContext sparse_ctr_ctx;
|
||||
u8 sparse_ctr[AES_BLOCK_SIZE]; ///< AES-128-CTR IV used for sparse table decryption.
|
||||
Aes128CtrContext sparse_ctr_ctx; ///< AES-128-CTR context used for sparse table decryption.
|
||||
|
||||
///< NSP-related fields.
|
||||
bool header_written; ///< Set to true after this FS section header has been written to an output dump.
|
||||
|
@ -487,14 +488,6 @@ bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 of
|
|||
/// Input offset must be relative to the start of the NCA FS section.
|
||||
bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val);
|
||||
|
||||
/// Returns a pointer to a dynamically 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 seamlessly replace data while dumping a NCA.
|
||||
/// This function doesn't support Patch RomFS sections, nor sections with Sparse and/or Compressed storage.
|
||||
/// Used internally by both ncaGenerateHierarchicalSha256Patch() and ncaGenerateHierarchicalIntegrityPatch().
|
||||
void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset);
|
||||
|
||||
/// Generates HierarchicalSha256 FS section patch data, which can be used to seamlessly replace NCA data.
|
||||
/// Input offset must be relative to the start of the last HierarchicalSha256 hash region (actual underlying FS).
|
||||
/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header.
|
||||
|
@ -551,32 +544,41 @@ NX_INLINE bool ncaIsHeaderDirty(NcaContext *ctx)
|
|||
return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0);
|
||||
}
|
||||
|
||||
NX_INLINE bool ncaValidateHierarchicalSha256Offsets(NcaHierarchicalSha256Data *hierarchical_sha256_data, u64 section_size)
|
||||
NX_INLINE bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size)
|
||||
{
|
||||
if (!hierarchical_sha256_data || !section_size || !hierarchical_sha256_data->hash_block_size || !hierarchical_sha256_data->hash_region_count || \
|
||||
hierarchical_sha256_data->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT) return false;
|
||||
if (!ctx || (!out_offset && !out_size)) return false;
|
||||
|
||||
for(u32 i = 0; i < hierarchical_sha256_data->hash_region_count; i++)
|
||||
bool success = true;
|
||||
|
||||
switch(ctx->hash_type)
|
||||
{
|
||||
NcaRegion *hash_region = &(hierarchical_sha256_data->hash_region[i]);
|
||||
if (!hash_region->size || (hash_region->offset + hash_region->size) > section_size) return false;
|
||||
case NcaHashType_None:
|
||||
if (out_offset) *out_offset = 0;
|
||||
if (out_size) *out_size = ctx->section_size;
|
||||
break;
|
||||
case NcaHashType_HierarchicalSha256:
|
||||
case NcaHashType_HierarchicalSha3256:
|
||||
{
|
||||
u32 layer_count = ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
||||
NcaRegion *hash_region = &(ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
|
||||
if (out_offset) *out_offset = hash_region->offset;
|
||||
if (out_size) *out_size = hash_region->size;
|
||||
}
|
||||
break;
|
||||
case NcaHashType_HierarchicalIntegrity:
|
||||
case NcaHashType_HierarchicalIntegritySha3:
|
||||
{
|
||||
NcaHierarchicalIntegrityVerificationLevelInformation *lvl_info = &(ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1]);
|
||||
if (out_offset) *out_offset = lvl_info->offset;
|
||||
if (out_size) *out_size = lvl_info->size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NX_INLINE bool ncaValidateHierarchicalIntegrityOffsets(NcaIntegrityMetaInfo *integrity_meta_info, u64 section_size)
|
||||
{
|
||||
if (!integrity_meta_info || !section_size || __builtin_bswap32(integrity_meta_info->magic) != NCA_IVFC_MAGIC || integrity_meta_info->master_hash_size != SHA256_HASH_SIZE || \
|
||||
integrity_meta_info->info_level_hash.max_level_count != NCA_IVFC_MAX_LEVEL_COUNT) return false;
|
||||
|
||||
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
||||
{
|
||||
NcaHierarchicalIntegrityVerificationLevelInformation *level_information = &(integrity_meta_info->info_level_hash.level_information[i]);
|
||||
if (!level_information->size || !level_information->block_order || (level_information->offset + level_information->size) > section_size) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return success;
|
||||
}
|
||||
|
||||
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
|
||||
|
|
|
@ -40,8 +40,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
|||
|
||||
if (!out || !base_nca_fs_ctx || !(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || \
|
||||
!update_nca_fs_ctx || !update_nca_fs_ctx->enabled || !(update_nca_ctx = (NcaContext*)update_nca_fs_ctx->nca_ctx) || \
|
||||
update_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || (update_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && \
|
||||
update_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || base_nca_ctx->header.program_id != update_nca_ctx->header.program_id || \
|
||||
update_nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs || base_nca_ctx->header.program_id != update_nca_ctx->header.program_id || \
|
||||
base_nca_ctx->header.content_type != update_nca_ctx->header.content_type || \
|
||||
__builtin_bswap32(update_nca_fs_ctx->header.patch_info.indirect_bucket.header.magic) != NCA_BKTR_MAGIC || \
|
||||
update_nca_fs_ctx->header.patch_info.indirect_bucket.header.version != NCA_BKTR_VERSION || \
|
||||
|
@ -59,8 +58,7 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
|||
bktrFreeContext(out);
|
||||
|
||||
/* Update missing base NCA RomFS status. */
|
||||
out->missing_base_romfs = (!base_nca_fs_ctx->enabled || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
|
||||
(base_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtr && base_nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrSkipLayerHash));
|
||||
out->missing_base_romfs = (!base_nca_fs_ctx->enabled || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs);
|
||||
|
||||
/* Initialize base NCA RomFS context. */
|
||||
if (!out->missing_base_romfs && !romfsInitializeContext(&(out->base_romfs_ctx), base_nca_fs_ctx))
|
||||
|
@ -149,8 +147,15 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
|||
/* Initialize update NCA RomFS context. */
|
||||
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image. */
|
||||
out->patch_romfs_ctx.nca_fs_ctx = update_nca_fs_ctx;
|
||||
out->patch_romfs_ctx.offset = out->offset = update_nca_fs_ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1].offset;
|
||||
out->patch_romfs_ctx.size = out->size = update_nca_fs_ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[NCA_IVFC_LEVEL_COUNT - 1].size;
|
||||
|
||||
if (!ncaGetFsSectionHashTargetProperties(update_nca_fs_ctx, &(out->offset), &(out->size)))
|
||||
{
|
||||
LOG_MSG("Failed to get target hash layer properties!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
out->patch_romfs_ctx.offset = out->offset;
|
||||
out->patch_romfs_ctx.size = out->size;
|
||||
|
||||
/* Read update NCA RomFS header. */
|
||||
if (!bktrPhysicalSectionRead(out, &(out->patch_romfs_ctx.header), sizeof(RomFileSystemHeader), out->patch_romfs_ctx.offset))
|
||||
|
|
|
@ -57,14 +57,19 @@ NX_INLINE bool ncaIsVersion0KeyAreaEncrypted(NcaContext *ctx);
|
|||
NX_INLINE u8 ncaGetKeyGenerationValue(NcaContext *ctx);
|
||||
NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx);
|
||||
|
||||
static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx);
|
||||
static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx);
|
||||
|
||||
static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset);
|
||||
static bool ncaFsSectionCheckHashRegionAccess(NcaFsSectionContext *ctx, u64 offset, u64 size, u64 *out_chunk_size);
|
||||
|
||||
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val);
|
||||
|
||||
static void ncaCalculateLayerHash(void *dst, const void *src, size_t size, bool use_sha3);
|
||||
static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, void *out, bool is_integrity_patch);
|
||||
static bool ncaWritePatchToMemoryBuffer(NcaContext *ctx, const void *patch, u64 patch_size, u64 patch_offset, void *buf, u64 buf_size, u64 buf_offset);
|
||||
|
||||
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset);
|
||||
static void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset);
|
||||
|
||||
bool ncaAllocateCryptoBuffer(void)
|
||||
{
|
||||
|
@ -92,12 +97,11 @@ void ncaFreeCryptoBuffer(void)
|
|||
bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, const NcmContentInfo *content_info, Ticket *tik)
|
||||
{
|
||||
NcmContentStorage *ncm_storage = NULL;
|
||||
u8 fs_header_hash_calc[SHA256_HASH_SIZE] = {0};
|
||||
u8 valid_fs_section_cnt = 0;
|
||||
|
||||
if (!out || (storage_id != NcmStorageId_GameCard && !(ncm_storage = titleGetNcmStorageByStorageId(storage_id))) || \
|
||||
(storage_id == NcmStorageId_GameCard && (!hfs_partition_type || hfs_partition_type >= GameCardHashFileSystemPartitionType_Count)) || !content_info || \
|
||||
content_info->content_type > NcmContentType_DeltaFragment)
|
||||
content_info->content_type >= NcmContentType_DeltaFragment)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -166,191 +170,8 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
|
|||
/* Parse NCA FS sections. */
|
||||
for(u8 i = 0; i < NCA_FS_HEADER_COUNT; i++)
|
||||
{
|
||||
NcaFsInfo *fs_info = &(out->header.fs_info[i]);
|
||||
NcaFsSectionContext *fs_ctx = &(out->fs_ctx[i]);
|
||||
u8 *fs_header_hash = out->header.fs_header_hash[i].hash;
|
||||
|
||||
NcaSparseInfo *sparse_info = &(fs_ctx->header.sparse_info);
|
||||
NcaBucketInfo *sparse_bucket = &(sparse_info->bucket);
|
||||
|
||||
/* Fill section context. */
|
||||
fs_ctx->nca_ctx = out;
|
||||
fs_ctx->section_num = i;
|
||||
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
||||
fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
|
||||
|
||||
/* Don't proceed if this NCA FS section isn't populated. */
|
||||
if (!ncaIsFsInfoEntryValid(fs_info)) continue;
|
||||
|
||||
/* Calculate NCA FS section header hash. */
|
||||
sha256CalculateHash(fs_header_hash_calc, &(fs_ctx->header), sizeof(NcaFsHeader));
|
||||
|
||||
/* Don't proceed if there's a checksum mismatch. */
|
||||
if (memcmp(fs_header_hash_calc, fs_header_hash, SHA256_HASH_SIZE) != 0) continue;
|
||||
|
||||
/* Calculate section offset and size. */
|
||||
fs_ctx->section_offset = NCA_FS_SECTOR_OFFSET(fs_info->start_sector);
|
||||
fs_ctx->section_size = (NCA_FS_SECTOR_OFFSET(fs_info->end_sector) - fs_ctx->section_offset);
|
||||
|
||||
/* Check if we're dealing with an invalid start offset or an empty size. */
|
||||
if (fs_ctx->section_offset < sizeof(NcaHeader) || !fs_ctx->section_size) continue;
|
||||
|
||||
/* Determine FS section hash type. */
|
||||
fs_ctx->hash_type = fs_ctx->header.hash_type;
|
||||
if (fs_ctx->hash_type == NcaHashType_Auto || fs_ctx->hash_type == NcaHashType_AutoSha3)
|
||||
{
|
||||
switch(fs_ctx->section_num)
|
||||
{
|
||||
case 0: /* ExeFS Partition FS. */
|
||||
case 2: /* Logo Partition FS. */
|
||||
fs_ctx->hash_type = (fs_ctx->hash_type == NcaHashType_Auto ? NcaHashType_HierarchicalSha256 : NcaHashType_HierarchicalSha3256);
|
||||
break;
|
||||
case 1: /* RomFS. */
|
||||
fs_ctx->hash_type = (fs_ctx->hash_type == NcaHashType_Auto ? NcaHashType_HierarchicalIntegrity : NcaHashType_HierarchicalIntegritySha3);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs_ctx->hash_type == NcaHashType_Auto || fs_ctx->hash_type == NcaHashType_AutoSha3 || fs_ctx->hash_type > NcaHashType_HierarchicalIntegritySha3) continue;
|
||||
|
||||
/* Determine FS section encryption type. */
|
||||
fs_ctx->encryption_type = (out->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : fs_ctx->header.encryption_type);
|
||||
if (fs_ctx->encryption_type == NcaEncryptionType_Auto)
|
||||
{
|
||||
switch(fs_ctx->section_num)
|
||||
{
|
||||
case 0: /* ExeFS Partition FS. */
|
||||
case 1: /* RomFS. */
|
||||
fs_ctx->encryption_type = NcaEncryptionType_AesCtr;
|
||||
break;
|
||||
case 2: /* Logo Partition FS. */
|
||||
fs_ctx->encryption_type = NcaEncryptionType_None;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs_ctx->encryption_type == NcaEncryptionType_Auto || fs_ctx->encryption_type > NcaEncryptionType_AesCtrExSkipLayerHash) continue;
|
||||
|
||||
/* Determine FS section type. */
|
||||
if (fs_ctx->header.fs_type == NcaFsType_PartitionFs && (fs_ctx->hash_type == NcaHashType_HierarchicalSha256 || fs_ctx->hash_type == NcaHashType_HierarchicalSha3256))
|
||||
{
|
||||
fs_ctx->section_type = NcaFsSectionType_PartitionFs;
|
||||
} else
|
||||
if (fs_ctx->header.fs_type == NcaFsType_RomFs && (fs_ctx->hash_type == NcaHashType_HierarchicalIntegrity || fs_ctx->hash_type == NcaHashType_HierarchicalIntegritySha3))
|
||||
{
|
||||
fs_ctx->section_type = ((fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx || fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash) ? \
|
||||
NcaFsSectionType_PatchRomFs : NcaFsSectionType_RomFs);
|
||||
} else
|
||||
if (fs_ctx->header.fs_type == NcaFsType_RomFs && fs_ctx->hash_type == NcaHashType_HierarchicalSha256 && out->format_version == NcaVersion_Nca0)
|
||||
{
|
||||
fs_ctx->section_type = NcaFsSectionType_Nca0RomFs;
|
||||
}
|
||||
|
||||
if (fs_ctx->section_type >= NcaFsSectionType_Invalid) continue;
|
||||
|
||||
/* Check if we should skip hash layer decryption while reading this FS section. */
|
||||
fs_ctx->skip_hash_layer_crypto = (fs_ctx->encryption_type == NcaEncryptionType_AesCtrSkipLayerHash || fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash);
|
||||
if (fs_ctx->skip_hash_layer_crypto)
|
||||
{
|
||||
u32 layer_count = 0;
|
||||
|
||||
if (fs_ctx->hash_type == NcaHashType_HierarchicalSha256 || fs_ctx->hash_type == NcaHashType_HierarchicalSha3256)
|
||||
{
|
||||
layer_count = fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
||||
if (layer_count <= NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT)
|
||||
{
|
||||
NcaRegion *last_region = &(fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
|
||||
fs_ctx->last_layer_offset = last_region->offset;
|
||||
fs_ctx->last_layer_size = last_region->size;
|
||||
}
|
||||
} else
|
||||
if (fs_ctx->hash_type == NcaHashType_HierarchicalIntegrity || fs_ctx->hash_type == NcaHashType_HierarchicalIntegritySha3)
|
||||
{
|
||||
layer_count = (fs_ctx->header.hash_data.integrity_meta_info.info_level_hash.max_level_count - 1);
|
||||
if (layer_count == NCA_IVFC_LEVEL_COUNT)
|
||||
{
|
||||
NcaHierarchicalIntegrityVerificationLevelInformation *last_level_info = &(fs_ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[layer_count - 1]);
|
||||
fs_ctx->last_layer_offset = last_level_info->offset;
|
||||
fs_ctx->last_layer_size = last_level_info->size;
|
||||
}
|
||||
} else {
|
||||
fs_ctx->skip_hash_layer_crypto = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we're dealing with a sparse storage. */
|
||||
if (fs_ctx->has_sparse_layer)
|
||||
{
|
||||
/* Check if the sparse bucket is valid. */
|
||||
u64 raw_storage_offset = sparse_info->physical_offset;
|
||||
u64 raw_storage_size = (sparse_bucket->offset + sparse_bucket->size);
|
||||
|
||||
if (__builtin_bswap32(sparse_bucket->header.magic) != NCA_BKTR_MAGIC || sparse_bucket->header.version != NCA_BKTR_VERSION || raw_storage_offset < sizeof(NcaHeader) || \
|
||||
((raw_storage_offset + raw_storage_size) > out->content_size)) continue;
|
||||
|
||||
if (!raw_storage_size || !sparse_bucket->header.entry_count)
|
||||
{
|
||||
/* Increase valid FS section count but don't set this FS section as enabled, since we can't use it. */
|
||||
valid_fs_section_cnt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Set sparse table properties. */
|
||||
fs_ctx->sparse_table_offset = (sparse_info->physical_offset + sparse_bucket->offset);
|
||||
fs_ctx->sparse_table_size = sparse_bucket->size;
|
||||
} else {
|
||||
/* Check if we're within boundaries. */
|
||||
if ((fs_ctx->section_offset + fs_ctx->section_size) > out->content_size) continue;
|
||||
}
|
||||
|
||||
/* Initialize crypto data. */
|
||||
if ((!out->rights_id_available || (out->rights_id_available && out->titlekey_retrieved)) && fs_ctx->encryption_type > NcaEncryptionType_None && \
|
||||
fs_ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash)
|
||||
{
|
||||
/* Initialize the partial AES counter for this section. */
|
||||
aes128CtrInitializePartialCtr(fs_ctx->ctr, fs_ctx->header.aes_ctr_upper_iv.value, fs_ctx->section_offset);
|
||||
|
||||
if (fs_ctx->has_sparse_layer)
|
||||
{
|
||||
/* Initialize the partial AES counter for the sparse info bucket table. */
|
||||
NcaAesCtrUpperIv sparse_upper_iv = {0};
|
||||
memcpy(sparse_upper_iv.value, fs_ctx->header.aes_ctr_upper_iv.value, sizeof(sparse_upper_iv.value));
|
||||
sparse_upper_iv.generation = ((u32)(sparse_info->generation) << 16);
|
||||
|
||||
aes128CtrInitializePartialCtr(fs_ctx->sparse_ctr, sparse_upper_iv.value, fs_ctx->sparse_table_offset);
|
||||
}
|
||||
|
||||
/* Initialize AES context. */
|
||||
if (out->rights_id_available)
|
||||
{
|
||||
/* AES-128-CTR is always used for FS crypto in NCAs with a rights ID. */
|
||||
aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->titlekey, fs_ctx->ctr);
|
||||
if (fs_ctx->has_sparse_layer) aes128CtrContextCreate(&(fs_ctx->sparse_ctr_ctx), out->titlekey, fs_ctx->sparse_ctr);
|
||||
} else {
|
||||
if (fs_ctx->encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
/* We need to create two different contexts with AES-128-XTS: one for decryption and another one for encryption. */
|
||||
aes128XtsContextCreate(&(fs_ctx->xts_decrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, false);
|
||||
aes128XtsContextCreate(&(fs_ctx->xts_encrypt_ctx), out->decrypted_key_area.aes_xts_1, out->decrypted_key_area.aes_xts_2, true);
|
||||
} else
|
||||
if (fs_ctx->encryption_type >= NcaEncryptionType_AesCtr && fs_ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash)
|
||||
{
|
||||
/* Patch RomFS sections also use the AES-128-CTR key from the decrypted NCA key area, for some reason. */
|
||||
aes128CtrContextCreate(&(fs_ctx->ctr_ctx), out->decrypted_key_area.aes_ctr, fs_ctx->ctr);
|
||||
if (fs_ctx->has_sparse_layer) aes128CtrContextCreate(&(fs_ctx->sparse_ctr_ctx), out->decrypted_key_area.aes_ctr, fs_ctx->sparse_ctr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable FS context if we got up to this point. */
|
||||
fs_ctx->enabled = true;
|
||||
|
||||
/* Increase valid NCA FS section count. */
|
||||
valid_fs_section_cnt++;
|
||||
/* Increase valid NCA FS section count if the FS section is valid. */
|
||||
if (ncaInitializeFsSectionContext(out, i)) valid_fs_section_cnt++;
|
||||
}
|
||||
|
||||
if (!valid_fs_section_cnt) LOG_MSG("Unable to identify any valid FS sections in NCA \"%s\"!", out->content_id_str);
|
||||
|
@ -401,13 +222,6 @@ bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out,
|
|||
return ret;
|
||||
}
|
||||
|
||||
void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset)
|
||||
{
|
||||
void *ret = NULL;
|
||||
SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = _ncaGenerateEncryptedFsSectionBlock(ctx, data, data_size, data_offset, out_block_size, out_block_offset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out)
|
||||
{
|
||||
bool ret = false;
|
||||
|
@ -608,7 +422,7 @@ const char *ncaGetFsSectionTypeName(NcaFsSectionContext *ctx)
|
|||
|
||||
if (!ctx || !ctx->enabled || !(nca_ctx = (NcaContext*)ctx->nca_ctx)) return str;
|
||||
|
||||
is_exefs = (nca_ctx->content_type == NcmContentType_Program && ctx->section_num == 0);
|
||||
is_exefs = (nca_ctx->content_type == NcmContentType_Program && ctx->section_idx == 0);
|
||||
|
||||
switch(ctx->section_type)
|
||||
{
|
||||
|
@ -874,9 +688,363 @@ NX_INLINE bool ncaCheckRightsIdAvailability(NcaContext *ctx)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool ncaInitializeFsSectionContext(NcaContext *nca_ctx, u32 section_idx)
|
||||
{
|
||||
if (!nca_ctx || section_idx >= NCA_FS_HEADER_COUNT)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
NcaFsInfo *fs_info = &(nca_ctx->header.fs_info[section_idx]);
|
||||
NcaFsSectionContext *fs_ctx = &(nca_ctx->fs_ctx[section_idx]);
|
||||
|
||||
u8 fs_header_hash_calc[SHA256_HASH_SIZE] = {0};
|
||||
u8 *fs_header_hash = nca_ctx->header.fs_header_hash[section_idx].hash;
|
||||
|
||||
NcaSparseInfo *sparse_info = &(fs_ctx->header.sparse_info);
|
||||
NcaBucketInfo *sparse_bucket = &(sparse_info->bucket);
|
||||
|
||||
bool success = false;
|
||||
|
||||
/* Clear FS section context. */
|
||||
memset(fs_ctx, 0, sizeof(NcaFsSectionContext));
|
||||
|
||||
/* Fill section context. */
|
||||
fs_ctx->nca_ctx = nca_ctx;
|
||||
fs_ctx->section_idx = section_idx;
|
||||
fs_ctx->section_type = NcaFsSectionType_Invalid; /* Placeholder. */
|
||||
fs_ctx->has_sparse_layer = (sparse_info->generation != 0);
|
||||
|
||||
/* Don't proceed if this NCA FS section isn't populated. */
|
||||
if (!ncaIsFsInfoEntryValid(fs_info))
|
||||
{
|
||||
LOG_MSG("Invalid FsInfo entry for section #%u in \"%s\". Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Calculate NCA FS section header hash. Don't proceed if there's a checksum mismatch. */
|
||||
sha256CalculateHash(fs_header_hash_calc, &(fs_ctx->header), sizeof(NcaFsHeader));
|
||||
if (memcmp(fs_header_hash_calc, fs_header_hash, SHA256_HASH_SIZE) != 0)
|
||||
{
|
||||
LOG_MSG("Checksum mismatch for FS section header #%u in \"%s\". Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Calculate section offset and size. */
|
||||
fs_ctx->section_offset = NCA_FS_SECTOR_OFFSET(fs_info->start_sector);
|
||||
fs_ctx->section_size = (NCA_FS_SECTOR_OFFSET(fs_info->end_sector) - fs_ctx->section_offset);
|
||||
|
||||
/* Check if we're dealing with an invalid start offset or an empty size. */
|
||||
if (fs_ctx->section_offset < sizeof(NcaHeader) || !fs_ctx->section_size)
|
||||
{
|
||||
LOG_MSG("Invalid offset/size for FS section #%u in \"%s\" (0x%lX, 0x%lX). Skipping FS section.", section_idx, nca_ctx->content_id_str, fs_ctx->section_offset, \
|
||||
fs_ctx->section_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Determine FS section hash type. */
|
||||
fs_ctx->hash_type = fs_ctx->header.hash_type;
|
||||
if (fs_ctx->hash_type == NcaHashType_Auto || fs_ctx->hash_type == NcaHashType_AutoSha3)
|
||||
{
|
||||
switch(fs_ctx->section_idx)
|
||||
{
|
||||
case 0: /* ExeFS Partition FS. */
|
||||
case 2: /* Logo Partition FS. */
|
||||
fs_ctx->hash_type = (fs_ctx->hash_type == NcaHashType_Auto ? NcaHashType_HierarchicalSha256 : NcaHashType_HierarchicalSha3256);
|
||||
break;
|
||||
case 1: /* RomFS. */
|
||||
fs_ctx->hash_type = (fs_ctx->hash_type == NcaHashType_Auto ? NcaHashType_HierarchicalIntegrity : NcaHashType_HierarchicalIntegritySha3);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs_ctx->hash_type == NcaHashType_Auto || fs_ctx->hash_type == NcaHashType_AutoSha3 || fs_ctx->hash_type > NcaHashType_HierarchicalIntegritySha3)
|
||||
{
|
||||
LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", section_idx, nca_ctx->content_id_str, fs_ctx->hash_type);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Determine FS section encryption type. */
|
||||
fs_ctx->encryption_type = (nca_ctx->format_version == NcaVersion_Nca0 ? NcaEncryptionType_AesXts : fs_ctx->header.encryption_type);
|
||||
if (fs_ctx->encryption_type == NcaEncryptionType_Auto)
|
||||
{
|
||||
switch(fs_ctx->section_idx)
|
||||
{
|
||||
case 0: /* ExeFS Partition FS. */
|
||||
case 1: /* RomFS. */
|
||||
fs_ctx->encryption_type = NcaEncryptionType_AesCtr;
|
||||
break;
|
||||
case 2: /* Logo Partition FS. */
|
||||
fs_ctx->encryption_type = NcaEncryptionType_None;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs_ctx->encryption_type == NcaEncryptionType_Auto || fs_ctx->encryption_type > NcaEncryptionType_AesCtrExSkipLayerHash)
|
||||
{
|
||||
LOG_MSG("Invalid encryption type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", section_idx, nca_ctx->content_id_str, fs_ctx->encryption_type);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check if we're dealing with a sparse storage. */
|
||||
if (fs_ctx->has_sparse_layer)
|
||||
{
|
||||
/* Check if the sparse bucket is valid. */
|
||||
u64 raw_storage_offset = sparse_info->physical_offset;
|
||||
u64 raw_storage_size = (sparse_bucket->offset + sparse_bucket->size);
|
||||
|
||||
if (__builtin_bswap32(sparse_bucket->header.magic) != NCA_BKTR_MAGIC || sparse_bucket->header.version != NCA_BKTR_VERSION || raw_storage_offset < sizeof(NcaHeader) || \
|
||||
((raw_storage_offset + raw_storage_size) > nca_ctx->content_size))
|
||||
{
|
||||
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "Invalid SparseInfo data for FS section #%u in \"%s\" (0x%lX). Skipping FS section. SparseInfo dump:", section_idx, \
|
||||
nca_ctx->content_id_str, nca_ctx->content_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!raw_storage_size || !sparse_bucket->header.entry_count)
|
||||
{
|
||||
/* Return true but don't set this FS section as enabled, since we can't really use it. */
|
||||
LOG_MSG("Empty SparseInfo data detected for FS section #%u in \"%s\". Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||
success = true;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Set sparse table properties. */
|
||||
fs_ctx->sparse_table_offset = (sparse_info->physical_offset + sparse_bucket->offset);
|
||||
fs_ctx->sparse_table_size = sparse_bucket->size;
|
||||
|
||||
/* Check if we're within boundaries. */
|
||||
if ((fs_ctx->sparse_table_offset + fs_ctx->sparse_table_size) > nca_ctx->content_size)
|
||||
{
|
||||
LOG_DATA(sparse_info, sizeof(NcaSparseInfo), "SparseInfo table for FS section #%u in \"%s\" is out of NCA boundaries (0x%lX). Skipping FS section. SparseInfo dump:", \
|
||||
section_idx, nca_ctx->content_id_str, nca_ctx->content_size);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Update section size. */
|
||||
fs_ctx->section_size = (MIN(fs_ctx->section_offset, raw_storage_offset) + (MAX(fs_ctx->section_offset, raw_storage_offset) - \
|
||||
MIN(fs_ctx->section_offset, raw_storage_offset)) + raw_storage_size);
|
||||
}
|
||||
|
||||
/* Check if we're within boundaries. */
|
||||
if ((fs_ctx->section_offset + fs_ctx->section_size) > nca_ctx->content_size)
|
||||
{
|
||||
LOG_MSG("FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Validate HashData boundaries. */
|
||||
if (!ncaFsSectionValidateHashDataBoundaries(fs_ctx)) goto end;
|
||||
|
||||
/* Get hash layer region size (offset must always be 0). */
|
||||
fs_ctx->hash_region.offset = 0;
|
||||
if (!ncaGetFsSectionHashTargetProperties(fs_ctx, NULL, &(fs_ctx->hash_region.size)))
|
||||
{
|
||||
LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", fs_ctx->section_idx, nca_ctx->content_id_str, fs_ctx->hash_type);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check if we're within boundaries. */
|
||||
if (fs_ctx->hash_region.size > fs_ctx->section_size || (fs_ctx->section_offset + fs_ctx->hash_region.size) > nca_ctx->content_size)
|
||||
{
|
||||
LOG_MSG("Hash layer region for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Determine FS section type. */
|
||||
/* TODO: should NcaHashType_None be handled here as well? */
|
||||
switch(fs_ctx->header.fs_type)
|
||||
{
|
||||
case NcaFsType_PartitionFs:
|
||||
if ((fs_ctx->hash_type == NcaHashType_HierarchicalSha256 || fs_ctx->hash_type == NcaHashType_HierarchicalSha3256) && \
|
||||
(fs_ctx->encryption_type < NcaEncryptionType_AesCtrEx || fs_ctx->encryption_type == NcaEncryptionType_AesCtrSkipLayerHash))
|
||||
{
|
||||
/* Partition FS with None, XTS or CTR encryption. */
|
||||
fs_ctx->section_type = NcaFsSectionType_PartitionFs;
|
||||
}
|
||||
|
||||
break;
|
||||
case NcaFsType_RomFs:
|
||||
if (fs_ctx->hash_type == NcaHashType_HierarchicalIntegrity || fs_ctx->hash_type == NcaHashType_HierarchicalIntegritySha3)
|
||||
{
|
||||
if ((fs_ctx->header.patch_info.indirect_bucket.size > 0 || fs_ctx->header.patch_info.aes_ctr_ex_bucket.size > 0) && \
|
||||
(fs_ctx->encryption_type == NcaEncryptionType_None || fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx || \
|
||||
fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash))
|
||||
{
|
||||
/* Patch RomFS. */
|
||||
fs_ctx->section_type = NcaFsSectionType_PatchRomFs;
|
||||
} else
|
||||
if (!fs_ctx->header.patch_info.indirect_bucket.size && !fs_ctx->header.patch_info.aes_ctr_ex_bucket.size && \
|
||||
((fs_ctx->encryption_type >= NcaEncryptionType_None && fs_ctx->encryption_type <= NcaEncryptionType_AesCtr) || \
|
||||
fs_ctx->encryption_type == NcaEncryptionType_AesCtrSkipLayerHash))
|
||||
{
|
||||
/* Regular RomFS. */
|
||||
fs_ctx->section_type = NcaFsSectionType_RomFs;
|
||||
}
|
||||
} else
|
||||
if (nca_ctx->format_version == NcaVersion_Nca0 && fs_ctx->hash_type == NcaHashType_HierarchicalSha256)
|
||||
{
|
||||
/* NCA0 RomFS with XTS encryption. */
|
||||
fs_ctx->section_type = NcaFsSectionType_Nca0RomFs;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (fs_ctx->section_type >= NcaFsSectionType_Invalid)
|
||||
{
|
||||
LOG_DATA(&(fs_ctx->header), sizeof(NcaFsHeader), "Unable to determine section type for FS section #%u in \"%s\" (0x%02X, 0x%02X). Skipping FS section. FS header dump:", \
|
||||
section_idx, nca_ctx->content_id_str, fs_ctx->hash_type, fs_ctx->encryption_type);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check if we should skip hash layer decryption while reading this FS section. */
|
||||
fs_ctx->skip_hash_layer_crypto = (fs_ctx->encryption_type == NcaEncryptionType_AesCtrSkipLayerHash || fs_ctx->encryption_type == NcaEncryptionType_AesCtrExSkipLayerHash);
|
||||
if (fs_ctx->skip_hash_layer_crypto && fs_ctx->hash_type == NcaHashType_None)
|
||||
{
|
||||
LOG_MSG("NcaHashType_None used with SkipLayerHash crypto for FS section #%u in \"%s\". Skipping FS section.", section_idx, nca_ctx->content_id_str);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Initialize crypto data. */
|
||||
if ((!nca_ctx->rights_id_available || (nca_ctx->rights_id_available && nca_ctx->titlekey_retrieved)) && fs_ctx->encryption_type > NcaEncryptionType_None && \
|
||||
fs_ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash)
|
||||
{
|
||||
/* Initialize the partial AES counter for this section. */
|
||||
aes128CtrInitializePartialCtr(fs_ctx->ctr, fs_ctx->header.aes_ctr_upper_iv.value, fs_ctx->section_offset);
|
||||
|
||||
if (fs_ctx->has_sparse_layer)
|
||||
{
|
||||
/* Initialize the partial AES counter for the sparse info bucket table. */
|
||||
NcaAesCtrUpperIv sparse_upper_iv = {0};
|
||||
memcpy(sparse_upper_iv.value, fs_ctx->header.aes_ctr_upper_iv.value, sizeof(sparse_upper_iv.value));
|
||||
sparse_upper_iv.generation = ((u32)(sparse_info->generation) << 16);
|
||||
|
||||
aes128CtrInitializePartialCtr(fs_ctx->sparse_ctr, sparse_upper_iv.value, fs_ctx->sparse_table_offset);
|
||||
}
|
||||
|
||||
/* Initialize AES context. */
|
||||
if (nca_ctx->rights_id_available)
|
||||
{
|
||||
/* AES-128-CTR is always used for FS crypto in NCAs with a rights ID. */
|
||||
aes128CtrContextCreate(&(fs_ctx->ctr_ctx), nca_ctx->titlekey, fs_ctx->ctr);
|
||||
if (fs_ctx->has_sparse_layer) aes128CtrContextCreate(&(fs_ctx->sparse_ctr_ctx), nca_ctx->titlekey, fs_ctx->sparse_ctr);
|
||||
} else {
|
||||
if (fs_ctx->encryption_type == NcaEncryptionType_AesXts)
|
||||
{
|
||||
/* We need to create two different contexts with AES-128-XTS: one for decryption and another one for encryption. */
|
||||
aes128XtsContextCreate(&(fs_ctx->xts_decrypt_ctx), nca_ctx->decrypted_key_area.aes_xts_1, nca_ctx->decrypted_key_area.aes_xts_2, false);
|
||||
aes128XtsContextCreate(&(fs_ctx->xts_encrypt_ctx), nca_ctx->decrypted_key_area.aes_xts_1, nca_ctx->decrypted_key_area.aes_xts_2, true);
|
||||
} else
|
||||
if (fs_ctx->encryption_type >= NcaEncryptionType_AesCtr && fs_ctx->encryption_type <= NcaEncryptionType_AesCtrExSkipLayerHash)
|
||||
{
|
||||
/* Patch RomFS sections also use the AES-128-CTR key from the decrypted NCA key area, for some reason. */
|
||||
aes128CtrContextCreate(&(fs_ctx->ctr_ctx), nca_ctx->decrypted_key_area.aes_ctr, fs_ctx->ctr);
|
||||
if (fs_ctx->has_sparse_layer) aes128CtrContextCreate(&(fs_ctx->sparse_ctr_ctx), nca_ctx->decrypted_key_area.aes_ctr, fs_ctx->sparse_ctr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable FS context if we got up to this point. */
|
||||
fs_ctx->enabled = success = true;
|
||||
|
||||
end:
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool ncaFsSectionValidateHashDataBoundaries(NcaFsSectionContext *ctx)
|
||||
{
|
||||
NcaContext *nca_ctx = (NcaContext*)ctx->nca_ctx;
|
||||
bool success = false, valid = true;
|
||||
u64 accum = 0;
|
||||
|
||||
switch(ctx->hash_type)
|
||||
{
|
||||
case NcaHashType_None:
|
||||
/* Nothing to validate. */
|
||||
success = true;
|
||||
break;
|
||||
case NcaHashType_HierarchicalSha256:
|
||||
case NcaHashType_HierarchicalSha3256:
|
||||
{
|
||||
NcaHierarchicalSha256Data *hash_data = &(ctx->header.hash_data.hierarchical_sha256_data);
|
||||
if (!hash_data->hash_block_size || !hash_data->hash_region_count || hash_data->hash_region_count > NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT)
|
||||
{
|
||||
LOG_DATA(hash_data, sizeof(NcaHierarchicalSha256Data), "Invalid HierarchicalSha256 data for FS section #%u in \"%s\". Skipping FS section. Hash data dump:", \
|
||||
ctx->section_idx, nca_ctx->content_id_str);
|
||||
break;
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < hash_data->hash_region_count; i++)
|
||||
{
|
||||
/* Validate all hash regions boundaries. Skip the last one if a sparse layer is used. */
|
||||
NcaRegion *hash_region = &(hash_data->hash_region[i]);
|
||||
if (hash_region->offset != accum || !hash_region->size || \
|
||||
((i < (hash_data->hash_region_count - 1) || !ctx->has_sparse_layer) && (hash_region->offset + hash_region->size) > ctx->section_size))
|
||||
{
|
||||
LOG_MSG("HierarchicalSha256 region #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
|
||||
i, ctx->section_idx, nca_ctx->content_id_str);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
accum += hash_region->size;
|
||||
}
|
||||
|
||||
success = valid;
|
||||
}
|
||||
|
||||
break;
|
||||
case NcaHashType_HierarchicalIntegrity:
|
||||
case NcaHashType_HierarchicalIntegritySha3:
|
||||
{
|
||||
NcaIntegrityMetaInfo *hash_data = &(ctx->header.hash_data.integrity_meta_info);
|
||||
if (__builtin_bswap32(hash_data->magic) != NCA_IVFC_MAGIC || hash_data->master_hash_size != SHA256_HASH_SIZE || \
|
||||
hash_data->info_level_hash.max_level_count != NCA_IVFC_MAX_LEVEL_COUNT)
|
||||
{
|
||||
LOG_DATA(hash_data, sizeof(NcaIntegrityMetaInfo), "Invalid HierarchicalIntegrity data for FS section #%u in \"%s\". Skipping FS section. Hash data dump:", \
|
||||
ctx->section_idx, nca_ctx->content_id_str);
|
||||
break;
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
|
||||
{
|
||||
/* Validate all level informations boundaries. Skip the last one if a sparse layer is used. */
|
||||
NcaHierarchicalIntegrityVerificationLevelInformation *level_information = &(hash_data->info_level_hash.level_information[i]);
|
||||
if (level_information->offset != accum || !level_information->size || !level_information->block_order || \
|
||||
((i < (NCA_IVFC_LEVEL_COUNT - 1) || !ctx->has_sparse_layer) && (level_information->offset + level_information->size) > ctx->section_size))
|
||||
{
|
||||
LOG_MSG("HierarchicalIntegrity level #%u for FS section #%u in \"%s\" is out of NCA boundaries. Skipping FS section.", \
|
||||
i, ctx->section_idx, nca_ctx->content_id_str);
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
accum += level_information->size;
|
||||
}
|
||||
|
||||
success = valid;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOG_MSG("Invalid hash type for FS section #%u in \"%s\" (0x%02X). Skipping FS section.", ctx->section_idx, nca_ctx->content_id_str, ctx->hash_type);
|
||||
break;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset)
|
||||
{
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||
ctx->section_type >= NcaFsSectionType_Invalid || ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type > NcaEncryptionType_AesCtrExSkipLayerHash || \
|
||||
!out || !read_size || (offset + read_size) > ctx->section_size)
|
||||
{
|
||||
|
@ -895,95 +1063,44 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
|
||||
bool ret = false;
|
||||
|
||||
if (!*(nca_ctx->content_id_str) || (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || (nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
(nca_ctx->format_version != NcaVersion_Nca0 && nca_ctx->format_version != NcaVersion_Nca2 && nca_ctx->format_version != NcaVersion_Nca3) || (content_offset + read_size) > nca_ctx->content_size)
|
||||
if (!*(nca_ctx->content_id_str) || (nca_ctx->storage_id != NcmStorageId_GameCard && !nca_ctx->ncm_storage) || \
|
||||
(nca_ctx->storage_id == NcmStorageId_GameCard && !nca_ctx->gamecard_offset) || \
|
||||
(nca_ctx->format_version != NcaVersion_Nca0 && nca_ctx->format_version != NcaVersion_Nca2 && nca_ctx->format_version != NcaVersion_Nca3) || \
|
||||
(content_offset + read_size) > nca_ctx->content_size)
|
||||
{
|
||||
LOG_MSG("Invalid NCA header parameters!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Check if we're supposed to read a hash layer without encryption. */
|
||||
if (ctx->skip_hash_layer_crypto)
|
||||
if (ncaFsSectionCheckHashRegionAccess(ctx, offset, read_size, &block_size))
|
||||
{
|
||||
if ((offset + read_size) <= ctx->last_layer_offset || (ctx->last_layer_offset + ctx->last_layer_size) <= offset)
|
||||
/* Read plaintext area. Use NCA-relative offset. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, block_size, content_offset))
|
||||
{
|
||||
/* Easy route. Just read the plaintext data we need and bail out. */
|
||||
ret = ncaReadContentFile(nca_ctx, out, read_size, content_offset);
|
||||
if (!ret) LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash layer) (#1).", read_size, content_offset, \
|
||||
nca_ctx->content_id_str, ctx->section_num);
|
||||
goto end;
|
||||
} else
|
||||
if ((offset < ctx->last_layer_offset && (offset + read_size) > ctx->last_layer_offset && (offset + read_size) <= (ctx->last_layer_offset + ctx->last_layer_size)) || \
|
||||
(ctx->last_layer_offset < offset && (ctx->last_layer_offset + ctx->last_layer_size) > offset && (ctx->last_layer_offset + ctx->last_layer_size) < (offset + read_size)))
|
||||
{
|
||||
/* Handle reads that span across both plaintext hash layers and encrypted FS area. */
|
||||
bool plaintext_first = (offset < ctx->last_layer_offset);
|
||||
|
||||
/* Calculate offsets and block sizes. */
|
||||
block_start_offset = content_offset;
|
||||
block_end_offset = (ctx->section_offset + (plaintext_first ? ctx->last_layer_offset : (ctx->last_layer_offset + ctx->last_layer_size)));
|
||||
block_size = (block_end_offset - block_start_offset);
|
||||
|
||||
if (plaintext_first)
|
||||
{
|
||||
/* Read plaintext area. Use NCA-relative offset. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, block_size, block_start_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash layer) (#2).", block_size, block_start_offset, \
|
||||
nca_ctx->content_id_str, ctx->section_num);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Read encrypted area. Use FS-section-relative offset. */
|
||||
ret = _ncaReadFsSection(ctx, (u8*)out + block_size, read_size - block_size, offset + block_size);
|
||||
} else {
|
||||
/* Read encrypted area. Use FS-section-relative offset. */
|
||||
if (!_ncaReadFsSection(ctx, out, block_size, offset)) goto end;
|
||||
|
||||
/* Read plaintext area. Use NCA-relative offset. */
|
||||
ret = ncaReadContentFile(nca_ctx, (u8*)out + block_size, read_size - block_size, block_end_offset);
|
||||
if (!ret) LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash layer) (#3).", read_size - block_size, \
|
||||
block_end_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
}
|
||||
|
||||
goto end;
|
||||
} else
|
||||
if (offset < ctx->last_layer_offset && (offset + read_size) > (ctx->last_layer_offset + ctx->last_layer_size))
|
||||
{
|
||||
/* Handle plaintext hash layer + encrypted FS area + plaintext hash layer reads. */
|
||||
u8 *out_u8 = (u8*)out;
|
||||
|
||||
/* Read plaintext area. Use NCA-relative offset. */
|
||||
block_start_offset = content_offset;
|
||||
block_end_offset = (ctx->section_offset + ctx->last_layer_offset);
|
||||
block_size = (block_end_offset - block_start_offset);
|
||||
|
||||
if (!ncaReadContentFile(nca_ctx, out_u8, block_size, block_start_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash layer) (#4).", block_size, block_start_offset, \
|
||||
nca_ctx->content_id_str, ctx->section_num);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Read encrypted area. Use FS-section-relative offset. */
|
||||
out_u8 += block_size;
|
||||
block_start_offset = ctx->last_layer_offset;
|
||||
block_size = ctx->last_layer_size;
|
||||
|
||||
if (!_ncaReadFsSection(ctx, out_u8, block_size, block_start_offset)) goto end;
|
||||
|
||||
/* Read plaintext area. Use NCA-relative offset. */
|
||||
out_u8 += block_size;
|
||||
block_start_offset = (block_end_offset + block_size);
|
||||
block_end_offset = (content_offset + read_size);
|
||||
block_size = (block_end_offset - block_start_offset);
|
||||
|
||||
ret = ncaReadContentFile(nca_ctx, out_u8, block_size, block_start_offset);
|
||||
if (!ret) LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash layer) (#5).", block_size, block_start_offset, \
|
||||
nca_ctx->content_id_str, ctx->section_num);
|
||||
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash region) (#1).", block_size, content_offset, \
|
||||
nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Read remaining encrypted data, if needed. Use FS-section-relative offset. */
|
||||
ret = (read_size ? _ncaReadFsSection(ctx, (u8*)out + block_size, read_size - block_size, offset + block_size) : true);
|
||||
goto end;
|
||||
} else
|
||||
if (block_size && block_size < read_size)
|
||||
{
|
||||
/* Read encrypted area. Use FS-section-relative offset. */
|
||||
if (!_ncaReadFsSection(ctx, out, block_size, offset)) goto end;
|
||||
|
||||
/* Update parameters. */
|
||||
read_size -= block_size;
|
||||
content_offset += block_size;
|
||||
|
||||
/* Read remaining plaintext data. Use NCA-relative offset. */
|
||||
ret = ncaReadContentFile(nca_ctx, (u8*)out + block_size, read_size, content_offset);
|
||||
if (!ret) LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (plaintext hash region) (#2).", read_size, content_offset, \
|
||||
nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Optimization for reads from plaintext FS sections or reads that are aligned to the AES-CTR / AES-XTS sector size. */
|
||||
|
@ -994,7 +1111,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -1014,7 +1131,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
if (crypt_res != read_size)
|
||||
{
|
||||
LOG_MSG("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, \
|
||||
ctx->section_num);
|
||||
ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
} else
|
||||
|
@ -1042,7 +1159,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, \
|
||||
ctx->section_num);
|
||||
ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -1055,7 +1172,7 @@ static bool _ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size
|
|||
if (crypt_res != chunk_size)
|
||||
{
|
||||
LOG_MSG("Failed to AES-XTS decrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, \
|
||||
ctx->section_num);
|
||||
ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
} else
|
||||
|
@ -1075,9 +1192,38 @@ end:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool ncaFsSectionCheckHashRegionAccess(NcaFsSectionContext *ctx, u64 offset, u64 size, u64 *out_chunk_size)
|
||||
{
|
||||
if (!ctx->skip_hash_layer_crypto) return false;
|
||||
|
||||
NcaRegion *hash_region = &(ctx->hash_region);
|
||||
|
||||
/* Check if our region contains the access. */
|
||||
if (hash_region->offset <= offset)
|
||||
{
|
||||
if (offset < (hash_region->offset + hash_region->size))
|
||||
{
|
||||
if ((hash_region->offset + hash_region->size) <= (offset + size))
|
||||
{
|
||||
*out_chunk_size = ((hash_region->offset + hash_region->size) - offset);
|
||||
} else {
|
||||
*out_chunk_size = size;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (hash_region->offset <= (offset + size)) *out_chunk_size = (hash_region->offset - offset);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val)
|
||||
{
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||
ctx->section_type != NcaFsSectionType_PatchRomFs || (ctx->encryption_type != NcaEncryptionType_AesCtrEx && ctx->encryption_type != NcaEncryptionType_AesCtrExSkipLayerHash) || \
|
||||
!out || !read_size || (offset + read_size) > ctx->section_size)
|
||||
{
|
||||
|
@ -1106,7 +1252,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOG_MSG("Failed to read 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", read_size, content_offset, nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -1132,7 +1278,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
if (!ncaReadContentFile(nca_ctx, g_ncaCryptoBuffer, chunk_size, block_start_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read 0x%lX bytes encrypted data block at offset 0x%lX from NCA \"%s\" FS section #%u! (unaligned).", chunk_size, block_start_offset, nca_ctx->content_id_str, \
|
||||
ctx->section_num);
|
||||
ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -1327,7 +1473,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data,
|
|||
if (!ctx->skip_hash_layer_crypto || i == layer_count)
|
||||
{
|
||||
/* Reencrypt current layer block (if needed). */
|
||||
cur_layer_patch->data = _ncaGenerateEncryptedFsSectionBlock(ctx, cur_layer_block + cur_layer_read_patch_offset, cur_data_size, cur_layer_offset + cur_data_offset, \
|
||||
cur_layer_patch->data = ncaGenerateEncryptedFsSectionBlock(ctx, cur_layer_block + cur_layer_read_patch_offset, cur_data_size, cur_layer_offset + cur_data_offset, \
|
||||
&(cur_layer_patch->size), &(cur_layer_patch->offset));
|
||||
if (!cur_layer_patch->data)
|
||||
{
|
||||
|
@ -1366,7 +1512,7 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data,
|
|||
}
|
||||
|
||||
/* Recalculate FS header hash. */
|
||||
sha256CalculateHash(nca_ctx->header.fs_header_hash[ctx->section_num].hash, &(ctx->header), sizeof(NcaFsHeader));
|
||||
sha256CalculateHash(nca_ctx->header.fs_header_hash[ctx->section_idx].hash, &(ctx->header), sizeof(NcaFsHeader));
|
||||
|
||||
/* Copy content ID. */
|
||||
memcpy(!is_integrity_patch ? &(hierarchical_sha256_patch->content_id) : &(hierarchical_integrity_patch->content_id), &(nca_ctx->content_id), sizeof(NcmContentId));
|
||||
|
@ -1417,12 +1563,17 @@ static bool ncaWritePatchToMemoryBuffer(NcaContext *ctx, const void *patch, u64
|
|||
return ((patch_block_offset + buf_block_size) == patch_size);
|
||||
}
|
||||
|
||||
static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset)
|
||||
/// Returns a pointer to a dynamically 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 seamlessly replace data while dumping a NCA.
|
||||
/// This function doesn't support Patch RomFS sections, nor sections with Sparse and/or Compressed storage.
|
||||
static void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, u64 *out_block_size, u64 *out_block_offset)
|
||||
{
|
||||
u8 *out = NULL;
|
||||
bool success = false;
|
||||
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || ctx->has_sparse_layer || !ctx->nca_ctx || ctx->section_num >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||
if (!g_ncaCryptoBuffer || !ctx || !ctx->enabled || ctx->has_sparse_layer || !ctx->nca_ctx || ctx->section_idx >= NCA_FS_HEADER_COUNT || ctx->section_offset < sizeof(NcaHeader) || \
|
||||
ctx->hash_type <= NcaHashType_None || ctx->hash_type == NcaHashType_AutoSha3 || ctx->hash_type > NcaHashType_HierarchicalIntegritySha3 || \
|
||||
ctx->encryption_type == NcaEncryptionType_Auto || ctx->encryption_type == NcaEncryptionType_AesCtrEx || ctx->encryption_type >= NcaEncryptionType_AesCtrExSkipLayerHash || \
|
||||
ctx->section_type >= NcaFsSectionType_Invalid || !data || !data_size || (data_offset + data_size) > ctx->section_size || !out_block_size || !out_block_offset)
|
||||
|
@ -1471,7 +1622,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_encrypt_ctx), out, out, data_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != data_size)
|
||||
{
|
||||
LOG_MSG("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", data_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOG_MSG("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", data_size, content_offset, nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
} else
|
||||
|
@ -1508,7 +1659,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
/* Read decrypted data using aligned offset and size. */
|
||||
if (!_ncaReadFsSection(ctx, out, block_size, block_start_offset))
|
||||
{
|
||||
LOG_MSG("Failed to read decrypted NCA \"%s\" FS section #%u data block!", nca_ctx->content_id_str, ctx->section_num);
|
||||
LOG_MSG("Failed to read decrypted NCA \"%s\" FS section #%u data block!", nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
@ -1523,7 +1674,7 @@ static void *_ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const
|
|||
crypt_res = aes128XtsNintendoCrypt(&(ctx->xts_encrypt_ctx), out, out, block_size, sector_num, NCA_AES_XTS_SECTOR_SIZE, true);
|
||||
if (crypt_res != block_size)
|
||||
{
|
||||
LOG_MSG("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", block_size, content_offset, nca_ctx->content_id_str, ctx->section_num);
|
||||
LOG_MSG("Failed to AES-XTS encrypt 0x%lX bytes data block at offset 0x%lX from NCA \"%s\" FS section #%u! (aligned).", block_size, content_offset, nca_ctx->content_id_str, ctx->section_idx);
|
||||
goto end;
|
||||
}
|
||||
} else
|
||||
|
|
|
@ -33,13 +33,11 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
PartitionFileSystemHeader pfs_header = {0};
|
||||
PartitionFileSystemEntry *main_npdm_entry = NULL;
|
||||
|
||||
u32 hash_region_count = 0;
|
||||
NcaRegion *hash_region = NULL;
|
||||
|
||||
bool success = false, dump_fs_header = false;
|
||||
|
||||
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || nca_fs_ctx->header.fs_type != NcaFsType_PartitionFs || \
|
||||
nca_fs_ctx->header.hash_type != NcaHashType_HierarchicalSha256 || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || (nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved))
|
||||
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || \
|
||||
(nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256 && nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha3256) || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || \
|
||||
(nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -51,18 +49,12 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
/* Fill context. */
|
||||
out->nca_fs_ctx = nca_fs_ctx;
|
||||
|
||||
if (!nca_fs_ctx->has_sparse_layer && !ncaValidateHierarchicalSha256Offsets(&(nca_fs_ctx->header.hash_data.hierarchical_sha256_data), nca_fs_ctx->section_size))
|
||||
if (!ncaGetFsSectionHashTargetProperties(nca_fs_ctx, &(out->offset), &(out->size)))
|
||||
{
|
||||
LOG_MSG("Invalid HierarchicalSha256 block!");
|
||||
LOG_MSG("Failed to get target hash layer properties!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
hash_region_count = nca_fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
||||
hash_region = &(nca_fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region[hash_region_count - 1]);
|
||||
|
||||
out->offset = hash_region->offset;
|
||||
out->size = hash_region->size;
|
||||
|
||||
/* Read partial Partition FS header. */
|
||||
if (!ncaReadFsSection(nca_fs_ctx, &pfs_header, sizeof(PartitionFileSystemHeader), out->offset))
|
||||
{
|
||||
|
|
|
@ -33,49 +33,26 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
|
|||
u64 dir_table_offset = 0, file_table_offset = 0;
|
||||
bool success = false, dump_fs_header = false;
|
||||
|
||||
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || (nca_ctx->format_version == NcaVersion_Nca0 && \
|
||||
(nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || nca_fs_ctx->header.hash_type != NcaHashType_HierarchicalSha256)) || (nca_ctx->format_version != NcaVersion_Nca0 && \
|
||||
(nca_fs_ctx->section_type != NcaFsSectionType_RomFs || nca_fs_ctx->header.hash_type != NcaHashType_HierarchicalIntegrity)) || (nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved))
|
||||
if (!out || !nca_fs_ctx || !nca_fs_ctx->enabled || !(nca_ctx = (NcaContext*)nca_fs_ctx->nca_ctx) || \
|
||||
(nca_ctx->format_version == NcaVersion_Nca0 && (nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs || nca_fs_ctx->hash_type != NcaHashType_HierarchicalSha256)) || \
|
||||
(nca_ctx->format_version != NcaVersion_Nca0 && (nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
|
||||
(nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegrity && nca_fs_ctx->hash_type != NcaHashType_HierarchicalIntegritySha3))) || \
|
||||
(nca_ctx->rights_id_available && !nca_ctx->titlekey_retrieved))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 layer_count = 0;
|
||||
NcaRegion *hash_region = NULL;
|
||||
NcaHierarchicalIntegrityVerificationLevelInformation *level_information = NULL;
|
||||
|
||||
/* Free output context beforehand. */
|
||||
romfsFreeContext(out);
|
||||
|
||||
/* Fill context. */
|
||||
out->nca_fs_ctx = nca_fs_ctx;
|
||||
|
||||
if (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
||||
if (!ncaGetFsSectionHashTargetProperties(nca_fs_ctx, &(out->offset), &(out->size)))
|
||||
{
|
||||
if (!ncaValidateHierarchicalSha256Offsets(&(nca_fs_ctx->header.hash_data.hierarchical_sha256_data), nca_fs_ctx->section_size))
|
||||
{
|
||||
LOG_MSG("Invalid HierarchicalSha256 block!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
layer_count = nca_fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region_count;
|
||||
hash_region = &(nca_fs_ctx->header.hash_data.hierarchical_sha256_data.hash_region[layer_count - 1]);
|
||||
|
||||
out->offset = hash_region->offset;
|
||||
out->size = hash_region->size;
|
||||
} else {
|
||||
if (!nca_fs_ctx->has_sparse_layer && !ncaValidateHierarchicalIntegrityOffsets(&(nca_fs_ctx->header.hash_data.integrity_meta_info), nca_fs_ctx->section_size))
|
||||
{
|
||||
LOG_MSG("Invalid HierarchicalIntegrity block!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
layer_count = NCA_IVFC_LEVEL_COUNT;
|
||||
level_information = &(nca_fs_ctx->header.hash_data.integrity_meta_info.info_level_hash.level_information[layer_count - 1]);
|
||||
|
||||
out->offset = level_information->offset;
|
||||
out->size = level_information->size;
|
||||
LOG_MSG("Failed to get target hash layer properties!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Read RomFS header. */
|
||||
|
|
|
@ -78,15 +78,6 @@ static void sha3ContextCreate(Sha3Context *out, u32 hash_size);
|
|||
static void sha3ProcessBlock(Sha3Context *ctx);
|
||||
static void sha3ProcessLastBlock(Sha3Context *ctx);
|
||||
|
||||
/* Functions for SHA3 context creation and simple all-in-one calculation. */
|
||||
|
||||
_SHA3_CTX_OPS(224);
|
||||
_SHA3_CTX_OPS(256);
|
||||
_SHA3_CTX_OPS(384);
|
||||
_SHA3_CTX_OPS(512);
|
||||
|
||||
#undef _SHA3_CTX_OPS
|
||||
|
||||
void sha3ContextUpdate(Sha3Context *ctx, const void *src, size_t size)
|
||||
{
|
||||
if (!ctx || !src || !size || ctx->finalized)
|
||||
|
@ -162,6 +153,15 @@ void sha3ContextGetHash(Sha3Context *ctx, void *dst)
|
|||
memcpy(dst, ctx->internal_state, ctx->hash_size);
|
||||
}
|
||||
|
||||
/* Functions for SHA3 context creation and simple all-in-one calculation. */
|
||||
|
||||
_SHA3_CTX_OPS(224);
|
||||
_SHA3_CTX_OPS(256);
|
||||
_SHA3_CTX_OPS(384);
|
||||
_SHA3_CTX_OPS(512);
|
||||
|
||||
#undef _SHA3_CTX_OPS
|
||||
|
||||
static u64 rotl_u64(u64 x, int s)
|
||||
{
|
||||
int N = (sizeof(u64) * 8);
|
||||
|
|
Loading…
Reference in a new issue