mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 10:16:39 +00:00
Fix mem leaks in PFS/RomFS initializers.
Also renamed Ecsda240 -> Ecc480 and added a new Hmac160 signature type.
This commit is contained in:
parent
7e8fa96829
commit
b64bcf5451
13 changed files with 280 additions and 598 deletions
|
@ -12,6 +12,8 @@ todo:
|
|||
romfs: filelist generation methods
|
||||
romfs: patch writing function
|
||||
|
||||
bktr: filelist generation methods
|
||||
|
||||
Result txIsFat32(bool *mode) {
|
||||
Result rc = serviceDispatch(&g_tx, 137);
|
||||
if (rc == 0xa08) {
|
||||
|
|
516
source/bktr.c
516
source/bktr.c
|
@ -18,520 +18,62 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "romfs.h"
|
||||
#include "bktr.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* Function prototypes. */
|
||||
|
||||
static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name);
|
||||
static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name);
|
||||
|
||||
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx)
|
||||
|
||||
|
||||
bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ctx, NcaFsSectionContext *update_nca_fs_ctx)
|
||||
{
|
||||
NcaContext *nca_ctx = NULL;
|
||||
u64 dir_table_offset = 0, file_table_offset = 0;
|
||||
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
|
||||
|
||||
if (!out || !nca_fs_ctx || nca_fs_ctx->section_type != NcaFsSectionType_RomFs || !nca_fs_ctx->header || !(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)))
|
||||
if (!out || !base_nca_fs_ctx || !(base_nca_ctx = (NcaContext*)base_nca_fs_ctx->nca_ctx) || base_nca_fs_ctx->section_type != NcaFsSectionType_RomFs || \
|
||||
base_nca_fs_ctx->encryption_type == NcaEncryptionType_AesCtrEx || !update_nca_fs_ctx || !(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 ||
|
||||
base_nca_ctx->header.program_id != update_nca_ctx->header.program_id)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize base NCA RomFS context */
|
||||
if (!romfsInitializeContext(&(out->base_romfs_ctx), base_nca_fs_ctx))
|
||||
{
|
||||
LOGFILE("Failed to initialize base NCA RomFS context!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize update NCA RomFS context */
|
||||
if (!romfsInitializeContext(&(out->patch_romfs_ctx), update_nca_fs_ctx))
|
||||
{
|
||||
LOGFILE("Failed to initialize update NCA RomFS context!");
|
||||
romfsFreeContext(&(out->base_romfs_ctx));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Fill context */
|
||||
out->nca_fs_ctx = nca_fs_ctx;
|
||||
out->offset = 0;
|
||||
out->size = 0;
|
||||
out->dir_table_size = 0;
|
||||
out->dir_table = NULL;
|
||||
out->file_table_size = 0;
|
||||
out->file_table = NULL;
|
||||
out->body_offset = 0;
|
||||
|
||||
if (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
||||
{
|
||||
if (!ncaValidateHierarchicalSha256Offsets(&(nca_fs_ctx->header->hash_info.hierarchical_sha256), nca_fs_ctx->section_size))
|
||||
{
|
||||
LOGFILE("Invalid HierarchicalSha256 block!");
|
||||
return false;
|
||||
}
|
||||
|
||||
out->offset = nca_fs_ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.offset;
|
||||
out->size = nca_fs_ctx->header->hash_info.hierarchical_sha256.hash_target_layer_info.size;
|
||||
} else {
|
||||
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image */
|
||||
if (nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && !ncaValidateHierarchicalIntegrityOffsets(&(nca_fs_ctx->header->hash_info.hierarchical_integrity), nca_fs_ctx->section_size))
|
||||
{
|
||||
LOGFILE("Invalid HierarchicalIntegrity block!");
|
||||
return false;
|
||||
}
|
||||
|
||||
out->offset = nca_fs_ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.offset;
|
||||
out->size = nca_fs_ctx->header->hash_info.hierarchical_integrity.hash_target_layer_info.size;
|
||||
}
|
||||
|
||||
/* Read RomFS header */
|
||||
if (!ncaReadFsSection(nca_fs_ctx, &(out->header), sizeof(RomFileSystemHeader), out->offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS header!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs && out->header.old_format.header_size != ROMFS_OLD_HEADER_SIZE) || \
|
||||
(nca_fs_ctx->section_type == NcaFsSectionType_RomFs && out->header.cur_format.header_size != ROMFS_HEADER_SIZE))
|
||||
{
|
||||
LOGFILE("Invalid RomFS header size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read directory entries table */
|
||||
dir_table_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.directory_entry_offset : out->header.cur_format.directory_entry_offset);
|
||||
out->dir_table_size = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.directory_entry_size : out->header.cur_format.directory_entry_size);
|
||||
|
||||
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image */
|
||||
if (!out->dir_table_size || (nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && (dir_table_offset >= out->size || (dir_table_offset + out->dir_table_size) > out->size)))
|
||||
{
|
||||
LOGFILE("Invalid RomFS directory entries table!");
|
||||
return false;
|
||||
}
|
||||
|
||||
out->dir_table = malloc(out->dir_table_size);
|
||||
if (!out->dir_table)
|
||||
{
|
||||
LOGFILE("Unable to allocate memory for RomFS directory entries table!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ncaReadFsSection(nca_fs_ctx, out->dir_table, out->dir_table_size, out->offset + dir_table_offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS directory entries table!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read file entries table */
|
||||
file_table_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.file_entry_offset : out->header.cur_format.file_entry_offset);
|
||||
out->file_table_size = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.file_entry_size : out->header.cur_format.file_entry_size);
|
||||
|
||||
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image */
|
||||
if (!out->file_table_size || (nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && (file_table_offset >= out->size || (file_table_offset + out->file_table_size) > out->size)))
|
||||
{
|
||||
LOGFILE("Invalid RomFS file entries table!");
|
||||
return false;
|
||||
}
|
||||
|
||||
out->file_table = malloc(out->file_table_size);
|
||||
if (!out->file_table)
|
||||
{
|
||||
LOGFILE("Unable to allocate memory for RomFS file entries table!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ncaReadFsSection(nca_fs_ctx, out->file_table, out->file_table_size, out->offset + file_table_offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS file entries table!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get file data body offset */
|
||||
/* Don't verify offsets from Patch RomFS sections, because they reflect the full, patched RomFS image */
|
||||
out->body_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.body_offset : out->header.cur_format.body_offset);
|
||||
if (nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && out->body_offset >= out->size)
|
||||
{
|
||||
LOGFILE("Invalid RomFS file data body!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset)
|
||||
{
|
||||
if (!ctx || !ctx->nca_fs_ctx || !ctx->size || !out || !read_size || offset >= ctx->size || (offset + read_size) > ctx->size)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read filesystem data */
|
||||
if (!ncaReadFsSection(ctx->nca_fs_ctx, out, read_size, ctx->offset + offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS data!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset)
|
||||
{
|
||||
if (!ctx || !ctx->body_offset || !file_entry || !file_entry->size || file_entry->offset >= ctx->size || (file_entry->offset + file_entry->size) > ctx->size || \
|
||||
!out || !read_size || offset >= file_entry->size || (offset + read_size) > file_entry->size)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read entry data */
|
||||
if (!romfsReadFileSystemData(ctx, out, read_size, ctx->body_offset + file_entry->offset + offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS file entry data!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size)
|
||||
{
|
||||
if (!ctx || !ctx->file_table_size || !ctx->file_table || !out_size)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 offset = 0, total_size = 0;
|
||||
RomFileSystemFileEntry *file_entry = NULL;
|
||||
|
||||
while(offset < ctx->file_table_size)
|
||||
{
|
||||
if (!(file_entry = romfsGetFileEntryByOffset(ctx, offset)))
|
||||
{
|
||||
LOGFILE("Failed to retrieve file entry!");
|
||||
return false;
|
||||
}
|
||||
|
||||
total_size += file_entry->size;
|
||||
offset += ALIGN_UP(sizeof(RomFileSystemFileEntry) + file_entry->name_length, 4);
|
||||
}
|
||||
|
||||
*out_size = total_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size)
|
||||
{
|
||||
if (!ctx || !ctx->dir_table_size || !ctx->dir_table || !ctx->file_table_size || !ctx->file_table || !dir_entry || (dir_entry->file_offset == ROMFS_VOID_ENTRY && \
|
||||
dir_entry->directory_offset == ROMFS_VOID_ENTRY) || !out_size)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 total_size = 0, child_dir_size = 0;
|
||||
u32 cur_file_offset = 0, cur_dir_offset = 0;
|
||||
RomFileSystemFileEntry *cur_file_entry = NULL;
|
||||
RomFileSystemDirectoryEntry *cur_dir_entry = NULL;
|
||||
|
||||
cur_file_offset = dir_entry->file_offset;
|
||||
while(cur_file_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(cur_file_entry = romfsGetFileEntryByOffset(ctx, cur_file_offset)))
|
||||
{
|
||||
LOGFILE("Failed to retrieve file entry!");
|
||||
return false;
|
||||
}
|
||||
|
||||
total_size += cur_file_entry->size;
|
||||
cur_file_offset = cur_file_entry->next_offset;
|
||||
}
|
||||
|
||||
cur_dir_offset = dir_entry->directory_offset;
|
||||
while(cur_dir_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(cur_dir_entry = romfsGetDirectoryEntryByOffset(ctx, cur_dir_offset)) || !romfsGetDirectoryDataSize(ctx, cur_dir_entry, &child_dir_size))
|
||||
{
|
||||
LOGFILE("Failed to retrieve directory entry/size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
total_size += child_dir_size;
|
||||
cur_dir_offset = cur_dir_entry->next_offset;
|
||||
}
|
||||
|
||||
*out_size = total_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path)
|
||||
{
|
||||
size_t path_len = 0;
|
||||
char *path_dup = NULL, *pch = NULL;
|
||||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !path || *path != '/' || !(path_len = strlen(path)) || !(dir_entry = romfsGetDirectoryEntryByOffset(ctx, 0)))
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if the root directory was requested */
|
||||
if (path_len == 1) return dir_entry;
|
||||
|
||||
/* Duplicate path to avoid problems with strtok() */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOGFILE("Unable to duplicate input path!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pch = strtok(path_dup, "/");
|
||||
if (!pch)
|
||||
{
|
||||
LOGFILE("Failed to tokenize input path!");
|
||||
dir_entry = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while(pch)
|
||||
{
|
||||
if (!(dir_entry = romfsGetChildDirectoryEntryByName(ctx, dir_entry, pch)))
|
||||
{
|
||||
LOGFILE("Failed to retrieve directory entry by name!");
|
||||
break;
|
||||
}
|
||||
|
||||
pch = strtok(NULL, "/");
|
||||
}
|
||||
|
||||
out:
|
||||
if (path_dup) free(path_dup);
|
||||
|
||||
return dir_entry;
|
||||
}
|
||||
|
||||
RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const char *path)
|
||||
{
|
||||
size_t path_len = 0;
|
||||
char *path_dup = NULL, *filename = NULL;
|
||||
RomFileSystemFileEntry *file_entry = NULL;
|
||||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->file_table || !ctx->file_table_size || !path || *path != '/' || (path_len = strlen(path)) <= 1)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Duplicate path */
|
||||
if (!(path_dup = strdup(path)))
|
||||
{
|
||||
LOGFILE("Unable to duplicate input path!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove any trailing slashes */
|
||||
while(path_dup[path_len - 1] == '/')
|
||||
{
|
||||
path_dup[path_len - 1] = '\0';
|
||||
path_len--;
|
||||
}
|
||||
|
||||
/* Safety check */
|
||||
if (!path_len || !(filename = strrchr(path_dup, '/')))
|
||||
{
|
||||
LOGFILE("Invalid input path!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Remove leading slash and adjust filename string pointer */
|
||||
*filename++ = '\0';
|
||||
|
||||
/* Retrieve directory entry */
|
||||
/* If the first character is NULL, then just retrieve the root directory entry */
|
||||
if (!(dir_entry = (*path_dup ? romfsGetDirectoryEntryByPath(ctx, path_dup) : romfsGetDirectoryEntryByOffset(ctx, 0))))
|
||||
{
|
||||
LOGFILE("Failed to retrieve directory entry!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Retrieve file entry */
|
||||
if (!(file_entry = romfsGetChildFileEntryByName(ctx, dir_entry, filename))) LOGFILE("Failed to retrieve file entry by name!");
|
||||
|
||||
out:
|
||||
if (path_dup) free(path_dup);
|
||||
|
||||
return file_entry;
|
||||
}
|
||||
|
||||
bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size)
|
||||
{
|
||||
size_t path_len = 0;
|
||||
u32 dir_offset = ROMFS_VOID_ENTRY, dir_entries_count = 0;
|
||||
RomFileSystemDirectoryEntry **dir_entries = NULL, **tmp_dir_entries = NULL;
|
||||
bool success = false;
|
||||
out->patch_info = &(update_nca_fs_ctx->header->patch_info);
|
||||
out->size = out->patch_romfs_ctx.size;
|
||||
out->virtual_seek = out->base_seek = out->patch_seek = 0;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (!dir_entry->name_length && dir_entry->parent_offset) || !out_path || out_path_size < 2)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if we're dealing with the root directory entry */
|
||||
if (!dir_entry->name_length)
|
||||
{
|
||||
sprintf(out_path, "/");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Allocate memory for our directory entries */
|
||||
dir_entries = calloc(1, sizeof(RomFileSystemDirectoryEntry*));
|
||||
if (!dir_entries)
|
||||
{
|
||||
LOGFILE("Unable to allocate memory for directory entries!");
|
||||
return false;
|
||||
}
|
||||
|
||||
path_len = (1 + dir_entry->name_length);
|
||||
*dir_entries = dir_entry;
|
||||
dir_entries_count++;
|
||||
|
||||
while(true)
|
||||
{
|
||||
dir_offset = dir_entries[dir_entries_count - 1]->parent_offset;
|
||||
if (!dir_offset) break;
|
||||
|
||||
/* Reallocate directory entries */
|
||||
if (!(tmp_dir_entries = realloc(dir_entries, (dir_entries_count + 1) * sizeof(RomFileSystemDirectoryEntry*))))
|
||||
{
|
||||
LOGFILE("Unable to reallocate directory entries buffer!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dir_entries = tmp_dir_entries;
|
||||
tmp_dir_entries = NULL;
|
||||
|
||||
if (!(dir_entries[dir_entries_count] = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !dir_entries[dir_entries_count]->name_length)
|
||||
{
|
||||
LOGFILE("Failed to retrieve directory entry!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
path_len += (1 + dir_entries[dir_entries_count]->name_length);
|
||||
dir_entries_count++;
|
||||
}
|
||||
|
||||
if (path_len >= out_path_size)
|
||||
{
|
||||
LOGFILE("Output path length exceeds output buffer size!");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Generate output path */
|
||||
*out_path = '\0';
|
||||
for(u32 i = dir_entries_count; i > 0; i--)
|
||||
{
|
||||
strcat(out_path, "/");
|
||||
strncat(out_path, dir_entries[i - 1]->name, dir_entries[i - 1]->name_length);
|
||||
}
|
||||
|
||||
success = true;
|
||||
|
||||
out:
|
||||
if (dir_entries) free(dir_entries);
|
||||
exit:
|
||||
if (!success) bktrFreeContext(out);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size)
|
||||
{
|
||||
size_t path_len = 0;
|
||||
RomFileSystemDirectoryEntry *dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->file_table || !ctx->file_table_size || !file_entry || !file_entry->name_length || !out_path || out_path_size < 2 || \
|
||||
!(dir_entry = romfsGetDirectoryEntryByOffset(ctx, file_entry->parent_offset)))
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Retrieve full RomFS path up to the file entry name */
|
||||
if (!romfsGeneratePathFromDirectoryEntry(ctx, dir_entry, out_path, out_path_size))
|
||||
{
|
||||
LOGFILE("Failed to retrieve RomFS directory path!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check path length */
|
||||
path_len = strlen(out_path);
|
||||
if ((1 + file_entry->name_length) >= (out_path_size - path_len))
|
||||
{
|
||||
LOGFILE("Output path length exceeds output buffer size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Concatenate file entry name */
|
||||
strcat(out_path, "/");
|
||||
strncat(out_path, file_entry->name, file_entry->name_length);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out)
|
||||
{
|
||||
if (!ctx || !ctx->nca_fs_ctx || !ctx->body_offset || (ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs && ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || !file_entry || \
|
||||
!file_entry->size || file_entry->offset >= ctx->size || (file_entry->offset + file_entry->size) > ctx->size || !data || !data_size || data_offset >= file_entry->size || \
|
||||
(data_offset + data_size) > file_entry->size || !out)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
u64 fs_offset = (ctx->body_offset + file_entry->offset + data_offset);
|
||||
|
||||
memset(&(out->old_format_patch), 0, sizeof(NcaHierarchicalSha256Patch));
|
||||
memset(&(out->cur_format_patch), 0, sizeof(NcaHierarchicalIntegrityPatch));
|
||||
|
||||
if (ctx->nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs)
|
||||
{
|
||||
out->use_old_format_patch = true;
|
||||
success = ncaGenerateHierarchicalSha256Patch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->old_format_patch));
|
||||
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalSha256 patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
||||
} else {
|
||||
out->use_old_format_patch = false;
|
||||
success = ncaGenerateHierarchicalIntegrityPatch(ctx->nca_fs_ctx, data, data_size, fs_offset, &(out->cur_format_patch));
|
||||
if (!success) LOGFILE("Failed to generate 0x%lX bytes HierarchicalIntegrity patch at offset 0x%lX for RomFS file entry!", data_size, fs_offset);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static RomFileSystemDirectoryEntry *romfsGetChildDirectoryEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name)
|
||||
{
|
||||
u64 dir_offset = 0;
|
||||
size_t name_len = 0;
|
||||
RomFileSystemDirectoryEntry *child_dir_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !dir_entry || (dir_offset = dir_entry->directory_offset) == ROMFS_VOID_ENTRY || !name || !(name_len = strlen(name))) return NULL;
|
||||
|
||||
while(dir_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(child_dir_entry = romfsGetDirectoryEntryByOffset(ctx, dir_offset)) || !child_dir_entry->name_length) return NULL;
|
||||
if (!strncmp(child_dir_entry->name, name, name_len)) return child_dir_entry;
|
||||
dir_offset = child_dir_entry->next_offset;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static RomFileSystemFileEntry *romfsGetChildFileEntryByName(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, const char *name)
|
||||
{
|
||||
u64 file_offset = 0;
|
||||
size_t name_len = 0;
|
||||
RomFileSystemFileEntry *child_file_entry = NULL;
|
||||
|
||||
if (!ctx || !ctx->dir_table || !ctx->dir_table_size || !ctx->file_table || !ctx->file_table_size || !dir_entry || (file_offset = dir_entry->file_offset) == ROMFS_VOID_ENTRY || !name || \
|
||||
!(name_len = strlen(name))) return NULL;
|
||||
|
||||
while(file_offset != ROMFS_VOID_ENTRY)
|
||||
{
|
||||
if (!(child_file_entry = romfsGetFileEntryByOffset(ctx, file_offset)) || !child_file_entry->name_length) return NULL;
|
||||
if (!strncmp(child_file_entry->name, name, name_len)) return child_file_entry;
|
||||
file_offset = child_file_entry->next_offset;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -71,11 +71,16 @@ typedef struct {
|
|||
} BktrAesCtrExStorageBlock;
|
||||
|
||||
typedef struct {
|
||||
RomFileSystemContext base_romfs; ///< Base NCA RomFS context.
|
||||
RomFileSystemContext patch_romfs; ///< Update NCA RomFS context. Must be used with RomFS directory/file entry functions, because it holds the updated directory/file tables.
|
||||
NcaPatchInfo *patch_info;
|
||||
RomFileSystemContext base_romfs_ctx; ///< Base NCA RomFS context.
|
||||
RomFileSystemContext patch_romfs_ctx; ///< Update NCA RomFS context. Must be used with RomFS directory/file entry functions, because it holds the updated directory/file tables.
|
||||
NcaPatchInfo *patch_info; ///< BKTR patch info block.
|
||||
u64 size; ///< Patched RomFS image size.
|
||||
BktrIndirectStorageBlock *indirect_block;
|
||||
BktrAesCtrExStorageBlock *aes_ctr_ex_block;
|
||||
|
||||
|
||||
|
||||
|
||||
u64 virtual_seek; ///< Relative to the start of the NCA FS section.
|
||||
u64 base_seek; ///< Relative to the start of the NCA FS section (base NCA RomFS).
|
||||
u64 patch_seek; ///< Relative to the start of the NCA FS section (update NCA BKTR).
|
||||
|
@ -88,32 +93,70 @@ bool bktrInitializeContext(BktrContext *out, NcaFsSectionContext *base_nca_fs_ct
|
|||
NX_INLINE void bktrFreeContext(BktrContext *ctx)
|
||||
{
|
||||
if (!ctx) return;
|
||||
romfsFreeContext(&(ctx->base_romfs));
|
||||
romfsFreeContext(&(ctx->update_romfs));
|
||||
romfsFreeContext(&(ctx->base_romfs_ctx));
|
||||
romfsFreeContext(&(ctx->patch_romfs_ctx));
|
||||
if (ctx->indirect_block) free(ctx->indirect_block);
|
||||
if (ctx->aes_ctr_ex_block) free(ctx->aes_ctr_ex_block);
|
||||
memset(ctx, 0, sizeof(BktrContext));
|
||||
}
|
||||
|
||||
/// Reads raw filesystem data using a BKTR context.
|
||||
/// Input offset must be relative to the start of the patched RomFS image.
|
||||
bool bktrReadFileSystemData(BktrContext *ctx, void *out, u64 read_size, u64 offset);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Reads raw filesystem data using a RomFS context.
|
||||
/// Input offset must be relative to the start of the RomFS.
|
||||
//bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
|
||||
|
||||
/// Reads data from a previously retrieved RomFileSystemFileEntry using a RomFS context.
|
||||
/// Reads data from a previously retrieved RomFileSystemFileEntry using a BKTR context.
|
||||
/// Input offset must be relative to the start of the RomFS file entry data.
|
||||
//bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset);
|
||||
bool bktrReadFileEntryData(BktrContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset);
|
||||
|
||||
/// Miscellaneous functions.
|
||||
/// These are just wrappers for RomFS functions.
|
||||
|
||||
NX_INLINE RomFileSystemDirectoryEntry *bktrGetDirectoryEntryByOffset(BktrContext *ctx, u32 dir_entry_offset)
|
||||
{
|
||||
if (!ctx) return NULL;
|
||||
return romfsGetDirectoryEntryByOffset(&(ctx->patch_romfs_ctx), dir_entry_offset);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemFileEntry *bktrGetFileEntryByOffset(BktrContext *ctx, u32 file_entry_offset)
|
||||
{
|
||||
if (!ctx) return NULL;
|
||||
return romfsGetFileEntryByOffset(&(ctx->patch_romfs_ctx), file_entry_offset);
|
||||
}
|
||||
|
||||
NX_INLINE bool bktrGetTotalDataSize(BktrContext *ctx, u64 *out_size)
|
||||
{
|
||||
if (!ctx) return false;
|
||||
return romfsGetTotalDataSize(&(ctx->patch_romfs_ctx), out_size);
|
||||
}
|
||||
|
||||
NX_INLINE bool bktrGetDirectoryDataSize(BktrContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size)
|
||||
{
|
||||
if (!ctx) return false;
|
||||
return romfsGetDirectoryDataSize(&(ctx->patch_romfs_ctx), dir_entry, out_size);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemDirectoryEntry *bktrGetDirectoryEntryByPath(BktrContext *ctx, const char *path)
|
||||
{
|
||||
if (!ctx) return NULL;
|
||||
return romfsGetDirectoryEntryByPath(&(ctx->patch_romfs_ctx), path);
|
||||
}
|
||||
|
||||
NX_INLINE RomFileSystemFileEntry *bktrGetFileEntryByPath(BktrContext *ctx, const char *path)
|
||||
{
|
||||
if (!ctx) return NULL;
|
||||
return romfsGetFileEntryByPath(&(ctx->patch_romfs_ctx), path);
|
||||
}
|
||||
|
||||
NX_INLINE bool bktrGeneratePathFromDirectoryEntry(BktrContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size)
|
||||
{
|
||||
if (!ctx) return false;
|
||||
return romfsGeneratePathFromDirectoryEntry(&(ctx->patch_romfs_ctx), dir_entry, out_path, out_path_size);
|
||||
}
|
||||
|
||||
NX_INLINE bool bktrGeneratePathFromFileEntry(BktrContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size)
|
||||
{
|
||||
if (!ctx) return false;
|
||||
return romfsGeneratePathFromFileEntry(&(ctx->patch_romfs_ctx), file_entry, out_path, out_path_size);
|
||||
}
|
||||
|
||||
#endif /* __BKTR_H__ */
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#define CERT_SAVEFILE_STORAGE_BASE_PATH "/certificate/"
|
||||
|
||||
#define CERT_TYPE(sig) (pub_key_type == CertPubKeyType_Rsa4096 ? CertType_Sig##sig##_PubKeyRsa4096 : \
|
||||
(pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcsda240))
|
||||
(pub_key_type == CertPubKeyType_Rsa2048 ? CertType_Sig##sig##_PubKeyRsa2048 : CertType_Sig##sig##_PubKeyEcc480))
|
||||
|
||||
/* Global variables. */
|
||||
|
||||
|
@ -91,7 +91,7 @@ void certFreeCertificateChain(CertificateChain *chain)
|
|||
|
||||
CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
|
||||
{
|
||||
if (!cert || cert->type == CertType_None || cert->type > CertType_SigEcsda240_PubKeyEcsda240 || cert->size < CERT_MIN_SIZE || cert->size > CERT_MAX_SIZE)
|
||||
if (!cert || cert->type == CertType_None || cert->type > CertType_SigHmac160_PubKeyEcc480 || cert->size < CERT_MIN_SIZE || cert->size > CERT_MAX_SIZE)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return NULL;
|
||||
|
@ -107,8 +107,8 @@ CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
|
|||
case CertType_SigRsa4096_PubKeyRsa2048:
|
||||
cert_common_blk = &(((CertSigRsa4096PubKeyRsa2048*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigRsa4096_PubKeyEcsda240:
|
||||
cert_common_blk = &(((CertSigRsa4096PubKeyEcsda240*)cert->data)->cert_common_blk);
|
||||
case CertType_SigRsa4096_PubKeyEcc480:
|
||||
cert_common_blk = &(((CertSigRsa4096PubKeyEcc480*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigRsa2048_PubKeyRsa4096:
|
||||
cert_common_blk = &(((CertSigRsa2048PubKeyRsa4096*)cert->data)->cert_common_blk);
|
||||
|
@ -116,17 +116,26 @@ CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
|
|||
case CertType_SigRsa2048_PubKeyRsa2048:
|
||||
cert_common_blk = &(((CertSigRsa2048PubKeyRsa2048*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigRsa2048_PubKeyEcsda240:
|
||||
cert_common_blk = &(((CertSigRsa2048PubKeyEcsda240*)cert->data)->cert_common_blk);
|
||||
case CertType_SigRsa2048_PubKeyEcc480:
|
||||
cert_common_blk = &(((CertSigRsa2048PubKeyEcc480*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigEcsda240_PubKeyRsa4096:
|
||||
cert_common_blk = &(((CertSigEcsda240PubKeyRsa4096*)cert->data)->cert_common_blk);
|
||||
case CertType_SigEcc480_PubKeyRsa4096:
|
||||
cert_common_blk = &(((CertSigEcc480PubKeyRsa4096*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigEcsda240_PubKeyRsa2048:
|
||||
cert_common_blk = &(((CertSigEcsda240PubKeyRsa2048*)cert->data)->cert_common_blk);
|
||||
case CertType_SigEcc480_PubKeyRsa2048:
|
||||
cert_common_blk = &(((CertSigEcc480PubKeyRsa2048*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigEcsda240_PubKeyEcsda240:
|
||||
cert_common_blk = &(((CertSigEcsda240PubKeyEcsda240*)cert->data)->cert_common_blk);
|
||||
case CertType_SigEcc480_PubKeyEcc480:
|
||||
cert_common_blk = &(((CertSigEcc480PubKeyEcc480*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigHmac160_PubKeyRsa4096:
|
||||
cert_common_blk = &(((CertSigHmac160PubKeyRsa4096*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigHmac160_PubKeyRsa2048:
|
||||
cert_common_blk = &(((CertSigHmac160PubKeyRsa2048*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
case CertType_SigHmac160_PubKeyEcc480:
|
||||
cert_common_blk = &(((CertSigHmac160PubKeyEcc480*)cert->data)->cert_common_blk);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -265,9 +274,12 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
|
|||
case SignatureType_Rsa2048Sha256:
|
||||
offset += sizeof(SignatureBlockRsa2048);
|
||||
break;
|
||||
case SignatureType_Ecsda240Sha1:
|
||||
case SignatureType_Ecsda240Sha256:
|
||||
offset += sizeof(SignatureBlockEcsda240);
|
||||
case SignatureType_Ecc480Sha1:
|
||||
case SignatureType_Ecc480Sha256:
|
||||
offset += sizeof(SignatureBlockEcc480);
|
||||
break;
|
||||
case SignatureType_Hmac160Sha1:
|
||||
offset += sizeof(SignatureBlockHmac160);
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid signature type value! (0x%08X)", sig_type);
|
||||
|
@ -281,7 +293,7 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
|
|||
|
||||
offset += MEMBER_SIZE(CertCommonBlock, pub_key_type);
|
||||
offset += MEMBER_SIZE(CertCommonBlock, name);
|
||||
offset += MEMBER_SIZE(CertCommonBlock, cert_id);
|
||||
offset += MEMBER_SIZE(CertCommonBlock, date);
|
||||
|
||||
switch(pub_key_type)
|
||||
{
|
||||
|
@ -291,8 +303,8 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
|
|||
case CertPubKeyType_Rsa2048:
|
||||
offset += sizeof(CertPublicKeyBlockRsa2048);
|
||||
break;
|
||||
case CertPubKeyType_Ecsda240:
|
||||
offset += sizeof(CertPublicKeyBlockEcsda240);
|
||||
case CertPubKeyType_Ecc480:
|
||||
offset += sizeof(CertPublicKeyBlockEcc480);
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid public key type value! (0x%08X)", pub_key_type);
|
||||
|
@ -313,9 +325,13 @@ static u8 certGetCertificateType(const void *data, u64 data_size)
|
|||
{
|
||||
type = CERT_TYPE(Rsa2048);
|
||||
} else
|
||||
if (sig_type == SignatureType_Ecsda240Sha1 || sig_type == SignatureType_Ecsda240Sha256)
|
||||
if (sig_type == SignatureType_Ecc480Sha1 || sig_type == SignatureType_Ecc480Sha256)
|
||||
{
|
||||
type = CERT_TYPE(Ecsda240);
|
||||
type = CERT_TYPE(Ecc480);
|
||||
} else
|
||||
if (sig_type == SignatureType_Hmac160Sha1)
|
||||
{
|
||||
type = CERT_TYPE(Hmac160);
|
||||
}
|
||||
|
||||
return type;
|
||||
|
|
|
@ -23,26 +23,29 @@
|
|||
#include "signature.h"
|
||||
|
||||
#define CERT_MAX_SIZE 0x500 /* Equivalent to sizeof(CertSigRsa4096PubKeyRsa4096) */
|
||||
#define CERT_MIN_SIZE 0x180 /* Equivalent to sizeof(CertSigEcsda240PubKeyEcsda240) */
|
||||
#define CERT_MIN_SIZE 0x140 /* Equivalent to sizeof(CertSigHmac160PubKeyEcc480) */
|
||||
|
||||
typedef enum {
|
||||
CertType_None = 0,
|
||||
CertType_SigRsa4096_PubKeyRsa4096 = 1,
|
||||
CertType_SigRsa4096_PubKeyRsa2048 = 2,
|
||||
CertType_SigRsa4096_PubKeyEcsda240 = 3,
|
||||
CertType_SigRsa4096_PubKeyEcc480 = 3,
|
||||
CertType_SigRsa2048_PubKeyRsa4096 = 4,
|
||||
CertType_SigRsa2048_PubKeyRsa2048 = 5,
|
||||
CertType_SigRsa2048_PubKeyEcsda240 = 6,
|
||||
CertType_SigEcsda240_PubKeyRsa4096 = 7,
|
||||
CertType_SigEcsda240_PubKeyRsa2048 = 8,
|
||||
CertType_SigEcsda240_PubKeyEcsda240 = 9
|
||||
CertType_SigRsa2048_PubKeyEcc480 = 6,
|
||||
CertType_SigEcc480_PubKeyRsa4096 = 7,
|
||||
CertType_SigEcc480_PubKeyRsa2048 = 8,
|
||||
CertType_SigEcc480_PubKeyEcc480 = 9,
|
||||
CertType_SigHmac160_PubKeyRsa4096 = 10,
|
||||
CertType_SigHmac160_PubKeyRsa2048 = 11,
|
||||
CertType_SigHmac160_PubKeyEcc480 = 12
|
||||
} CertType;
|
||||
|
||||
/// Always stored using big endian byte order.
|
||||
typedef enum {
|
||||
CertPubKeyType_Rsa4096 = 0,
|
||||
CertPubKeyType_Rsa2048 = 1,
|
||||
CertPubKeyType_Ecsda240 = 2
|
||||
CertPubKeyType_Ecc480 = 2
|
||||
} CertPubKeyType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -60,69 +63,87 @@ typedef struct {
|
|||
typedef struct {
|
||||
u8 public_key[0x3C];
|
||||
u8 padding[0x3C];
|
||||
} CertPublicKeyBlockEcsda240;
|
||||
} CertPublicKeyBlockEcc480;
|
||||
|
||||
/// Placed after the certificate signature block.
|
||||
typedef struct {
|
||||
char issuer[0x40];
|
||||
u32 pub_key_type;
|
||||
u32 pub_key_type; ///< CertPubKeyType.
|
||||
char name[0x40];
|
||||
u32 cert_id;
|
||||
u32 date;
|
||||
} CertCommonBlock;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
CertPublicKeyBlockRsa4096 pub_key_block;
|
||||
} CertSigRsa4096PubKeyRsa4096;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
CertPublicKeyBlockRsa2048 pub_key_block;
|
||||
} CertSigRsa4096PubKeyRsa2048;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240.
|
||||
CertPublicKeyBlockEcsda240 pub_key_block;
|
||||
} CertSigRsa4096PubKeyEcsda240;
|
||||
SignatureBlockRsa4096 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
|
||||
CertPublicKeyBlockEcc480 pub_key_block;
|
||||
} CertSigRsa4096PubKeyEcc480;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
CertPublicKeyBlockRsa4096 pub_key_block;
|
||||
} CertSigRsa2048PubKeyRsa4096;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
CertPublicKeyBlockRsa2048 pub_key_block;
|
||||
} CertSigRsa2048PubKeyRsa2048;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240.
|
||||
CertPublicKeyBlockEcsda240 pub_key_block;
|
||||
} CertSigRsa2048PubKeyEcsda240;
|
||||
SignatureBlockRsa2048 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
|
||||
CertPublicKeyBlockEcc480 pub_key_block;
|
||||
} CertSigRsa2048PubKeyEcc480;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
CertPublicKeyBlockRsa4096 pub_key_block;
|
||||
} CertSigEcsda240PubKeyRsa4096;
|
||||
} CertSigEcc480PubKeyRsa4096;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
CertPublicKeyBlockRsa2048 pub_key_block;
|
||||
} CertSigEcsda240PubKeyRsa2048;
|
||||
} CertSigEcc480PubKeyRsa2048;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockEcsda240 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecsda240.
|
||||
CertPublicKeyBlockEcsda240 pub_key_block;
|
||||
} CertSigEcsda240PubKeyEcsda240;
|
||||
SignatureBlockEcc480 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
|
||||
CertPublicKeyBlockEcc480 pub_key_block;
|
||||
} CertSigEcc480PubKeyEcc480;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa4096.
|
||||
CertPublicKeyBlockRsa4096 pub_key_block;
|
||||
} CertSigHmac160PubKeyRsa4096;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Rsa2048.
|
||||
CertPublicKeyBlockRsa2048 pub_key_block;
|
||||
} CertSigHmac160PubKeyRsa2048;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockHmac160 sig_block; ///< sig_type field is stored using big endian byte order.
|
||||
CertCommonBlock cert_common_blk; ///< pub_key_type field must be CertPubKeyType_Ecc480.
|
||||
CertPublicKeyBlockEcc480 pub_key_block;
|
||||
} CertSigHmac160PubKeyEcc480;
|
||||
|
||||
/// Used to store certificate type, size and raw data.
|
||||
typedef struct {
|
||||
|
|
|
@ -321,7 +321,7 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset);
|
|||
|
||||
/// Reads decrypted data from a NCA FS section using an input context.
|
||||
/// Input offset must be relative to the start of the NCA FS section.
|
||||
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading BKTR subsections.
|
||||
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading the BKTR AesCtrEx storage.
|
||||
bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset);
|
||||
|
||||
/// Returns a pointer to a heap-allocated buffer used to encrypt the input plaintext data, based on the encryption type used by the input NCA FS section, as well as its offset and size.
|
||||
|
|
|
@ -86,6 +86,7 @@ bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *
|
|||
if (!ncaReadFsSection(nca_fs_ctx, out->header, out->header_size, out->offset))
|
||||
{
|
||||
LOGFILE("Failed to read full partition FS header!");
|
||||
pfsFreeContext(out);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,10 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
|
|||
NcaContext *nca_ctx = NULL;
|
||||
u64 dir_table_offset = 0, file_table_offset = 0;
|
||||
|
||||
if (!out || !nca_fs_ctx || nca_fs_ctx->section_type != NcaFsSectionType_RomFs || !nca_fs_ctx->header || !(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)))
|
||||
if (!out || !nca_fs_ctx || (nca_fs_ctx->section_type != NcaFsSectionType_RomFs && nca_fs_ctx->section_type != NcaFsSectionType_PatchRomFs) || !nca_fs_ctx->header || \
|
||||
!(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)))
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return false;
|
||||
|
@ -85,6 +86,8 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
|
|||
return false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
/* Read directory entries table */
|
||||
dir_table_offset = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.directory_entry_offset : out->header.cur_format.directory_entry_offset);
|
||||
out->dir_table_size = (nca_fs_ctx->section_type == NcaFsSectionType_Nca0RomFs ? (u64)out->header.old_format.directory_entry_size : out->header.cur_format.directory_entry_size);
|
||||
|
@ -106,7 +109,7 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
|
|||
if (!ncaReadFsSection(nca_fs_ctx, out->dir_table, out->dir_table_size, out->offset + dir_table_offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS directory entries table!");
|
||||
return false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Read file entries table */
|
||||
|
@ -117,20 +120,20 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
|
|||
if (!out->file_table_size || (nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && (file_table_offset >= out->size || (file_table_offset + out->file_table_size) > out->size)))
|
||||
{
|
||||
LOGFILE("Invalid RomFS file entries table!");
|
||||
return false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
out->file_table = malloc(out->file_table_size);
|
||||
if (!out->file_table)
|
||||
{
|
||||
LOGFILE("Unable to allocate memory for RomFS file entries table!");
|
||||
return false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!ncaReadFsSection(nca_fs_ctx, out->file_table, out->file_table_size, out->offset + file_table_offset))
|
||||
{
|
||||
LOGFILE("Failed to read RomFS file entries table!");
|
||||
return false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Get file data body offset */
|
||||
|
@ -139,10 +142,15 @@ bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *nca_
|
|||
if (nca_fs_ctx->encryption_type != NcaEncryptionType_AesCtrEx && out->body_offset >= out->size)
|
||||
{
|
||||
LOGFILE("Invalid RomFS file data body!");
|
||||
return false;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
return true;
|
||||
success = true;
|
||||
|
||||
exit:
|
||||
if (!success) romfsFreeContext(out);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset)
|
||||
|
|
|
@ -130,7 +130,7 @@ bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *f
|
|||
bool romfsGetTotalDataSize(RomFileSystemContext *ctx, u64 *out_size);
|
||||
|
||||
/// Calculates the extracted size from a RomFS directory.
|
||||
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size);
|
||||
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size);
|
||||
|
||||
/// Retrieves a RomFS directory entry by path.
|
||||
/// Input path must have a leading slash ('/'). If just a single slash is provided, a pointer to the root directory entry shall be returned.
|
||||
|
|
|
@ -20,12 +20,13 @@
|
|||
#define __SIGNATURE_H__
|
||||
|
||||
typedef enum {
|
||||
SignatureType_Rsa4096Sha1 = 0x10000,
|
||||
SignatureType_Rsa2048Sha1 = 0x10001,
|
||||
SignatureType_Ecsda240Sha1 = 0x10002,
|
||||
SignatureType_Rsa4096Sha256 = 0x10003,
|
||||
SignatureType_Rsa2048Sha256 = 0x10004,
|
||||
SignatureType_Ecsda240Sha256 = 0x10005
|
||||
SignatureType_Rsa4096Sha1 = 0x10000,
|
||||
SignatureType_Rsa2048Sha1 = 0x10001,
|
||||
SignatureType_Ecc480Sha1 = 0x10002,
|
||||
SignatureType_Rsa4096Sha256 = 0x10003,
|
||||
SignatureType_Rsa2048Sha256 = 0x10004,
|
||||
SignatureType_Ecc480Sha256 = 0x10005,
|
||||
SignatureType_Hmac160Sha1 = 0x10006
|
||||
} SignatureType;
|
||||
|
||||
typedef struct {
|
||||
|
@ -41,9 +42,15 @@ typedef struct {
|
|||
} SignatureBlockRsa2048;
|
||||
|
||||
typedef struct {
|
||||
u32 sig_type; ///< SignatureType_Ecsda240Sha1, SignatureType_Ecsda240Sha256.
|
||||
u32 sig_type; ///< SignatureType_Ecc480Sha1, SignatureType_Ecc480Sha256.
|
||||
u8 signature[0x3C];
|
||||
u8 padding[0x40];
|
||||
} SignatureBlockEcsda240;
|
||||
} SignatureBlockEcc480;
|
||||
|
||||
typedef struct {
|
||||
u32 sig_type; ///< SignatureType_Hmac160Sha1.
|
||||
u8 signature[0x14];
|
||||
u8 padding[0x28];
|
||||
} SignatureBlockHmac160;
|
||||
|
||||
#endif /* __SIGNATURE_H__ */
|
||||
|
|
44
source/tik.c
44
source/tik.c
|
@ -83,7 +83,7 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
|
|||
}
|
||||
|
||||
/* Check if this ticket has already been retrieved */
|
||||
if (dst->type > TikType_None && dst->type <= TikType_SigEcsda240 && dst->size >= TIK_MIN_SIZE && dst->size <= TIK_MAX_SIZE)
|
||||
if (dst->type > TikType_None && dst->type <= TikType_SigHmac160 && dst->size >= TIK_MIN_SIZE && dst->size <= TIK_MAX_SIZE)
|
||||
{
|
||||
TikCommonBlock *tik_common_blk = tikGetCommonBlockFromTicket(dst);
|
||||
if (tik_common_blk && !memcmp(tik_common_blk->rights_id.c, id->c, 0x10)) return true;
|
||||
|
@ -115,7 +115,7 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
|
|||
|
||||
TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik)
|
||||
{
|
||||
if (!tik || tik->type == TikType_None || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE)
|
||||
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE)
|
||||
{
|
||||
LOGFILE("Invalid parameters!");
|
||||
return NULL;
|
||||
|
@ -131,8 +131,11 @@ TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik)
|
|||
case TikType_SigRsa2048:
|
||||
tik_common_blk = &(((TikSigRsa2048*)tik->data)->tik_common_blk);
|
||||
break;
|
||||
case TikType_SigEcsda240:
|
||||
tik_common_blk = &(((TikSigEcsda240*)tik->data)->tik_common_blk);
|
||||
case TikType_SigEcc480:
|
||||
tik_common_blk = &(((TikSigEcc480*)tik->data)->tik_common_blk);
|
||||
break;
|
||||
case TikType_SigHmac160:
|
||||
tik_common_blk = &(((TikSigHmac160*)tik->data)->tik_common_blk);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -143,7 +146,7 @@ TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik)
|
|||
|
||||
void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
|
||||
{
|
||||
if (!tik || tik->type == TikType_None || tik->type > TikType_SigEcsda240 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE) return;
|
||||
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < TIK_MIN_SIZE || tik->size > TIK_MAX_SIZE) return;
|
||||
|
||||
bool dev_cert = false;
|
||||
TikCommonBlock *tik_common_blk = NULL;
|
||||
|
@ -161,9 +164,13 @@ void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
|
|||
tik->size = sizeof(TikSigRsa2048);
|
||||
memset(tik->data + 4, 0xFF, MEMBER_SIZE(SignatureBlockRsa2048, signature));
|
||||
break;
|
||||
case TikType_SigEcsda240:
|
||||
tik->size = sizeof(TikSigEcsda240);
|
||||
memset(tik->data + 4, 0xFF, MEMBER_SIZE(SignatureBlockEcsda240, signature));
|
||||
case TikType_SigEcc480:
|
||||
tik->size = sizeof(TikSigEcc480);
|
||||
memset(tik->data + 4, 0xFF, MEMBER_SIZE(SignatureBlockEcc480, signature));
|
||||
break;
|
||||
case TikType_SigHmac160:
|
||||
tik->size = sizeof(TikSigHmac160);
|
||||
memset(tik->data + 4, 0xFF, MEMBER_SIZE(SignatureBlockHmac160, signature));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -362,9 +369,12 @@ static TikCommonBlock *tikGetCommonBlockFromMemoryBuffer(void *data)
|
|||
case SignatureType_Rsa2048Sha256:
|
||||
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockRsa2048));
|
||||
break;
|
||||
case SignatureType_Ecsda240Sha1:
|
||||
case SignatureType_Ecsda240Sha256:
|
||||
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockEcsda240));
|
||||
case SignatureType_Ecc480Sha1:
|
||||
case SignatureType_Ecc480Sha256:
|
||||
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockEcc480));
|
||||
break;
|
||||
case SignatureType_Hmac160Sha1:
|
||||
tik_common_blk = (TikCommonBlock*)(data_u8 + sizeof(SignatureBlockHmac160));
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid signature type value! (0x%08X)", sig_type);
|
||||
|
@ -572,10 +582,14 @@ static bool tikGetTicketTypeAndSize(const void *data, u64 data_size, u8 *out_typ
|
|||
type = TikType_SigRsa2048;
|
||||
offset += sizeof(SignatureBlockRsa2048);
|
||||
break;
|
||||
case SignatureType_Ecsda240Sha1:
|
||||
case SignatureType_Ecsda240Sha256:
|
||||
type = TikType_SigEcsda240;
|
||||
offset += sizeof(SignatureBlockEcsda240);
|
||||
case SignatureType_Ecc480Sha1:
|
||||
case SignatureType_Ecc480Sha256:
|
||||
type = TikType_SigEcc480;
|
||||
offset += sizeof(SignatureBlockEcc480);
|
||||
break;
|
||||
case SignatureType_Hmac160Sha1:
|
||||
type = TikType_SigHmac160;
|
||||
offset += sizeof(SignatureBlockHmac160);
|
||||
break;
|
||||
default:
|
||||
LOGFILE("Invalid signature type value! (0x%08X)", sig_type);
|
||||
|
|
14
source/tik.h
14
source/tik.h
|
@ -23,13 +23,14 @@
|
|||
#include "signature.h"
|
||||
|
||||
#define TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file */
|
||||
#define TIK_MIN_SIZE 0x200 /* Equivalent to sizeof(TikSigEcsda240) - assuming no ESv2 records are available */
|
||||
#define TIK_MIN_SIZE 0x1C0 /* Equivalent to sizeof(TikSigHmac160) - assuming no ESv2 records are available */
|
||||
|
||||
typedef enum {
|
||||
TikType_None = 0,
|
||||
TikType_SigRsa4096 = 1,
|
||||
TikType_SigRsa2048 = 2,
|
||||
TikType_SigEcsda240 = 3
|
||||
TikType_SigEcc480 = 3,
|
||||
TikType_SigHmac160 = 4
|
||||
} TikType;
|
||||
|
||||
typedef enum {
|
||||
|
@ -96,9 +97,14 @@ typedef struct {
|
|||
} TikSigRsa2048;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockEcsda240 sig_block;
|
||||
SignatureBlockEcc480 sig_block;
|
||||
TikCommonBlock tik_common_blk;
|
||||
} TikSigEcsda240;
|
||||
} TikSigEcc480;
|
||||
|
||||
typedef struct {
|
||||
SignatureBlockHmac160 sig_block;
|
||||
TikCommonBlock tik_common_blk;
|
||||
} TikSigHmac160;
|
||||
|
||||
/// Section records are placed right after the ticket data. These aren't available in TikTitleKeyType_Common tickets.
|
||||
/// These are only used if the sect_* fields are non-zero (other than 'sect_hdr_offset').
|
||||
|
|
|
@ -34,8 +34,30 @@
|
|||
|
||||
#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define NPDM_META_MAGIC 0x4D455441 /* "META" */
|
||||
|
||||
#define TITLE_PATCH_BITMASK (u64)0x800
|
||||
#define TITLE_ADDON_BITMASK (u64)0xFFFFFFFFFFFF0000
|
||||
|
||||
|
||||
NX_INLINE u64 titleGetPatchIdFromApplicationId(u64 app_id)
|
||||
{
|
||||
return (app_id | TITLE_PATCH_BITMASK);
|
||||
}
|
||||
|
||||
NX_INLINE u64 titleGetApplicationIdFromPatchId(u64 patch_id)
|
||||
{
|
||||
return (patch_id & ~TITLE_PATCH_BITMASK);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
typedef enum {
|
||||
UtilsCustomFirmwareType_Unknown = 0,
|
||||
UtilsCustomFirmwareType_Atmosphere = 1,
|
||||
|
|
Loading…
Reference in a new issue