mirror of
https://github.com/Atmosphere-NX/hac2l.git
synced 2025-06-21 11:12:39 +02:00
hac2l: support saving raw sections, printing rsa signature validity
This commit is contained in:
parent
929e8d4f79
commit
59f80172a1
@ -62,7 +62,9 @@ namespace ams::hactool {
|
|||||||
s32 exefs_index = -1;
|
s32 exefs_index = -1;
|
||||||
s32 romfs_index = -1;
|
s32 romfs_index = -1;
|
||||||
std::array<bool, fssystem::NcaHeader::FsCountMax> has_sections{};
|
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<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<fs::IStorage>, fssystem::NcaHeader::FsCountMax> sections{};
|
||||||
std::array<std::shared_ptr<fssystem::IAsynchronousAccessSplitter>, fssystem::NcaHeader::FsCountMax> splitters{};
|
std::array<std::shared_ptr<fssystem::IAsynchronousAccessSplitter>, fssystem::NcaHeader::FsCountMax> splitters{};
|
||||||
std::array<fssystem::NcaFsHeaderReader, fssystem::NcaHeader::FsCountMax> header_readers{};
|
std::array<fssystem::NcaFsHeaderReader, fssystem::NcaHeader::FsCountMax> header_readers{};
|
||||||
@ -111,6 +113,10 @@ namespace ams::hactool {
|
|||||||
this->PrintString("Magic", magic_str);
|
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 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 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); }
|
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. */
|
/* Utility/management. */
|
||||||
void PresetInternalKeys();
|
void PresetInternalKeys();
|
||||||
|
|
||||||
|
@ -131,10 +131,16 @@ namespace ams::hactool {
|
|||||||
std::shared_ptr<fs::IStorage> npdm_storage;
|
std::shared_ptr<fs::IStorage> npdm_storage;
|
||||||
|
|
||||||
for (s32 i = 0; i < fssystem::NcaHeader::FsCountMax; ++i) {
|
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)) {
|
if (R_SUCCEEDED(res)) {
|
||||||
ctx->has_sections[i] = true;
|
ctx->has_sections[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;
|
||||||
|
|
||||||
/* Try to mount the section. */
|
/* Try to mount the section. */
|
||||||
const auto fs_type = ctx->header_readers[i].GetFsType();
|
const auto fs_type = ctx->header_readers[i].GetFsType();
|
||||||
switch (fs_type) {
|
switch (fs_type) {
|
||||||
@ -184,11 +190,14 @@ namespace ams::hactool {
|
|||||||
fprintf(stderr, "[Warning]: NCA section %d has unknown section type %d\n", i, static_cast<int>(fs_type));
|
fprintf(stderr, "[Warning]: NCA section %d has unknown section type %d\n", i, static_cast<int>(fs_type));
|
||||||
break;
|
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)) {
|
} else if (fs::ResultPartitionNotFound::Includes(res)) {
|
||||||
ctx->has_sections[i] = false;
|
ctx->has_sections[i] = false;
|
||||||
} else {
|
} else {
|
||||||
/* TODO: Should we stop here instead of pretending the NCA doesn't have this section? */
|
/* 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->PrintMagic(ctx.reader->GetMagic());
|
||||||
this->PrintHex("HeaderSign1 Key Generation", ctx.reader->GetHeaderSign1KeyGeneration());
|
this->PrintHex("HeaderSign1 Key Generation", ctx.reader->GetHeaderSign1KeyGeneration());
|
||||||
this->PrintBytes("HeaderSign1:", raw_header.header_sign_1, sizeof(raw_header.header_sign_1));
|
if (!m_options.verify) {
|
||||||
this->PrintBytes("HeaderSign2:", raw_header.header_sign_2, sizeof(raw_header.header_sign_2));
|
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());
|
this->PrintHex12("Content Size", ctx.reader->GetContentSize());
|
||||||
|
|
||||||
union {
|
union {
|
||||||
@ -343,11 +377,6 @@ namespace ams::hactool {
|
|||||||
|
|
||||||
/* Process sections. */
|
/* Process sections. */
|
||||||
for (s32 i = 0; i < fssystem::NcaHeader::FsCountMax; ++i) {
|
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. */
|
/* TODO: Save section as file, including raw. */
|
||||||
{
|
{
|
||||||
/* Determine path to save to. */
|
/* Determine path to save to. */
|
||||||
@ -364,9 +393,13 @@ namespace ams::hactool {
|
|||||||
|
|
||||||
/* If we have a path, save to it. */
|
/* If we have a path, save to it. */
|
||||||
if (path != nullptr) {
|
if (path != nullptr) {
|
||||||
|
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());
|
SaveToFile(m_local_fs, path, ctx.sections[i].get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Extract section to directory. */
|
/* Extract section to directory. */
|
||||||
if (ctx.is_mounted[i]) {
|
if (ctx.is_mounted[i]) {
|
||||||
|
@ -606,7 +606,24 @@ namespace ams::hactool {
|
|||||||
|
|
||||||
this->PrintHex("Sign Key Generation", ctx.npdm->signature_key_generation);
|
this->PrintHex("Sign Key Generation", ctx.npdm->signature_key_generation);
|
||||||
|
|
||||||
|
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("Signature", ctx.acid->signature, sizeof(ctx.acid->signature));
|
||||||
|
}
|
||||||
|
|
||||||
this->PrintBytes("HeaderSign2 Modulus", ctx.acid->modulus, sizeof(ctx.acid->modulus));
|
this->PrintBytes("HeaderSign2 Modulus", ctx.acid->modulus, sizeof(ctx.acid->modulus));
|
||||||
|
|
||||||
this->PrintHex2("Flags", ctx.acid->flags);
|
this->PrintHex2("Flags", ctx.acid->flags);
|
||||||
|
Loading…
Reference in New Issue
Block a user