mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 10:16:39 +00:00
BKTR rewrite part 6: bktrReadAesCtrExStorage().
This commit is contained in:
parent
ee9949179d
commit
54046994a6
3 changed files with 117 additions and 22 deletions
|
@ -498,9 +498,9 @@ bool ncaGetFsSectionHashTargetProperties(NcaFsSectionContext *ctx, u64 *out_offs
|
|||
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR AesCtrEx storage data. Use ncaReadAesCtrExStorageFromBktrSection() for that.
|
||||
bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset);
|
||||
|
||||
/// Reads decrypted BKTR AesCtrEx storage data from a NCA Patch RomFS section using an input context and an AesCtrEx CTR value.
|
||||
/// Reads plaintext AesCtrEx storage data from a NCA Patch RomFS section using an input context and an AesCtrEx CTR value.
|
||||
/// 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);
|
||||
bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool decrypt);
|
||||
|
||||
/// 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).
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef struct {
|
|||
u64 size;
|
||||
u64 virtual_offset;
|
||||
u32 ctr_val;
|
||||
bool aes_ctr_ex_crypt;
|
||||
u8 parent_storage_type; ///< BucketTreeStorageType.
|
||||
} BucketTreeSubStorageReadParams;
|
||||
|
||||
|
@ -84,7 +85,7 @@ static bool bktrInitializeCompressedStorageContext(BucketTreeContext *out, NcaFs
|
|||
static bool bktrReadCompressedStorage(BucketTreeVisitor *visitor, void *out, u64 read_size, u64 offset);
|
||||
|
||||
static bool bktrReadSubStorage(BucketTreeSubStorage *substorage, BucketTreeSubStorageReadParams *params);
|
||||
NX_INLINE void bktrBucketInitializeSubStorageReadParams(BucketTreeSubStorageReadParams *out, void *buffer, u64 offset, u64 size, u64 virtual_offset, u32 ctr_val, u8 parent_storage_type);
|
||||
NX_INLINE void bktrBucketInitializeSubStorageReadParams(BucketTreeSubStorageReadParams *out, void *buffer, u64 offset, u64 size, u64 virtual_offset, u32 ctr_val, bool aes_ctr_ex_crypt, u8 parent_storage_type);
|
||||
|
||||
static bool bktrVerifyBucketInfo(NcaBucketInfo *bucket, u64 node_size, u64 entry_size, u64 *out_node_storage_size, u64 *out_entry_storage_size);
|
||||
static bool bktrValidateTableOffsetNode(const BucketTreeTable *table, u64 node_size, u64 entry_size, u32 entry_count, u64 *out_start_offset, u64 *out_end_offset);
|
||||
|
@ -436,8 +437,8 @@ static bool bktrReadIndirectStorage(BucketTreeVisitor *visitor, void *out, u64 r
|
|||
bool is_sparse = (ctx->storage_type == BucketTreeStorageType_Sparse);
|
||||
|
||||
if (!out || !bktrIsValidSubstorage(&(ctx->substorages[0])) || (!is_sparse && !bktrIsValidSubstorage(&(ctx->substorages[1]))) || \
|
||||
(!is_sparse && ctx->substorages[0].type != BucketTreeSubStorageType_Regular && ctx->substorages[0].type != BucketTreeStorageType_Compressed) || \
|
||||
(is_sparse && ctx->substorages[0].type != BucketTreeSubStorageType_Regular) || (!is_sparse && ctx->substorages[1].type != BucketTreeSubStorageType_AesCtrEx))
|
||||
(!is_sparse && ((ctx->substorages[0].type != BucketTreeSubStorageType_Regular && ctx->substorages[0].type != BucketTreeStorageType_Compressed) || ctx->substorages[1].type != BucketTreeSubStorageType_AesCtrEx)) || \
|
||||
(is_sparse && ctx->substorages[0].type != BucketTreeSubStorageType_Regular))
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -499,14 +500,13 @@ static bool bktrReadIndirectStorage(BucketTreeVisitor *visitor, void *out, u64 r
|
|||
}
|
||||
|
||||
/* Perform read operation. */
|
||||
BucketTreeSubStorageReadParams params = {0};
|
||||
const u64 data_offset = (offset - cur_entry_offset + cur_entry.physical_offset);
|
||||
|
||||
bktrBucketInitializeSubStorageReadParams(¶ms, out, data_offset, read_size, offset, 0, ctx->storage_type);
|
||||
|
||||
if ((offset + read_size) <= next_entry_offset)
|
||||
{
|
||||
/* Read only within the current indirect storage entry. */
|
||||
BucketTreeSubStorageReadParams params = {0};
|
||||
const u64 data_offset = (offset - cur_entry_offset + cur_entry.physical_offset);
|
||||
bktrBucketInitializeSubStorageReadParams(¶ms, out, data_offset, read_size, offset, 0, false, ctx->storage_type);
|
||||
|
||||
if (cur_entry.storage_index == BucketTreeIndirectStorageIndex_Original)
|
||||
{
|
||||
/* Retrieve data from the original data storage. */
|
||||
|
@ -608,6 +608,97 @@ end:
|
|||
return success;
|
||||
}
|
||||
|
||||
static bool bktrReadAesCtrExStorage(BucketTreeVisitor *visitor, void *out, u64 read_size, u64 offset)
|
||||
{
|
||||
BucketTreeContext *ctx = visitor->bktr_ctx;
|
||||
NcaFsSectionContext *nca_fs_ctx = ctx->nca_fs_ctx;
|
||||
|
||||
if (!out || !bktrIsValidSubstorage(&(ctx->substorages[0])) || ctx->substorages[0].type != BucketTreeSubStorageType_Regular)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Validate AesCtrEx Storage entry. */
|
||||
BucketTreeAesCtrExStorageEntry cur_entry = {0};
|
||||
memcpy(&cur_entry, visitor->entry, sizeof(BucketTreeAesCtrExStorageEntry));
|
||||
|
||||
if (!bktrIsOffsetWithinStorageRange(ctx, cur_entry.offset) || cur_entry.offset > offset || !IS_ALIGNED(cur_entry.offset, AES_BLOCK_SIZE))
|
||||
{
|
||||
LOG_MSG("Invalid AesCtrEx Storage entry! (0x%lX) (#1).", cur_entry.offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 cur_entry_offset = cur_entry.offset, next_entry_offset = 0;
|
||||
bool moved = false, success = false;
|
||||
|
||||
/* Check if we can retrieve the next entry. */
|
||||
if (bktrVisitorCanMoveNext(visitor))
|
||||
{
|
||||
/* Retrieve the next entry. */
|
||||
if (!bktrVisitorMoveNext(visitor))
|
||||
{
|
||||
LOG_MSG("Failed to retrieve next AesCtrEx Storage entry!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Validate AesCtrEx Storage entry. */
|
||||
BucketTreeAesCtrExStorageEntry *next_entry = (BucketTreeAesCtrExStorageEntry*)visitor->entry;
|
||||
if (!bktrIsOffsetWithinStorageRange(ctx, next_entry->offset))
|
||||
{
|
||||
LOG_MSG("Invalid AesCtrEx Storage entry! (0x%lX) (#2).", next_entry->offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Store next entry's virtual offset. */
|
||||
next_entry_offset = next_entry->offset;
|
||||
|
||||
/* Update variable. */
|
||||
moved = true;
|
||||
} else {
|
||||
/* Set the next entry offset to the storage's end. */
|
||||
next_entry_offset = ctx->end_offset;
|
||||
}
|
||||
|
||||
/* Verify next entry offset. */
|
||||
if (!IS_ALIGNED(next_entry_offset, AES_BLOCK_SIZE) || next_entry_offset <= cur_entry_offset || offset >= next_entry_offset)
|
||||
{
|
||||
LOG_MSG("Invalid offset for the AesCtrEx Storage's next entry! (0x%lX).", next_entry_offset);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Verify read area size. */
|
||||
if ((offset + read_size) > ctx->end_offset)
|
||||
{
|
||||
LOG_MSG("Error: read area exceeds AesCtrEx Storage size!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Perform read operation. */
|
||||
if ((offset + read_size) <= next_entry_offset)
|
||||
{
|
||||
/* Read only within the current AesCtrEx storage entry. */
|
||||
BucketTreeSubStorageReadParams params = {0};
|
||||
bktrBucketInitializeSubStorageReadParams(¶ms, out, offset, read_size, 0, cur_entry.generation, cur_entry.encryption == BucketTreeAesCtrExStorageEncryption_Enabled, ctx->storage_type);
|
||||
|
||||
success = bktrReadSubStorage(&(ctx->substorages[0]), ¶ms);
|
||||
if (!success) LOG_MSG("Failed to read 0x%lX-byte long chunk at offset 0x%lX from AesCtrEx storage!", read_size, offset);
|
||||
} else {
|
||||
/* Handle reads that span multiple AesCtrEx storage entries. */
|
||||
if (moved) bktrVisitorMovePrevious(visitor);
|
||||
|
||||
const u64 aes_ctr_ex_block_size = (next_entry_offset - offset);
|
||||
|
||||
success = (bktrReadAesCtrExStorage(visitor, out, aes_ctr_ex_block_size, offset) && \
|
||||
bktrReadAesCtrExStorage(visitor, (u8*)out + aes_ctr_ex_block_size, read_size - aes_ctr_ex_block_size, offset + aes_ctr_ex_block_size));
|
||||
|
||||
if (!success) LOG_MSG("Failed to read 0x%lX bytes block from multiple AesCtrEx Storage entries at offset 0x%lX!", read_size, offset);
|
||||
}
|
||||
|
||||
end:
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool bktrInitializeCompressedStorageContext(BucketTreeContext *out, NcaFsSectionContext *nca_fs_ctx)
|
||||
{
|
||||
if (!nca_fs_ctx->has_compression_layer)
|
||||
|
@ -691,7 +782,7 @@ static bool bktrReadSubStorage(BucketTreeSubStorage *substorage, BucketTreeSubSt
|
|||
if (params->parent_storage_type == BucketTreeStorageType_AesCtrEx)
|
||||
{
|
||||
/* Perform a read on the target NCA using AesCtrEx crypto. */
|
||||
success = ncaReadAesCtrExStorageFromBktrSection(nca_fs_ctx, params->buffer, params->size, params->offset, params->ctr_val);
|
||||
success = ncaReadAesCtrExStorageFromBktrSection(nca_fs_ctx, params->buffer, params->size, params->offset, params->ctr_val, params->aes_ctr_ex_crypt);
|
||||
} else {
|
||||
/* Make sure to handle Sparse virtual offsets if we need to. */
|
||||
if (params->parent_storage_type == BucketTreeStorageType_Sparse && params->virtual_offset) nca_fs_ctx->cur_sparse_virtual_offset = params->virtual_offset;
|
||||
|
@ -709,13 +800,14 @@ static bool bktrReadSubStorage(BucketTreeSubStorage *substorage, BucketTreeSubSt
|
|||
return success;
|
||||
}
|
||||
|
||||
NX_INLINE void bktrBucketInitializeSubStorageReadParams(BucketTreeSubStorageReadParams *out, void *buffer, u64 offset, u64 size, u64 virtual_offset, u32 ctr_val, u8 parent_storage_type)
|
||||
NX_INLINE void bktrBucketInitializeSubStorageReadParams(BucketTreeSubStorageReadParams *out, void *buffer, u64 offset, u64 size, u64 virtual_offset, u32 ctr_val, bool aes_ctr_ex_crypt, u8 parent_storage_type)
|
||||
{
|
||||
out->buffer = buffer;
|
||||
out->offset = offset;
|
||||
out->size = size;
|
||||
out->virtual_offset = ((virtual_offset && parent_storage_type == BucketTreeStorageType_Sparse) ? virtual_offset : 0);
|
||||
out->ctr_val = ((ctr_val && parent_storage_type == BucketTreeStorageType_AesCtrEx) ? ctr_val : 0);
|
||||
out->aes_ctr_ex_crypt = ((aes_ctr_ex_crypt && parent_storage_type == BucketTreeStorageType_AesCtrEx) true : false);
|
||||
out->parent_storage_type = parent_storage_type;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ 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 bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool decrypt);
|
||||
|
||||
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);
|
||||
|
@ -257,10 +257,10 @@ bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 of
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val)
|
||||
bool ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool decrypt)
|
||||
{
|
||||
bool ret = false;
|
||||
SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = _ncaReadAesCtrExStorageFromBktrSection(ctx, out, read_size, offset, ctr_val);
|
||||
SCOPED_LOCK(&g_ncaCryptoBufferMutex) ret = _ncaReadAesCtrExStorageFromBktrSection(ctx, out, read_size, offset, ctr_val, decrypt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1273,7 +1273,7 @@ static bool ncaFsSectionCheckHashRegionAccess(NcaFsSectionContext *ctx, u64 offs
|
|||
}
|
||||
}
|
||||
|
||||
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val)
|
||||
static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool decrypt)
|
||||
{
|
||||
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_None && ctx->encryption_type != NcaEncryptionType_AesCtrEx && \
|
||||
|
@ -1299,7 +1299,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
}
|
||||
|
||||
/* Optimization for reads that are aligned to the AES-CTR sector size. */
|
||||
if (!(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE))
|
||||
if (!decrypt || (!(content_offset % AES_BLOCK_SIZE) && !(read_size % AES_BLOCK_SIZE)))
|
||||
{
|
||||
/* Read data. */
|
||||
if (!ncaReadContentFile(nca_ctx, out, read_size, content_offset))
|
||||
|
@ -1308,10 +1308,13 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Decrypt data */
|
||||
aes128CtrUpdatePartialCtrEx(ctx->ctr, ctr_val, content_offset);
|
||||
aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr);
|
||||
aes128CtrCrypt(&(ctx->ctr_ctx), out, out, read_size);
|
||||
/* Decrypt data, if needed. */
|
||||
if (decrypt)
|
||||
{
|
||||
aes128CtrUpdatePartialCtrEx(ctx->ctr, ctr_val, content_offset);
|
||||
aes128CtrContextResetCtr(&(ctx->ctr_ctx), ctx->ctr);
|
||||
aes128CtrCrypt(&(ctx->ctr_ctx), out, out, read_size);
|
||||
}
|
||||
|
||||
ret = true;
|
||||
goto end;
|
||||
|
@ -1342,7 +1345,7 @@ static bool _ncaReadAesCtrExStorageFromBktrSection(NcaFsSectionContext *ctx, voi
|
|||
/* Copy decrypted data. */
|
||||
memcpy(out, g_ncaCryptoBuffer + data_start_offset, out_chunk_size);
|
||||
|
||||
ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val) : true);
|
||||
ret = (block_size > NCA_CRYPTO_BUFFER_SIZE ? _ncaReadAesCtrExStorageFromBktrSection(ctx, (u8*)out + out_chunk_size, read_size - out_chunk_size, offset + out_chunk_size, ctr_val, decrypt) : true);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
|
|
Loading…
Reference in a new issue