hac2l: support saving raw sections, printing rsa signature validity

This commit is contained in:
Michael Scire 2022-03-13 20:58:30 -07:00
parent 929e8d4f79
commit 59f80172a1
3 changed files with 112 additions and 49 deletions

View File

@ -62,7 +62,9 @@ namespace ams::hactool {
s32 exefs_index = -1;
s32 romfs_index = -1;
std::array<bool, fssystem::NcaHeader::FsCountMax> has_sections{};
std::array<bool, fssystem::NcaHeader::FsCountMax> has_real_sections{};
std::array<bool, fssystem::NcaHeader::FsCountMax> is_mounted{};
std::array<std::shared_ptr<fs::IStorage>, fssystem::NcaHeader::FsCountMax> raw_sections{};
std::array<std::shared_ptr<fs::IStorage>, fssystem::NcaHeader::FsCountMax> sections{};
std::array<std::shared_ptr<fssystem::IAsynchronousAccessSplitter>, fssystem::NcaHeader::FsCountMax> splitters{};
std::array<fssystem::NcaFsHeaderReader, fssystem::NcaHeader::FsCountMax> header_readers{};
@ -111,6 +113,10 @@ namespace ams::hactool {
this->PrintString("Magic", magic_str);
}
static void MakeVerifyFieldName(char *dst, size_t dst_size, const char *name, bool verified) {
util::TSNPrintf(dst, dst_size, "%s %s", name, verified ? "(GOOD)" : "(FAIL)");
}
void PrintHex(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%" PRIX64, v); }
void PrintHex2(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%02" PRIX64, v); }
void PrintHex4(const char *field_name, u64 v) const { this->PrintFormat(field_name, "0x%04" PRIX64, v); }
@ -143,6 +149,13 @@ namespace ams::hactool {
}
}
void PrintBytesWithVerify(const char *field_name, bool verified, const void *src, size_t src_size) {
char verif_field_name[0x40];
MakeVerifyFieldName(verif_field_name, sizeof(verif_field_name), field_name, verified);
return this->PrintBytes(verif_field_name, src, src_size);
}
/* Utility/management. */
void PresetInternalKeys();

View File

@ -131,64 +131,73 @@ namespace ams::hactool {
std::shared_ptr<fs::IStorage> npdm_storage;
for (s32 i = 0; i < fssystem::NcaHeader::FsCountMax; ++i) {
const auto res = util::GetReference(g_storage_on_nca_creator).CreateWithContext(std::addressof(ctx->sections[i]), std::addressof(ctx->splitters[i]), std::addressof(ctx->header_readers[i]), std::addressof(ctx->storage_contexts[i]), ctx->reader, i);
ctx->storage_contexts[i].open_raw_storage = true;
const auto res = util::GetReference(g_storage_on_nca_creator).CreateWithContext(std::addressof(ctx->raw_sections[i]), std::addressof(ctx->splitters[i]), std::addressof(ctx->header_readers[i]), std::addressof(ctx->storage_contexts[i]), ctx->reader, i);
if (R_SUCCEEDED(res)) {
ctx->has_sections[i] = true;
/* Try to mount the section. */
const auto fs_type = ctx->header_readers[i].GetFsType();
switch (fs_type) {
case fssystem::NcaFsHeader::FsType::PartitionFs:
{
const auto mount_res = util::GetReference(g_partition_fs_creator).Create(std::addressof(ctx->file_systems[i]), ctx->sections[i]);
if (R_SUCCEEDED(mount_res)) {
ctx->is_mounted[i] = true;
/* Try to open the non-raw section. */
const auto real_res = util::GetReference(g_storage_on_nca_creator).CreateByRawStorage(std::addressof(ctx->sections[i]), std::addressof(ctx->splitters[i]), std::addressof(ctx->header_readers[i]), std::shared_ptr<fs::IStorage>(ctx->raw_sections[i]), std::addressof(ctx->storage_contexts[i]), ctx->reader);
if (R_SUCCEEDED(real_res)) {
ctx->has_real_sections[i] = true;
/* Check if section is exefs. */
if (ctx->exefs_index < 0 && ctx->reader->GetContentType() == fssystem::NcaHeader::ContentType::Program) {
bool is_exefs = false;
const auto check_npdm_res = fssystem::HasFile(std::addressof(is_exefs), ctx->file_systems[i].get(), fs::MakeConstantPath("/main.npdm"));
if (R_SUCCEEDED(check_npdm_res)) {
if (is_exefs) {
ctx->exefs_index = i;
/* Try to mount the section. */
const auto fs_type = ctx->header_readers[i].GetFsType();
switch (fs_type) {
case fssystem::NcaFsHeader::FsType::PartitionFs:
{
const auto mount_res = util::GetReference(g_partition_fs_creator).Create(std::addressof(ctx->file_systems[i]), ctx->sections[i]);
if (R_SUCCEEDED(mount_res)) {
ctx->is_mounted[i] = true;
if (const auto open_npdm_res = OpenFileStorage(std::addressof(npdm_storage), ctx->file_systems[i], "/main.npdm"); R_FAILED(open_npdm_res)) {
fprintf(stderr, "[Warning]: main.npdm exists in exefs section %d but could not be opened: 2%03d-%04d\n", i, open_npdm_res.GetModule(), open_npdm_res.GetDescription());
/* Check if section is exefs. */
if (ctx->exefs_index < 0 && ctx->reader->GetContentType() == fssystem::NcaHeader::ContentType::Program) {
bool is_exefs = false;
const auto check_npdm_res = fssystem::HasFile(std::addressof(is_exefs), ctx->file_systems[i].get(), fs::MakeConstantPath("/main.npdm"));
if (R_SUCCEEDED(check_npdm_res)) {
if (is_exefs) {
ctx->exefs_index = i;
if (const auto open_npdm_res = OpenFileStorage(std::addressof(npdm_storage), ctx->file_systems[i], "/main.npdm"); R_FAILED(open_npdm_res)) {
fprintf(stderr, "[Warning]: main.npdm exists in exefs section %d but could not be opened: 2%03d-%04d\n", i, open_npdm_res.GetModule(), open_npdm_res.GetDescription());
}
}
} else {
fprintf(stderr, "[Warning]: Failed to check if NCA section %d is exefs: 2%03d-%04d\n", i, check_npdm_res.GetModule(), check_npdm_res.GetDescription());
}
} else {
fprintf(stderr, "[Warning]: Failed to check if NCA section %d is exefs: 2%03d-%04d\n", i, check_npdm_res.GetModule(), check_npdm_res.GetDescription());
}
} else {
fprintf(stderr, "[Warning]: Failed to mount NCA section %d as PartitionFileSystem: 2%03d-%04d\n", i, mount_res.GetModule(), mount_res.GetDescription());
}
} else {
fprintf(stderr, "[Warning]: Failed to mount NCA section %d as PartitionFileSystem: 2%03d-%04d\n", i, mount_res.GetModule(), mount_res.GetDescription());
}
}
break;
case fssystem::NcaFsHeader::FsType::RomFs:
{
const auto mount_res = util::GetReference(g_rom_fs_creator).Create(std::addressof(ctx->file_systems[i]), ctx->sections[i]);
if (R_SUCCEEDED(mount_res)) {
ctx->is_mounted[i] = true;
break;
case fssystem::NcaFsHeader::FsType::RomFs:
{
const auto mount_res = util::GetReference(g_rom_fs_creator).Create(std::addressof(ctx->file_systems[i]), ctx->sections[i]);
if (R_SUCCEEDED(mount_res)) {
ctx->is_mounted[i] = true;
if (ctx->romfs_index < 0) {
ctx->romfs_index = i;
if (ctx->romfs_index < 0) {
ctx->romfs_index = i;
}
} else {
fprintf(stderr, "[Warning]: Failed to mount NCA section %d as RomFsFileSystem: 2%03d-%04d\n", i, mount_res.GetModule(), mount_res.GetDescription());
}
} else {
fprintf(stderr, "[Warning]: Failed to mount NCA section %d as RomFsFileSystem: 2%03d-%04d\n", i, mount_res.GetModule(), mount_res.GetDescription());
}
}
break;
default:
fprintf(stderr, "[Warning]: NCA section %d has unknown section type %d\n", i, static_cast<int>(fs_type));
break;
break;
default:
fprintf(stderr, "[Warning]: NCA section %d has unknown section type %d\n", i, static_cast<int>(fs_type));
break;
}
} else {
fprintf(stderr, "[Warning]: Failed to open NCA section %d: 2%03d-%04d, NCA may be corrupt.\n", i, res.GetModule(), res.GetDescription());
}
} else if (fs::ResultPartitionNotFound::Includes(res)) {
ctx->has_sections[i] = false;
} else {
/* TODO: Should we stop here instead of pretending the NCA doesn't have this section? */
fprintf(stderr, "[Warning]: Failed to open NCA section %d: 2%03d-%04d\n", i, res.GetModule(), res.GetDescription());
fprintf(stderr, "[Warning]: Failed to open raw NCA section %d: 2%03d-%04d\n", i, res.GetModule(), res.GetDescription());
}
}
@ -222,8 +231,33 @@ namespace ams::hactool {
this->PrintMagic(ctx.reader->GetMagic());
this->PrintHex("HeaderSign1 Key Generation", ctx.reader->GetHeaderSign1KeyGeneration());
this->PrintBytes("HeaderSign1:", raw_header.header_sign_1, sizeof(raw_header.header_sign_1));
this->PrintBytes("HeaderSign2:", raw_header.header_sign_2, sizeof(raw_header.header_sign_2));
if (!m_options.verify) {
this->PrintBytes("HeaderSign1", raw_header.header_sign_1, sizeof(raw_header.header_sign_1));
this->PrintBytes("HeaderSign2", raw_header.header_sign_2, sizeof(raw_header.header_sign_2));
} else {
this->PrintBytesWithVerify("HeaderSign1", ctx.reader->GetHeaderSign1Valid(), raw_header.header_sign_1, sizeof(raw_header.header_sign_1));
if (ctx.reader->GetContentType() == fssystem::NcaHeader::ContentType::Program) {
bool is_header_sign2_valid = false;
if (ctx.npdm_ctx.modulus != nullptr) {
const u8 *sig = raw_header.header_sign_2;
const size_t sig_size = sizeof(raw_header.header_sign_2);
const u8 *mod = static_cast<const u8 *>(ctx.npdm_ctx.modulus);
const size_t mod_size = crypto::Rsa2048PssSha256Verifier::ModulusSize;
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
u8 hsh[fssystem::IHash256Generator::HashSize];
ctx.reader->GetHeaderSign2TargetHash(hsh, sizeof(hsh));
is_header_sign2_valid = crypto::VerifyRsa2048PssSha256WithHash(sig, sig_size, mod, mod_size, exp, exp_size, hsh, sizeof(hsh));
}
this->PrintBytesWithVerify("HeaderSign2", is_header_sign2_valid, raw_header.header_sign_2, sizeof(raw_header.header_sign_2));
} else {
this->PrintBytes("HeaderSign2", raw_header.header_sign_2, sizeof(raw_header.header_sign_2));
}
}
this->PrintHex12("Content Size", ctx.reader->GetContentSize());
union {
@ -343,11 +377,6 @@ namespace ams::hactool {
/* Process sections. */
for (s32 i = 0; i < fssystem::NcaHeader::FsCountMax; ++i) {
/* If we don't have the section, do nothing. */
if (!ctx.has_sections[i]) {
continue;
}
/* TODO: Save section as file, including raw. */
{
/* Determine path to save to. */
@ -364,7 +393,11 @@ namespace ams::hactool {
/* If we have a path, save to it. */
if (path != nullptr) {
SaveToFile(m_local_fs, path, ctx.sections[i].get());
if (m_options.raw && ctx.has_sections[i]) {
SaveToFile(m_local_fs, path, ctx.raw_sections[i].get());
} else if (ctx.has_real_sections[i]) {
SaveToFile(m_local_fs, path, ctx.sections[i].get());
}
}
}

View File

@ -606,7 +606,24 @@ namespace ams::hactool {
this->PrintHex("Sign Key Generation", ctx.npdm->signature_key_generation);
this->PrintBytes("Signature", ctx.acid->signature, sizeof(ctx.acid->signature));
if (m_options.verify) {
/* Verify the signature. */
const u8 *sig = ctx.acid->signature;
const size_t sig_size = sizeof(ctx.acid->signature);
const u8 *mod = fssystem::GetAcidSignatureKeyModulus(!m_options.dev, ctx.npdm->signature_key_generation);
const size_t mod_size = fssystem::AcidSignatureKeyModulusSize;
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
const u8 *msg = ctx.acid->modulus;
const size_t msg_size = ctx.acid->size;
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size);
this->PrintBytesWithVerify("Signature", is_signature_valid, ctx.acid->signature, sizeof(ctx.acid->signature));
} else {
this->PrintBytes("Signature", ctx.acid->signature, sizeof(ctx.acid->signature));
}
this->PrintBytes("HeaderSign2 Modulus", ctx.acid->modulus, sizeof(ctx.acid->modulus));
this->PrintHex2("Flags", ctx.acid->flags);