mirror of
https://github.com/DarkStore-3DS/Project_CTR.git
synced 2026-07-03 00:39:14 +00:00
Merge pull request #118 from 3DSGuy/ctrtool-silence-output
Add support for silencing CTRTool output
This commit is contained in:
+33
-18
@@ -171,10 +171,14 @@ void ctrtool::CciProcess::importHeader()
|
|||||||
ctrtool::KeyBag::Aes128Key initial_data_key;
|
ctrtool::KeyBag::Aes128Key initial_data_key;
|
||||||
bool initial_data_key_available = false;
|
bool initial_data_key_available = false;
|
||||||
|
|
||||||
|
// crypto_type 3 zeros initial_data key (used in developer ROMs only)
|
||||||
|
if (mHeader.card_info.flag.crypto_type == ntd::n3ds::CciHeader::CryptoType_FixedKey)
|
||||||
|
{
|
||||||
|
memset(initial_data_key.data(), 0, initial_data_key.size());
|
||||||
|
initial_data_key_available = true;
|
||||||
|
}
|
||||||
// crypto_type 0-2 is the normal "secure" initial data key
|
// crypto_type 0-2 is the normal "secure" initial data key
|
||||||
if (mHeader.card_info.flag.crypto_type == ntd::n3ds::CciHeader::CryptoType_Secure0 ||
|
else
|
||||||
mHeader.card_info.flag.crypto_type == ntd::n3ds::CciHeader::CryptoType_Secure1 ||
|
|
||||||
mHeader.card_info.flag.crypto_type == ntd::n3ds::CciHeader::CryptoType_Secure2)
|
|
||||||
{
|
{
|
||||||
if (mKeyBag.brom_static_key_x.find(mKeyBag.KEYSLOT_INITIAL_DATA) != mKeyBag.brom_static_key_x.end())
|
if (mKeyBag.brom_static_key_x.find(mKeyBag.KEYSLOT_INITIAL_DATA) != mKeyBag.brom_static_key_x.end())
|
||||||
{
|
{
|
||||||
@@ -186,16 +190,6 @@ void ctrtool::CciProcess::importHeader()
|
|||||||
initial_data_key_available = false;
|
initial_data_key_available = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// crypto_type 3 zeros initial_data key (used in developer roms mostly)
|
|
||||||
else if (mHeader.card_info.flag.crypto_type == ntd::n3ds::CciHeader::CryptoType_FixedKey)
|
|
||||||
{
|
|
||||||
memset(initial_data_key.data(), 0, initial_data_key.size());
|
|
||||||
initial_data_key_available = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fmt::print("[WARNING] Unsupported CardInfo::CryptoType ({})\n", (uint32_t)mHeader.card_info.flag.crypto_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initial_data_key_available)
|
if (initial_data_key_available)
|
||||||
{
|
{
|
||||||
@@ -212,6 +206,16 @@ void ctrtool::CciProcess::importHeader()
|
|||||||
{
|
{
|
||||||
mDecryptedTitleKey = decrypted_title_key;
|
mDecryptedTitleKey = decrypted_title_key;
|
||||||
}
|
}
|
||||||
|
// Since CCM mode decrypts AND verifies, we should process the result here if required
|
||||||
|
if (mVerify)
|
||||||
|
{
|
||||||
|
mValidInitialDataMac = dec_result == 0 ? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
|
if (mValidInitialDataMac != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] InitialData MAC was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// test encrypt
|
// test encrypt
|
||||||
@@ -224,6 +228,11 @@ void ctrtool::CciProcess::importHeader()
|
|||||||
|
|
||||||
mbedtls_ccm_free(&ccm_ctx);
|
mbedtls_ccm_free(&ccm_ctx);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// no initial data key
|
||||||
|
fmt::print(stderr, "[{} ERROR] Failed to determine key to decrypt InitialData.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
|
||||||
// open fs reader
|
// open fs reader
|
||||||
mFsReader = std::shared_ptr<tc::io::VirtualFileSystem>(new tc::io::VirtualFileSystem(ntd::n3ds::CciFsShapshotGenerator(mInputStream)));
|
mFsReader = std::shared_ptr<tc::io::VirtualFileSystem>(new tc::io::VirtualFileSystem(ntd::n3ds::CciFsShapshotGenerator(mInputStream)));
|
||||||
@@ -243,11 +252,14 @@ void ctrtool::CciProcess::verifyHeader()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Could not read static CFA_CCI public key.\n");
|
fmt::print(stderr, "[{} ERROR] Could not load CCI RSA2048 public key.\n", mModuleLabel);
|
||||||
mValidSignature = ValidState::Fail;
|
mValidSignature = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
mValidInitialDataMac = mDecryptedTitleKey.isSet() ? ValidState::Good : ValidState::Fail;
|
if (mValidSignature != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for NcsdCommonHeader was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctrtool::CciProcess::printHeader()
|
void ctrtool::CciProcess::printHeader()
|
||||||
@@ -349,7 +361,10 @@ void ctrtool::CciProcess::extractFs()
|
|||||||
// build out path
|
// build out path
|
||||||
out_path = mExtractPath.get() + *itr;
|
out_path = mExtractPath.get() + *itr;
|
||||||
|
|
||||||
fmt::print("Saving {}...\n", out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving {}...\n", mModuleLabel, out_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// begin export
|
// begin export
|
||||||
mFsReader->openFile(*itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream);
|
mFsReader->openFile(*itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream);
|
||||||
@@ -362,7 +377,7 @@ void ctrtool::CciProcess::extractFs()
|
|||||||
cache_read_len = in_stream->read(cache.data(), cache.size());
|
cache_read_len = in_stream->read(cache.data(), cache.size());
|
||||||
if (cache_read_len == 0)
|
if (cache_read_len == 0)
|
||||||
{
|
{
|
||||||
throw tc::io::IOException(mModuleLabel, "Failed to read from RomFs file.");
|
throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from \"{}\".", (std::string)(dir.abs_path + *itr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
out_stream->write(cache.data(), cache_read_len);
|
out_stream->write(cache.data(), cache_read_len);
|
||||||
@@ -376,7 +391,7 @@ void ctrtool::CciProcess::processContent()
|
|||||||
{
|
{
|
||||||
if (mContentIndex >= ntd::n3ds::NcsdCommonHeader::kPartitionNum)
|
if (mContentIndex >= ntd::n3ds::NcsdCommonHeader::kPartitionNum)
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Content index {:d} isn't valid for CCI, use index 0-7, defaulting to 0 now.\n", mContentIndex);
|
fmt::print(stderr, "[{} ERROR] Content index {:d} isn't valid for CCI, use index 0-7, defaulting to 0 now.\n", mModuleLabel, mContentIndex);
|
||||||
mContentIndex = 0;
|
mContentIndex = 0;
|
||||||
}
|
}
|
||||||
if (mHeader.ncsd_header.partition_offsetsize[mContentIndex].blk_size.unwrap() != 0)
|
if (mHeader.ncsd_header.partition_offsetsize[mContentIndex].blk_size.unwrap() != 0)
|
||||||
|
|||||||
+91
-45
@@ -13,7 +13,6 @@ ctrtool::CiaProcess::CiaProcess() :
|
|||||||
mInputStream(),
|
mInputStream(),
|
||||||
mKeyBag(),
|
mKeyBag(),
|
||||||
mShowHeaderInfo(false),
|
mShowHeaderInfo(false),
|
||||||
mShowFs(false),
|
|
||||||
mVerbose(false),
|
mVerbose(false),
|
||||||
mVerify(false),
|
mVerify(false),
|
||||||
mCertExtractPath(),
|
mCertExtractPath(),
|
||||||
@@ -49,10 +48,9 @@ void ctrtool::CiaProcess::setKeyBag(const ctrtool::KeyBag& key_bag)
|
|||||||
mNcchProcess.setKeyBag(key_bag);
|
mNcchProcess.setKeyBag(key_bag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctrtool::CiaProcess::setCliOutputMode(bool show_header_info, bool show_fs)
|
void ctrtool::CiaProcess::setCliOutputMode(bool show_header_info)
|
||||||
{
|
{
|
||||||
mShowHeaderInfo = show_header_info;
|
mShowHeaderInfo = show_header_info;
|
||||||
mShowFs = show_fs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctrtool::CiaProcess::setVerboseMode(bool verbose)
|
void ctrtool::CiaProcess::setVerboseMode(bool verbose)
|
||||||
@@ -288,11 +286,6 @@ void ctrtool::CiaProcess::importHeader()
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
// TODO load certificates from KeyBag
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fmt::print("[LOG] CIA has no Certificate, cannot verify Ticket or TitleMetaData.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTikSizeInfo.size > 0)
|
if (mTikSizeInfo.size > 0)
|
||||||
{
|
{
|
||||||
@@ -301,12 +294,18 @@ void ctrtool::CiaProcess::importHeader()
|
|||||||
// determine title key
|
// determine title key
|
||||||
if (mKeyBag.fallback_title_key.isSet())
|
if (mKeyBag.fallback_title_key.isSet())
|
||||||
{
|
{
|
||||||
fmt::print("[LOG] Using fallback titlekey.\n");
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Using fallback titlekey.\n", mModuleLabel);
|
||||||
|
}
|
||||||
mDecryptedTitleKey = mKeyBag.fallback_title_key.get();
|
mDecryptedTitleKey = mKeyBag.fallback_title_key.get();
|
||||||
}
|
}
|
||||||
else if (mKeyBag.common_key.find(mTicket.key_id) != mKeyBag.common_key.end())
|
else if (mKeyBag.common_key.find(mTicket.key_id) != mKeyBag.common_key.end())
|
||||||
{
|
{
|
||||||
fmt::print("[LOG] Decrypting titlekey from ticket.\n");
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Decrypting titlekey from ticket.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
|
||||||
// get common key
|
// get common key
|
||||||
auto common_key = mKeyBag.common_key[mTicket.key_id];
|
auto common_key = mKeyBag.common_key[mTicket.key_id];
|
||||||
@@ -324,7 +323,7 @@ void ctrtool::CiaProcess::importHeader()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print("[LOG] Cannot determine titlekey.\n");
|
fmt::print(stderr, "[{} ERROR] Cannot determine titlekey.\n", mModuleLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -405,72 +404,97 @@ void ctrtool::CiaProcess::verifyMetadata()
|
|||||||
// verify cert
|
// verify cert
|
||||||
for (size_t i = 0; i < mCertChain.size(); i++)
|
for (size_t i = 0; i < mCertChain.size(); i++)
|
||||||
{
|
{
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mCertChain[i].signature.issuer);
|
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mCertChain[i].signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mCertChain[i].signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mCertChain[i].signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
||||||
{
|
|
||||||
mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mCertSigValid[i] = local_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mCertSigValid[i] = local_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
||||||
|
{
|
||||||
|
// only show this warning for non-root signed certificates
|
||||||
|
if (mCertChain[i].signature.issuer != "Root")
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Public key \"{}\" (for certificate \"{}\") was not present in the CIA certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mCertChain[i].signature.issuer, mCertChain[i].subject);
|
||||||
|
}
|
||||||
|
mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (certificate).\n", mCertChain[i].signature.issuer);
|
fmt::print(stderr, "[{} ERROR] Could not locate public key for \"{}\" (certificate).\n", mModuleLabel, mCertChain[i].signature.issuer);
|
||||||
mCertSigValid[i] = ValidState::Fail;
|
mCertSigValid[i] = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log certificate signature validation error
|
||||||
|
if (mCertSigValid[i] != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for Certificate \"{}\" was invalid.\n", mModuleLabel, mCertChain[i].signature.issuer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mHeader.format_version.unwrap() == ntd::n3ds::CiaHeader::FormatVersion_Default && mTikSizeInfo.size > 0)
|
if (mHeader.format_version.unwrap() == ntd::n3ds::CiaHeader::FormatVersion_Default && mTikSizeInfo.size > 0)
|
||||||
{
|
{
|
||||||
// verify ticket
|
// verify ticket
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mTicket.signature.issuer);
|
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTicket.signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTicket.signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mTicket.signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
||||||
{
|
|
||||||
mTicketSigValid = keybag_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mTicketSigValid = local_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mTicketSigValid = local_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Public key \"{}\" (for ticket) was not present in the CIA certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mTicket.signature.issuer);
|
||||||
|
mTicketSigValid = keybag_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (ticket).\n", mTicket.signature.issuer);
|
fmt::print(stderr, "[{} ERROR] Could not locate public key \"{}\" (for ticket).\n", mModuleLabel, mTicket.signature.issuer);
|
||||||
mTicketSigValid = ValidState::Fail;
|
mTicketSigValid = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log ticket signature validation error
|
||||||
|
if (mTicketSigValid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for Ticket was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mHeader.format_version.unwrap() == ntd::n3ds::CiaHeader::FormatVersion_Default && mTmdSizeInfo.size > 0)
|
if (mHeader.format_version.unwrap() == ntd::n3ds::CiaHeader::FormatVersion_Default && mTmdSizeInfo.size > 0)
|
||||||
{
|
{
|
||||||
// verify tmd
|
// verify tmd
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mTitleMetaData.signature.issuer);
|
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTitleMetaData.signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTitleMetaData.signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mTitleMetaData.signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
||||||
{
|
|
||||||
mTitleMetaDataSigValid = keybag_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mTitleMetaDataSigValid = local_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mTitleMetaDataSigValid = local_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Public key \"{}\" (for tmd) was not present in the CIA certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mTitleMetaData.signature.issuer);
|
||||||
|
mTitleMetaDataSigValid = keybag_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (tmd).\n", mTitleMetaData.signature.issuer);
|
fmt::print(stderr, "[{} ERROR] Could not locate public key \"{}\" (for tmd).\n", mModuleLabel, mTitleMetaData.signature.issuer);
|
||||||
mTitleMetaDataSigValid = ValidState::Fail;
|
mTitleMetaDataSigValid = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log tmd signature validation error
|
||||||
|
if (mTitleMetaDataSigValid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for TitleMetaData was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -519,6 +543,11 @@ void ctrtool::CiaProcess::verifyContent()
|
|||||||
sha256_calc.getHash(sha256_hash.data());
|
sha256_calc.getHash(sha256_hash.data());
|
||||||
|
|
||||||
itr->second.valid_state = memcmp(sha256_hash.data(), itr->second.hash.data(), sha256_hash.size()) == 0 ? ValidState::Good : ValidState::Fail;
|
itr->second.valid_state = memcmp(sha256_hash.data(), itr->second.hash.data(), sha256_hash.size()) == 0 ? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
|
if (itr->second.valid_state != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Hash for content (index=0x{:04x}, id=0x{:08x}) was invalid.\n", mModuleLabel, itr->second.cindex, itr->second.cid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -673,7 +702,11 @@ void ctrtool::CiaProcess::extractCia()
|
|||||||
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mCertSizeInfo.offset, mCertSizeInfo.size));
|
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mCertSizeInfo.offset, mCertSizeInfo.size));
|
||||||
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
||||||
|
|
||||||
fmt::print("Saving certs to {}...\n", out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving certs to {}...\n", mModuleLabel, out_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
copyStream(in_stream, out_stream);
|
copyStream(in_stream, out_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -684,7 +717,10 @@ void ctrtool::CiaProcess::extractCia()
|
|||||||
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mTikSizeInfo.offset, mTikSizeInfo.size));
|
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mTikSizeInfo.offset, mTikSizeInfo.size));
|
||||||
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
||||||
|
|
||||||
fmt::print("Saving tik to {}...\n", out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving tik to {}...\n", mModuleLabel, out_path.to_string());
|
||||||
|
}
|
||||||
copyStream(in_stream, out_stream);
|
copyStream(in_stream, out_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -695,7 +731,10 @@ void ctrtool::CiaProcess::extractCia()
|
|||||||
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mTmdSizeInfo.offset, mTmdSizeInfo.size));
|
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mTmdSizeInfo.offset, mTmdSizeInfo.size));
|
||||||
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
||||||
|
|
||||||
fmt::print("Saving tmd to {}...\n", out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving tmd to {}...\n", mModuleLabel, out_path.to_string());
|
||||||
|
}
|
||||||
copyStream(in_stream, out_stream);
|
copyStream(in_stream, out_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,7 +745,10 @@ void ctrtool::CiaProcess::extractCia()
|
|||||||
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mFooterSizeInfo.offset, mFooterSizeInfo.size));
|
in_stream = std::shared_ptr<tc::io::SubStream>(new tc::io::SubStream(mInputStream, mFooterSizeInfo.offset, mFooterSizeInfo.size));
|
||||||
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
||||||
|
|
||||||
fmt::print("Saving meta to {}...\n", out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving meta to {}...\n", mModuleLabel, out_path.to_string());
|
||||||
|
}
|
||||||
copyStream(in_stream, out_stream);
|
copyStream(in_stream, out_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -729,7 +771,11 @@ void ctrtool::CiaProcess::extractCia()
|
|||||||
|
|
||||||
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
out_stream = std::shared_ptr<tc::io::FileStream>(new tc::io::FileStream(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write));
|
||||||
|
|
||||||
fmt::print("Saving content {:04x} to {}...\n", itr->second.cindex, out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving content {:04x} to {}...\n", mModuleLabel, itr->second.cindex, out_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
copyStream(in_stream, out_stream);
|
copyStream(in_stream, out_stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -760,7 +806,7 @@ void ctrtool::CiaProcess::processContent()
|
|||||||
{
|
{
|
||||||
if (mContentIndex >= ntd::n3ds::CiaHeader::kCiaMaxContentNum)
|
if (mContentIndex >= ntd::n3ds::CiaHeader::kCiaMaxContentNum)
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Content index {:d} isn't valid for CIA, use index 0-{:d}, defaulting to 0 now.\n", mContentIndex, ((size_t)ntd::n3ds::CiaHeader::kCiaMaxContentNum)-1);
|
fmt::print(stderr, "[{} ERROR] Content index {:d} isn't valid for CIA, use index 0-{:d}, defaulting to 0 now.\n", mModuleLabel, mContentIndex, ((size_t)ntd::n3ds::CiaHeader::kCiaMaxContentNum)-1);
|
||||||
mContentIndex = 0;
|
mContentIndex = 0;
|
||||||
}
|
}
|
||||||
if (mContentInfo.find(mContentIndex) != mContentInfo.end() && mContentInfo[mContentIndex].size != 0)
|
if (mContentInfo.find(mContentIndex) != mContentInfo.end() && mContentInfo[mContentIndex].size != 0)
|
||||||
@@ -782,7 +828,7 @@ void ctrtool::CiaProcess::processContent()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print("[LOG] TWL title processing not supported\n");
|
throw tc::NotImplementedException(mModuleLabel, "TWL title processing not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public:
|
|||||||
|
|
||||||
void setInputStream(const std::shared_ptr<tc::io::IStream>& input_stream);
|
void setInputStream(const std::shared_ptr<tc::io::IStream>& input_stream);
|
||||||
void setKeyBag(const ctrtool::KeyBag& key_bag);
|
void setKeyBag(const ctrtool::KeyBag& key_bag);
|
||||||
void setCliOutputMode(bool show_header_info, bool show_fs);
|
void setCliOutputMode(bool show_header_info);
|
||||||
void setVerboseMode(bool verbose);
|
void setVerboseMode(bool verbose);
|
||||||
void setVerifyMode(bool verify);
|
void setVerifyMode(bool verify);
|
||||||
void setCertExtractPath(const tc::io::Path& extract_path);
|
void setCertExtractPath(const tc::io::Path& extract_path);
|
||||||
@@ -44,7 +44,6 @@ private:
|
|||||||
std::shared_ptr<tc::io::IStream> mInputStream;
|
std::shared_ptr<tc::io::IStream> mInputStream;
|
||||||
ctrtool::KeyBag mKeyBag;
|
ctrtool::KeyBag mKeyBag;
|
||||||
bool mShowHeaderInfo;
|
bool mShowHeaderInfo;
|
||||||
bool mShowFs;
|
|
||||||
bool mVerbose;
|
bool mVerbose;
|
||||||
bool mVerify;
|
bool mVerify;
|
||||||
tc::Optional<tc::io::Path> mCertExtractPath;
|
tc::Optional<tc::io::Path> mCertExtractPath;
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ void ctrtool::CrrProcess::verifyData()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Could not read static CRR public key.\n");
|
fmt::print(stderr, "[{} ERROR] Could not read static CRR public key.\n", mModuleLabel);
|
||||||
mValidCertificateSignature = ValidState::Fail;
|
mValidCertificateSignature = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,6 +145,20 @@ void ctrtool::CrrProcess::verifyData()
|
|||||||
{
|
{
|
||||||
mValidUniqueId = ((mBodyHeader.unique_id.unwrap() & mHeader.body_certificate.unique_id_mask.unwrap()) == 0) ? ValidState::Good : ValidState::Fail;
|
mValidUniqueId = ((mBodyHeader.unique_id.unwrap() & mHeader.body_certificate.unique_id_mask.unwrap()) == 0) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log validation errors
|
||||||
|
if (mValidCertificateSignature != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for CRR Certificate was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
if (mValidBodySignature != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for CRR Body was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
if (mValidUniqueId != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] CRR UniqueId was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctrtool::CrrProcess::printData()
|
void ctrtool::CrrProcess::printData()
|
||||||
|
|||||||
@@ -96,13 +96,18 @@ void ctrtool::ExHeaderProcess::verifyExHeader()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Could not read AccessDescriptor public key.\n");
|
fmt::print(stderr, "[{} ERROR] Could not load AccessDescriptor RSA2048 public key.\n", mModuleLabel);
|
||||||
mValidSignature = ValidState::Fail;
|
mValidSignature = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mValidSignature != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for AccessDescriptor was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
|
||||||
mValidLocalCaps.system_save_id[0] = ValidState::Good;
|
mValidLocalCaps.system_save_id[0] = ValidState::Good;
|
||||||
mValidLocalCaps.system_save_id[1] = ValidState::Good;
|
mValidLocalCaps.system_save_id[1] = ValidState::Good;
|
||||||
mValidLocalCaps.access_info = ValidState::Good;
|
mValidLocalCaps.fs_access = ValidState::Good;
|
||||||
mValidLocalCaps.core_version = ValidState::Good;
|
mValidLocalCaps.core_version = ValidState::Good;
|
||||||
mValidLocalCaps.program_id = ValidState::Good;
|
mValidLocalCaps.program_id = ValidState::Good;
|
||||||
mValidLocalCaps.priority = ValidState::Good;
|
mValidLocalCaps.priority = ValidState::Good;
|
||||||
@@ -183,10 +188,10 @@ void ctrtool::ExHeaderProcess::verifyExHeader()
|
|||||||
{
|
{
|
||||||
if (exhdr_fs_access.test(fs_bit) == true && desc_fs_access.test(fs_bit) == false)
|
if (exhdr_fs_access.test(fs_bit) == true && desc_fs_access.test(fs_bit) == false)
|
||||||
{
|
{
|
||||||
mValidLocalCaps.access_info = ValidState::Fail;
|
mValidLocalCaps.fs_access = ValidState::Fail;
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/ExHeader] FsAccess Bit {:d} was not permitted\n", fs_bit);
|
fmt::print(stderr, "[{} ERROR] FsAccess Bit {:d} was not permitted\n", mModuleLabel, fs_bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,10 +218,65 @@ void ctrtool::ExHeaderProcess::verifyExHeader()
|
|||||||
mValidLocalCaps.service_control = Fail;
|
mValidLocalCaps.service_control = Fail;
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/ExHeader] Service \"{}\" was not permitted\n", exhdr_service_access_control[i].decode());
|
fmt::print(stderr, "[{} ERROR] Service \"{}\" was not permitted\n", mModuleLabel, exhdr_service_access_control[i].decode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mValidLocalCaps.system_save_id[0] != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemSaveId1");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.system_save_id[1] != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemSaveId2");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.fs_access != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "FsAccess");
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (mValidLocalCaps.core_version != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "CoreVersion");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (mValidLocalCaps.program_id != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ProgramId");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.priority != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ThreadPriority");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.affinity_mask != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "AffinityMask");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.ideal_processor != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "IdealProcessor");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.old3ds_system_mode != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemMode (Old3DS)");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.new3ds_system_mode != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemMode (New3DS)");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.enable_l2_cache != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "EnableL2Cache");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.new3ds_cpu_speed != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "CpuSpeed");
|
||||||
|
}
|
||||||
|
if (mValidLocalCaps.service_control != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ServiceAccess");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctrtool::ExHeaderProcess::printExHeader()
|
void ctrtool::ExHeaderProcess::printExHeader()
|
||||||
@@ -355,7 +415,7 @@ void ctrtool::ExHeaderProcess::printARM11SystemLocalCapabilities(const ntd::n3ds
|
|||||||
}
|
}
|
||||||
fmt::print("Other Variation Saves: {}\n", (use_other_variation_savedata ? "Accessible" : "Inaccessible"));
|
fmt::print("Other Variation Saves: {}\n", (use_other_variation_savedata ? "Accessible" : "Inaccessible"));
|
||||||
uint64_t fs_access_raw = ((((tc::bn::le64<uint64_t>*)&info.fs_access)->unwrap() << 8) >> 8); // clearing the upper 8 bits since fs_access is 56 bits
|
uint64_t fs_access_raw = ((((tc::bn::le64<uint64_t>*)&info.fs_access)->unwrap() << 8) >> 8); // clearing the upper 8 bits since fs_access is 56 bits
|
||||||
fmt::print("Access info: {:6} 0x{:014x}\n", getValidString(valid.access_info), fs_access_raw);
|
fmt::print("FS access: {:6} 0x{:014x}\n", getValidString(valid.fs_access), fs_access_raw);
|
||||||
for (size_t i = 0; i < info.fs_access.bit_size(); i++)
|
for (size_t i = 0; i < info.fs_access.bit_size(); i++)
|
||||||
{
|
{
|
||||||
if (info.fs_access.test(i))
|
if (info.fs_access.test(i))
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ private:
|
|||||||
{
|
{
|
||||||
system_save_id[0] = ValidState::Unchecked;
|
system_save_id[0] = ValidState::Unchecked;
|
||||||
system_save_id[1] = ValidState::Unchecked;
|
system_save_id[1] = ValidState::Unchecked;
|
||||||
access_info = ValidState::Unchecked;
|
fs_access = ValidState::Unchecked;
|
||||||
core_version = ValidState::Unchecked;
|
core_version = ValidState::Unchecked;
|
||||||
program_id = ValidState::Unchecked;
|
program_id = ValidState::Unchecked;
|
||||||
priority = ValidState::Unchecked;
|
priority = ValidState::Unchecked;
|
||||||
@@ -53,7 +53,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::array<byte_t, 2> system_save_id;
|
std::array<byte_t, 2> system_save_id;
|
||||||
byte_t access_info;
|
byte_t fs_access;
|
||||||
byte_t core_version;
|
byte_t core_version;
|
||||||
byte_t program_id;
|
byte_t program_id;
|
||||||
byte_t priority;
|
byte_t priority;
|
||||||
|
|||||||
@@ -130,11 +130,11 @@ void ctrtool::ExeFsProcess::verifyFs()
|
|||||||
}
|
}
|
||||||
hash_calc.getHash(hash.data());
|
hash_calc.getHash(hash.data());
|
||||||
|
|
||||||
mSectionValidation[i] = memcmp(hash.data(), hdr_hash.data(), hash.size()) == 0? Good : Fail;
|
mSectionValidation[i] = memcmp(hash.data(), hdr_hash.data(), hash.size()) == 0? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
if (mVerbose)
|
if (mSectionValidation[i] != ValidState::Good)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/ExeFs] File: \"{}\" {} hash validation\n", mHeader.file_table[i].name.decode(), (mSectionValidation[i] == ValidState::Good ? "passed" : "failed"));
|
fmt::print(stderr, "[{} ERROR] ExeFs file \"{}\" had an invalid SHA2-256 hash.\n", mModuleLabel, mHeader.file_table[i].name.decode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,10 @@ void ctrtool::ExeFsProcess::extractFs()
|
|||||||
}
|
}
|
||||||
if (test_hash != nullptr && memcmp(test_hash, hash.data(), hash.size()) == 0)
|
if (test_hash != nullptr && memcmp(test_hash, hash.data(), hash.size()) == 0)
|
||||||
{
|
{
|
||||||
fmt::print("Decompressing section {} to {}...\n", *itr, f_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Decompressing {} to {}...\n", mModuleLabel, *itr, f_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
tc::ByteData decompdata = tc::ByteData(lzss_get_decompressed_size(compdata.data(), compdata.size()));
|
tc::ByteData decompdata = tc::ByteData(lzss_get_decompressed_size(compdata.data(), compdata.size()));
|
||||||
lzss_decompress(compdata.data(), compdata.size(), decompdata.data(), decompdata.size());
|
lzss_decompress(compdata.data(), compdata.size(), decompdata.data(), decompdata.size());
|
||||||
@@ -233,7 +236,10 @@ void ctrtool::ExeFsProcess::extractFs()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print("Saving section {} to {}...\n", *itr, f_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving {} to {}...\n", mModuleLabel, *itr, f_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
out_stream->seek(0, tc::io::SeekOrigin::Begin);
|
out_stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
out_stream->write(compdata.data(), compdata.size());
|
out_stream->write(compdata.data(), compdata.size());
|
||||||
@@ -241,7 +247,10 @@ void ctrtool::ExeFsProcess::extractFs()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print("Saving section {} to {}...\n", *itr, f_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving {} to {}...\n", mModuleLabel, *itr, f_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
tc::ByteData filedata = tc::ByteData(in_stream->length());
|
tc::ByteData filedata = tc::ByteData(in_stream->length());
|
||||||
in_stream->seek(0, tc::io::SeekOrigin::Begin);
|
in_stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
|
|||||||
@@ -188,6 +188,11 @@ void ctrtool::FirmProcess::verifyHashes()
|
|||||||
hash_calc.getHash(hash.data());
|
hash_calc.getHash(hash.data());
|
||||||
|
|
||||||
mValidFirmSectionHash[i] = memcmp(hash.data(), hdr_hash.data(), hash.size()) == 0? ValidState::Good : ValidState::Fail;
|
mValidFirmSectionHash[i] = memcmp(hash.data(), hdr_hash.data(), hash.size()) == 0? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
|
if (mValidFirmSectionHash[i] != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] FIRM section {:d} SHA2-256 hash was invalid.\n", mModuleLabel, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,7 +231,7 @@ void ctrtool::FirmProcess::verifySignature()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Could not read static rsa_key {}.\n", key_id == mKeyBag.RSAKEY_FIRM_NAND ? "RSAKEY_FIRM_NAND" : "RSAKEY_FIRM_RECOVERY");
|
fmt::print(stderr, "[{} ERROR] Could not load {} RSA2048 public key.\n", mModuleLabel, key_id == mKeyBag.RSAKEY_FIRM_NAND ? "FIRM_NAND" : "FIRM_RECOVERY");
|
||||||
valid_signature = ValidState::Fail;
|
valid_signature = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +244,7 @@ void ctrtool::FirmProcess::verifySignature()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Could not read rsa_sighax_signature for {}.\n", key_id == mKeyBag.RSAKEY_FIRM_NAND ? "RSAKEY_FIRM_NAND" : "RSAKEY_FIRM_RECOVERY");
|
fmt::print(stderr, "[{} ERROR] Could not load {} SigHax RSA2048 signature.\n", mModuleLabel, key_id == mKeyBag.RSAKEY_FIRM_NAND ? "FIRM_NAND" : "FIRM_RECOVERY");
|
||||||
is_sighax = false;
|
is_sighax = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,10 +256,12 @@ void ctrtool::FirmProcess::verifySignature()
|
|||||||
// check if sighax
|
// check if sighax
|
||||||
else if (valid_signature == ValidState::Fail && is_sighax == true)
|
else if (valid_signature == ValidState::Fail && is_sighax == true)
|
||||||
{
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for FIRM was invalid (SigHax).\n", mModuleLabel);
|
||||||
mSignatureState = SignatureState_SigHax;
|
mSignatureState = SignatureState_SigHax;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for FIRM was invalid.\n", mModuleLabel);
|
||||||
mSignatureState = SignatureState_Fail;
|
mSignatureState = SignatureState_Fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,7 +311,10 @@ void ctrtool::FirmProcess::extractSections()
|
|||||||
local_fs.createDirectory(mExtractPath.get());
|
local_fs.createDirectory(mExtractPath.get());
|
||||||
local_fs.openFile(f_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream);
|
local_fs.openFile(f_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream);
|
||||||
|
|
||||||
fmt::print("Saving section {} to {}...\n", i, f_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving section {} to {}...\n", mModuleLabel, i, f_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
tc::ByteData filedata = tc::ByteData(in_stream->length());
|
tc::ByteData filedata = tc::ByteData(in_stream->length());
|
||||||
in_stream->seek(0, tc::io::SeekOrigin::Begin);
|
in_stream->seek(0, tc::io::SeekOrigin::Begin);
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ void ctrtool::IvfcProcess::verifyLevels()
|
|||||||
{
|
{
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/IVFC] IVFC Layer {:d}, Block {:d} failed validation\n", i, j);
|
fmt::print(stderr, "[{} ERROR] IVFC Layer {:d}, Block {:d} failed validation.\n", mModuleLabel, i, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -138,9 +138,10 @@ void ctrtool::IvfcProcess::verifyLevels()
|
|||||||
}
|
}
|
||||||
|
|
||||||
mLevelValidation[i] = bad_blocks == 0? ValidState::Good : ValidState::Fail;
|
mLevelValidation[i] = bad_blocks == 0? ValidState::Good : ValidState::Fail;
|
||||||
if (mVerbose)
|
|
||||||
|
if (mLevelValidation[i] != ValidState::Good)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/IVFC] IVFC Layer {:d} {} validation\n", i, (mLevelValidation[i] == ValidState::Good ? "passed" : "failed"));
|
fmt::print(stderr, "[{} ERROR] IVFC Layer {:d} failed validation.\n", mModuleLabel, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+52
-23
@@ -241,7 +241,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
}
|
}
|
||||||
if (crypto_is_stripped)
|
if (crypto_is_stripped)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/NCCH] NCCH appears to be decrypted, contrary to header flags.\n");
|
fmt::print(stderr, "[{} ERROR] NCCH appears to be decrypted, contrary to header flags.\n", mModuleLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine encryption mode
|
// determine encryption mode
|
||||||
@@ -287,7 +287,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
keyslot[0].valid_key = ValidState::Fail;
|
keyslot[0].valid_key = ValidState::Fail;
|
||||||
keyslot[1].valid_key = ValidState::Fail;
|
keyslot[1].valid_key = ValidState::Fail;
|
||||||
|
|
||||||
fmt::print(stderr, "Could not read {} fixed key.\n", (isSystemTitle()? "system" : "application"));
|
fmt::print(stderr, "[{} ERROR] Could not load {} fixed key.\n", mModuleLabel, (isSystemTitle()? "system" : "application"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// save keys
|
// save keys
|
||||||
@@ -307,7 +307,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
{
|
{
|
||||||
keyslot[0].valid_x = ValidState::Fail;
|
keyslot[0].valid_x = ValidState::Fail;
|
||||||
|
|
||||||
fmt::print(stderr, "Could not read secure key_x[0x{:02x}].\n", 0);
|
fmt::print(stderr, "[{} ERROR] Could not load secure key_x[0x{:02x}].\n", mModuleLabel, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -328,7 +328,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
{
|
{
|
||||||
keyslot[1].valid_x = ValidState::Fail;
|
keyslot[1].valid_x = ValidState::Fail;
|
||||||
|
|
||||||
fmt::print(stderr, "Could not read secure key_x[0x{:02x}].\n", security_version);
|
fmt::print(stderr, "[{} ERROR] Could not load secure key_x[0x{:02x}].\n", mModuleLabel, security_version);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -360,8 +360,8 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
{
|
{
|
||||||
keyslot[1].valid_y = ValidState::Fail;
|
keyslot[1].valid_y = ValidState::Fail;
|
||||||
|
|
||||||
fmt::print(stderr, "This title uses seed crypto, but no seed is set, unable to decrypt.\n");
|
fmt::print(stderr, "[{} ERROR] This title uses seed crypto, but no seed is set, unable to decrypt.\n", mModuleLabel);
|
||||||
fmt::print(stderr, "Use -p to avoid decryption or use --seeddb=dbfile or --seed=SEEDHERE.\n");
|
fmt::print(stderr, " Use -p to avoid decryption or use --seeddb=dbfile or --seed=SEEDHERE.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyslot[1].valid_y != ValidState::Fail)
|
if (keyslot[1].valid_y != ValidState::Fail)
|
||||||
@@ -375,7 +375,8 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
{
|
{
|
||||||
keyslot[1].valid_y = ValidState::Fail;
|
keyslot[1].valid_y = ValidState::Fail;
|
||||||
|
|
||||||
fmt::print(stderr, "Seed check mismatch. (Got {:08x}, expected: {:08x})\n",
|
fmt::print(stderr, "[{} ERROR] Seed check mismatch. (Got {:08x}, expected: {:08x})\n",
|
||||||
|
mModuleLabel,
|
||||||
((tc::bn::be32<uint32_t>*)hash.data())->unwrap(),
|
((tc::bn::be32<uint32_t>*)hash.data())->unwrap(),
|
||||||
((tc::bn::be32<uint32_t>*)mHeader.header.seed_checksum.data())->unwrap());
|
((tc::bn::be32<uint32_t>*)mHeader.header.seed_checksum.data())->unwrap());
|
||||||
}
|
}
|
||||||
@@ -424,8 +425,8 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
// output keys if required
|
// output keys if required
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/NCCH] NCCH AES Key0 {}\n", (keyslot[0].valid_key ? tc::cli::FormatUtil::formatBytesAsString(keyslot[0].key.data(), keyslot[0].key.size(), true, "") : "could not be determined"));
|
fmt::print(stderr, "[{} LOG] NCCH AES Key0 {}\n", mModuleLabel, (keyslot[0].valid_key ? tc::cli::FormatUtil::formatBytesAsString(keyslot[0].key.data(), keyslot[0].key.size(), true, "") : "could not be determined"));
|
||||||
fmt::print("[LOG/NCCH] NCCH AES Key1 {}\n", (keyslot[1].valid_key ? tc::cli::FormatUtil::formatBytesAsString(keyslot[1].key.data(), keyslot[1].key.size(), true, "") : "could not be determined"));
|
fmt::print(stderr, "[{} LOG] NCCH AES Key1 {}\n", mModuleLabel, (keyslot[1].valid_key ? tc::cli::FormatUtil::formatBytesAsString(keyslot[1].key.data(), keyslot[1].key.size(), true, "") : "could not be determined"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate aes counter
|
// generate aes counter
|
||||||
@@ -436,7 +437,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
|
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/NCCH] NCCH ExHeader AES Counter {}\n", tc::cli::FormatUtil::formatBytesAsString(exheader_aesctr.data(), exheader_aesctr.size(), true, ""));
|
fmt::print(stderr, "[{} LOG] NCCH ExHeader AES Counter {}\n", mModuleLabel, tc::cli::FormatUtil::formatBytesAsString(exheader_aesctr.data(), exheader_aesctr.size(), true, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mRegionInfo[NcchRegion_ExeFs].size)
|
if (mRegionInfo[NcchRegion_ExeFs].size)
|
||||||
@@ -445,7 +446,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
|
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/NCCH] NCCH ExeFS AES Counter {}\n", tc::cli::FormatUtil::formatBytesAsString(exefs_aesctr.data(), exefs_aesctr.size(), true, ""));
|
fmt::print(stderr, "[{} LOG] NCCH ExeFS AES Counter {}\n", mModuleLabel, tc::cli::FormatUtil::formatBytesAsString(exefs_aesctr.data(), exefs_aesctr.size(), true, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mRegionInfo[NcchRegion_RomFs].size)
|
if (mRegionInfo[NcchRegion_RomFs].size)
|
||||||
@@ -454,7 +455,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
|
|
||||||
if (mVerbose)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
fmt::print("[LOG/NCCH] NCCH RomFS AES Counter {}\n", tc::cli::FormatUtil::formatBytesAsString(romfs_aesctr.data(), romfs_aesctr.size(), true, ""));
|
fmt::print(stderr, "[{} LOG] NCCH RomFS AES Counter {}\n", mModuleLabel, tc::cli::FormatUtil::formatBytesAsString(romfs_aesctr.data(), romfs_aesctr.size(), true, ""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -531,7 +532,7 @@ void ctrtool::NcchProcess::determineRegionEncryption()
|
|||||||
// otherwise use the "best-effort" single key stream (only icon and banner will be decrypt properly)
|
// otherwise use the "best-effort" single key stream (only icon and banner will be decrypt properly)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print(stderr, "Only NCCH key0 was determined, so ExeFS will be partially decrypted\n");
|
fmt::print(stderr, "[{} ERROR] Only NCCH key0 was determined, ExeFS may be partially decrypted\n", mModuleLabel);
|
||||||
mRegionInfo[NcchRegion_ExeFs].ready_stream = std::shared_ptr<tc::crypto::Aes128CtrEncryptedStream>(new tc::crypto::Aes128CtrEncryptedStream(mRegionInfo[NcchRegion_ExeFs].raw_stream, keyslot[0].key, exefs_aesctr));
|
mRegionInfo[NcchRegion_ExeFs].ready_stream = std::shared_ptr<tc::crypto::Aes128CtrEncryptedStream>(new tc::crypto::Aes128CtrEncryptedStream(mRegionInfo[NcchRegion_ExeFs].raw_stream, keyslot[0].key, exefs_aesctr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -596,7 +597,7 @@ void ctrtool::NcchProcess::verifyRegions()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read CFA public key.\n");
|
fmt::print(stderr, "[{} ERROR] Could not load CFA RSA2048 public key.\n", mModuleLabel);
|
||||||
mRegionInfo[NcchRegion_Header].valid = ValidState::Fail;
|
mRegionInfo[NcchRegion_Header].valid = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,36 +622,60 @@ void ctrtool::NcchProcess::verifyRegions()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read CXI public key from AccessDescriptor.\n");
|
fmt::print(stderr, "[{} ERROR] Could not load CXI RSA2048 public key from AccessDescriptor.\n", mModuleLabel);
|
||||||
mRegionInfo[NcchRegion_Header].valid = ValidState::Fail;
|
mRegionInfo[NcchRegion_Header].valid = ValidState::Fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mRegionInfo[NcchRegion_Header].valid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for NcchHeader was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exheader hash
|
// exheader hash
|
||||||
if (mRegionInfo[NcchRegion_ExHeader].hashed_size > 0)
|
if (mRegionInfo[NcchRegion_ExHeader].hashed_size > 0)
|
||||||
{
|
{
|
||||||
mRegionInfo[NcchRegion_ExHeader].valid = memcmp(region_hashes[NcchRegion_ExHeader].data(), mHeader.header.exhdr_hash.data(), region_hashes[NcchRegion_ExHeader].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
mRegionInfo[NcchRegion_ExHeader].valid = memcmp(region_hashes[NcchRegion_ExHeader].data(), mHeader.header.exhdr_hash.data(), region_hashes[NcchRegion_ExHeader].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
|
if (mRegionInfo[NcchRegion_ExHeader].valid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] ExtendedHeader SHA2-256 hash was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logo hash
|
// logo hash
|
||||||
if (mRegionInfo[NcchRegion_Logo].hashed_size > 0)
|
if (mRegionInfo[NcchRegion_Logo].hashed_size > 0)
|
||||||
{
|
{
|
||||||
mRegionInfo[NcchRegion_Logo].valid = memcmp(region_hashes[NcchRegion_Logo].data(), mHeader.header.logo_hash.data(), region_hashes[NcchRegion_Logo].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
mRegionInfo[NcchRegion_Logo].valid = memcmp(region_hashes[NcchRegion_Logo].data(), mHeader.header.logo_hash.data(), region_hashes[NcchRegion_Logo].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
|
if (mRegionInfo[NcchRegion_Logo].valid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Logo SHA2-256 hash was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// exefs hash
|
// exefs hash
|
||||||
if (mRegionInfo[NcchRegion_ExeFs].hashed_size > 0)
|
if (mRegionInfo[NcchRegion_ExeFs].hashed_size > 0)
|
||||||
{
|
{
|
||||||
mRegionInfo[NcchRegion_ExeFs].valid = memcmp(region_hashes[NcchRegion_ExeFs].data(), mHeader.header.exefs_prot_hash.data(), region_hashes[NcchRegion_ExeFs].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
mRegionInfo[NcchRegion_ExeFs].valid = memcmp(region_hashes[NcchRegion_ExeFs].data(), mHeader.header.exefs_prot_hash.data(), region_hashes[NcchRegion_ExeFs].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
||||||
|
|
||||||
|
if (mRegionInfo[NcchRegion_ExeFs].valid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] ExeFs SuperBlock SHA2-256 hash was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// romfs hash
|
// romfs hash
|
||||||
if (mRegionInfo[NcchRegion_RomFs].hashed_size > 0)
|
if (mRegionInfo[NcchRegion_RomFs].hashed_size > 0)
|
||||||
{
|
{
|
||||||
mRegionInfo[NcchRegion_RomFs].valid = memcmp(region_hashes[NcchRegion_RomFs].data(), mHeader.header.romfs_prot_hash.data(), region_hashes[NcchRegion_RomFs].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
mRegionInfo[NcchRegion_RomFs].valid = memcmp(region_hashes[NcchRegion_RomFs].data(), mHeader.header.romfs_prot_hash.data(), region_hashes[NcchRegion_RomFs].size()) == 0 ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (mRegionInfo[NcchRegion_RomFs].valid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] RomFs SuperBlock SHA2-256 hash was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ctrtool::NcchProcess::printHeader()
|
void ctrtool::NcchProcess::printHeader()
|
||||||
@@ -743,16 +768,20 @@ void ctrtool::NcchProcess::extractRegionBinaries()
|
|||||||
{
|
{
|
||||||
if (mRegionOpt[i].bin_extract_path.isSet() && mRegionInfo[i].ready_stream != nullptr)
|
if (mRegionOpt[i].bin_extract_path.isSet() && mRegionInfo[i].ready_stream != nullptr)
|
||||||
{
|
{
|
||||||
switch(i)
|
if (mVerbose)
|
||||||
{
|
{
|
||||||
case NcchRegion_Header: fmt::print("Saving Header...\n"); break;
|
switch(i)
|
||||||
case NcchRegion_ExHeader: fmt::print("Saving Extended Header...\n"); break;
|
{
|
||||||
case NcchRegion_PlainRegion: fmt::print("Saving Plain Region...\n"); break;
|
case NcchRegion_Header: fmt::print(stderr, "[{} LOG] Saving Header...\n", mModuleLabel); break;
|
||||||
case NcchRegion_Logo: fmt::print("Saving Logo...\n"); break;
|
case NcchRegion_ExHeader: fmt::print(stderr, "[{} LOG] Saving Extended Header...\n", mModuleLabel); break;
|
||||||
case NcchRegion_ExeFs: fmt::print("Saving ExeFS...\n"); break;
|
case NcchRegion_PlainRegion: fmt::print(stderr, "[{} LOG] Saving Plain Region...\n", mModuleLabel); break;
|
||||||
case NcchRegion_RomFs: fmt::print("Saving RomFS...\n"); break;
|
case NcchRegion_Logo: fmt::print(stderr, "[{} LOG] Saving Logo...\n", mModuleLabel); break;
|
||||||
|
case NcchRegion_ExeFs: fmt::print(stderr, "[{} LOG] Saving ExeFs...\n", mModuleLabel); break;
|
||||||
|
case NcchRegion_RomFs: fmt::print(stderr, "[{} LOG] Saving RomFs...\n", mModuleLabel); break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
in_stream = mRegionInfo[i].ready_stream;
|
in_stream = mRegionInfo[i].ready_stream;
|
||||||
local_fs.openFile(mRegionOpt[i].bin_extract_path.get(), tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream);
|
local_fs.openFile(mRegionOpt[i].bin_extract_path.get(), tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream);
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,10 @@ void ctrtool::RomFsProcess::visitDir(const tc::io::Path& v_path, const tc::io::P
|
|||||||
// build out path
|
// build out path
|
||||||
out_path = l_path + *itr;
|
out_path = l_path + *itr;
|
||||||
|
|
||||||
fmt::print("Saving {}...\n", out_path.to_string());
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Saving {}...\n", mModuleLabel, out_path.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
// begin export
|
// begin export
|
||||||
mFsReader->openFile(v_path + *itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream);
|
mFsReader->openFile(v_path + *itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream);
|
||||||
|
|||||||
@@ -356,6 +356,8 @@ private:
|
|||||||
ctrtool::SettingsInitializer::SettingsInitializer(const std::vector<std::string>& args) :
|
ctrtool::SettingsInitializer::SettingsInitializer(const std::vector<std::string>& args) :
|
||||||
Settings(),
|
Settings(),
|
||||||
mModuleLabel("ctrtool::SettingsInitializer"),
|
mModuleLabel("ctrtool::SettingsInitializer"),
|
||||||
|
mSuppressOutput(false),
|
||||||
|
mShowKeys(false),
|
||||||
mFallBackTitleKey(),
|
mFallBackTitleKey(),
|
||||||
mFallBackSeed(),
|
mFallBackSeed(),
|
||||||
mSeedDbPath()
|
mSeedDbPath()
|
||||||
@@ -365,6 +367,15 @@ ctrtool::SettingsInitializer::SettingsInitializer(const std::vector<std::string>
|
|||||||
if (infile.path.isNull())
|
if (infile.path.isNull())
|
||||||
throw tc::ArgumentException(mModuleLabel, "No input file was specified.");
|
throw tc::ArgumentException(mModuleLabel, "No input file was specified.");
|
||||||
|
|
||||||
|
// suppress output if requested
|
||||||
|
if (mSuppressOutput)
|
||||||
|
{
|
||||||
|
opt.info = false;
|
||||||
|
//opt.verbose = false;
|
||||||
|
exefs.list_fs = false;
|
||||||
|
romfs.list_fs = false;
|
||||||
|
}
|
||||||
|
|
||||||
opt.keybag = KeyBagInitializer(opt.is_dev, mFallBackTitleKey, mSeedDbPath, mFallBackSeed);
|
opt.keybag = KeyBagInitializer(opt.is_dev, mFallBackTitleKey, mSeedDbPath, mFallBackSeed);
|
||||||
|
|
||||||
// determine filetype if not manually specified
|
// determine filetype if not manually specified
|
||||||
@@ -417,9 +428,10 @@ void ctrtool::SettingsInitializer::parse_args(const std::vector<std::string>& ar
|
|||||||
|
|
||||||
// get option flags
|
// get option flags
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.info, {"-i", "--info"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.info, {"-i", "--info"})));
|
||||||
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verbose, {"-v", "--verbose"})));
|
||||||
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(mSuppressOutput, {"-q", "--quiet"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.plain, {"-p", "--plain"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.plain, {"-p", "--plain"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.raw, {"-r", "--raw"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.raw, {"-r", "--raw"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verbose, {"-v", "--verbose"})));
|
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verify, {"-y", "--verify"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.verify, {"-y", "--verify"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.is_dev, {"-d", "--dev"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.is_dev, {"-d", "--dev"})));
|
||||||
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.show_keys, {"--showkeys"})));
|
opts.registerOptionHandler(std::shared_ptr<FlagOptionHandler>(new FlagOptionHandler(opt.show_keys, {"--showkeys"})));
|
||||||
@@ -593,14 +605,16 @@ void ctrtool::SettingsInitializer::usage_text()
|
|||||||
|
|
||||||
fmt::print(stderr,
|
fmt::print(stderr,
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
" -i, --info Show file info.\n"
|
//" -i, --info Show file info.\n"
|
||||||
" This is the default action.\n"
|
//" This is the default action.\n"
|
||||||
//" -x, --extract Extract data from file.\n"
|
//" -x, --extract Extract data from file.\n"
|
||||||
//" This is also the default action.\n"
|
//" This is also the default action.\n"
|
||||||
|
" -v, --verbose Give verbose output.\n"
|
||||||
|
" -q, --quiet Only output errors (regular output is silenced).\n"
|
||||||
" -p, --plain Extract data without decrypting.\n"
|
" -p, --plain Extract data without decrypting.\n"
|
||||||
" -r, --raw Keep raw data, don't unpack.\n"
|
" -r, --raw Keep raw data, don't unpack.\n"
|
||||||
//" -k, --keyset=file Specify keyset file.\n"
|
//" -k, --keyset=file Specify keyset file.\n"
|
||||||
" -v, --verbose Give verbose output.\n"
|
|
||||||
" -y, --verify Verify hashes and signatures.\n"
|
" -y, --verify Verify hashes and signatures.\n"
|
||||||
" -d, --dev Decrypt with development keys instead of retail.\n"
|
" -d, --dev Decrypt with development keys instead of retail.\n"
|
||||||
//" --unitsize=size Set media unit size (default 0x200).\n"
|
//" --unitsize=size Set media unit size (default 0x200).\n"
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ struct Settings
|
|||||||
struct Options
|
struct Options
|
||||||
{
|
{
|
||||||
bool info;
|
bool info;
|
||||||
|
bool verbose;
|
||||||
bool plain;
|
bool plain;
|
||||||
bool raw;
|
bool raw;
|
||||||
bool verbose;
|
|
||||||
bool verify;
|
bool verify;
|
||||||
bool show_keys;
|
bool show_keys;
|
||||||
bool is_dev;
|
bool is_dev;
|
||||||
@@ -123,9 +123,9 @@ struct Settings
|
|||||||
infile.path = tc::Optional<tc::io::Path>();
|
infile.path = tc::Optional<tc::io::Path>();
|
||||||
|
|
||||||
opt.info = true;
|
opt.info = true;
|
||||||
|
opt.verbose = false;
|
||||||
opt.plain = false;
|
opt.plain = false;
|
||||||
opt.raw = false;
|
opt.raw = false;
|
||||||
opt.verbose = false;
|
|
||||||
opt.verify = false;
|
opt.verify = false;
|
||||||
opt.show_keys = false;
|
opt.show_keys = false;
|
||||||
opt.is_dev = false;
|
opt.is_dev = false;
|
||||||
@@ -168,6 +168,8 @@ private:
|
|||||||
|
|
||||||
std::string mModuleLabel;
|
std::string mModuleLabel;
|
||||||
|
|
||||||
|
bool mSuppressOutput;
|
||||||
|
|
||||||
bool mShowKeys;
|
bool mShowKeys;
|
||||||
tc::Optional<std::string> mFallBackTitleKey;
|
tc::Optional<std::string> mFallBackTitleKey;
|
||||||
tc::Optional<std::string> mFallBackSeed;
|
tc::Optional<std::string> mFallBackSeed;
|
||||||
|
|||||||
+46
-20
@@ -93,7 +93,10 @@ void ctrtool::TikProcess::importData()
|
|||||||
// determine title key
|
// determine title key
|
||||||
if (mKeyBag.common_key.find(mTicket.key_id) != mKeyBag.common_key.end())
|
if (mKeyBag.common_key.find(mTicket.key_id) != mKeyBag.common_key.end())
|
||||||
{
|
{
|
||||||
fmt::print("[LOG] Decrypting titlekey from ticket.\n");
|
if (mVerbose)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Decrypting titlekey from ticket.\n", mModuleLabel);
|
||||||
|
}
|
||||||
|
|
||||||
// get common key
|
// get common key
|
||||||
auto common_key = mKeyBag.common_key[mTicket.key_id];
|
auto common_key = mKeyBag.common_key[mTicket.key_id];
|
||||||
@@ -111,7 +114,7 @@ void ctrtool::TikProcess::importData()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fmt::print("[LOG] Cannot determine titlekey.\n");
|
fmt::print(stderr, "[{} LOG] Cannot determine titlekey.\n", mModuleLabel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,48 +153,71 @@ void ctrtool::TikProcess::verifyData()
|
|||||||
// verify cert
|
// verify cert
|
||||||
for (size_t i = 0; i < mCertChain.size(); i++)
|
for (size_t i = 0; i < mCertChain.size(); i++)
|
||||||
{
|
{
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mCertChain[i].signature.issuer);
|
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mCertChain[i].signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mCertChain[i].signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mCertChain[i].signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
||||||
{
|
|
||||||
mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mCertSigValid[i] = local_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mCertSigValid[i] = local_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
||||||
|
{
|
||||||
|
// only show this warning for non-root signed certificates
|
||||||
|
if (mCertChain[i].signature.issuer != "Root")
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Public key \"{}\" (for certificate \"{}\") was not present in the certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mCertChain[i].signature.issuer, mCertChain[i].subject);
|
||||||
|
}
|
||||||
|
mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (certificate).\n", mCertChain[i].signature.issuer);
|
fmt::print(stderr, "[{} LOG] Could not locate public key for \"{}\" (certificate).\n", mModuleLabel, mCertChain[i].signature.issuer);
|
||||||
mCertSigValid[i] = ValidState::Fail;
|
mCertSigValid[i] = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log certificate signature validation error
|
||||||
|
if (mCertSigValid[i] != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} LOG] Signature for Certificate \"{}\" was invalid.\n", mModuleLabel, mCertChain[i].signature.issuer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify ticket
|
// verify ticket
|
||||||
{
|
{
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mTicket.signature.issuer);
|
// verify ticket
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTicket.signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTicket.signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mTicket.signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
||||||
{
|
|
||||||
mTicketSigValid = keybag_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mTicketSigValid = local_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mTicketSigValid = local_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
|
||||||
|
{
|
||||||
|
// only show this warning when there are certificates appended to the ticket (only tickets downloaded from CDN will have an appended certificate chain)
|
||||||
|
if (mCertChain.size() != 0)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Public key \"{}\" (for ticket) was not present in the appended certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mTicket.signature.issuer);
|
||||||
|
}
|
||||||
|
mTicketSigValid = keybag_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (ticket).\n", mTicket.signature.issuer);
|
fmt::print(stderr, "[{} ERROR] Could not locate public key \"{}\" (for ticket).\n", mModuleLabel, mTicket.signature.issuer);
|
||||||
mTicketSigValid = ValidState::Fail;
|
mTicketSigValid = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log ticket signature validation error
|
||||||
|
if (mTicketSigValid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for Ticket was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+42
-19
@@ -125,48 +125,71 @@ void ctrtool::TmdProcess::verifyData()
|
|||||||
// verify cert
|
// verify cert
|
||||||
for (size_t i = 0; i < mCertChain.size(); i++)
|
for (size_t i = 0; i < mCertChain.size(); i++)
|
||||||
{
|
{
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mCertChain[i].signature.issuer);
|
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mCertChain[i].signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mCertChain[i].signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mCertChain[i].signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
||||||
{
|
|
||||||
mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mCertSigValid[i] = local_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mCertSigValid[i] = local_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mCertChain[i].signature.sig_type)
|
||||||
|
{
|
||||||
|
// only show this warning for non-root signed certificates
|
||||||
|
if (mCertChain[i].signature.issuer != "Root")
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Public key \"{}\" (for certificate \"{}\") was not present in the certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mCertChain[i].signature.issuer, mCertChain[i].subject);
|
||||||
|
}
|
||||||
|
mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (certificate).\n", mCertChain[i].signature.issuer);
|
fmt::print(stderr, "[{} ERROR] Could not locate public key for \"{}\" (certificate).\n", mModuleLabel, mCertChain[i].signature.issuer);
|
||||||
mCertSigValid[i] = ValidState::Fail;
|
mCertSigValid[i] = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log certificate signature validation error
|
||||||
|
if (mCertSigValid[i] != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for Certificate \"{}\" was invalid.\n", mModuleLabel, mCertChain[i].signature.issuer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify tmd
|
// verify ticket
|
||||||
{
|
{
|
||||||
auto keybag_issuer_itr = mIssuerSigner.find(mTitleMetaData.signature.issuer);
|
// verify ticket
|
||||||
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTitleMetaData.signature.issuer);
|
auto local_issuer_itr = mCertImportedIssuerSigner.find(mTitleMetaData.signature.issuer);
|
||||||
|
auto keybag_issuer_itr = mIssuerSigner.find(mTitleMetaData.signature.issuer);
|
||||||
|
|
||||||
// try first with the keybag imported issuer
|
// first try with the issuer profiles imported from the local certificates
|
||||||
if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
||||||
{
|
|
||||||
mTitleMetaDataSigValid = keybag_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
|
||||||
}
|
|
||||||
// fallback try with the issuer profiles imported from the local certificates
|
|
||||||
else if (local_issuer_itr != mCertImportedIssuerSigner.end() && local_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
|
||||||
{
|
{
|
||||||
mTitleMetaDataSigValid = local_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
mTitleMetaDataSigValid = local_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
// fallback try with the keybag imported issuer
|
||||||
|
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
|
||||||
|
{
|
||||||
|
// only show this warning when there are certificates appended to the tmd (only tmd downloaded from CDN will have an appended certificate chain)
|
||||||
|
if (mCertChain.size() != 0)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Public key \"{}\" (for tmd) was not present in the appended certificate chain. The public key included with CTRTool was used instead.\n", mModuleLabel, mTitleMetaData.signature.issuer);
|
||||||
|
}
|
||||||
|
mTitleMetaDataSigValid = keybag_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cannot locate rsa key to verify
|
// cannot locate rsa key to verify
|
||||||
fmt::print(stderr, "Could not read public key for \"{}\" (tmd).\n", mTitleMetaData.signature.issuer);
|
fmt::print(stderr, "[{} ERROR] Could not locate public key \"{}\" (for tmd).\n", mModuleLabel, mTitleMetaData.signature.issuer);
|
||||||
mTitleMetaDataSigValid = ValidState::Fail;
|
mTitleMetaDataSigValid = ValidState::Fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// log tmd signature validation error
|
||||||
|
if (mTitleMetaDataSigValid != ValidState::Good)
|
||||||
|
{
|
||||||
|
fmt::print(stderr, "[{} ERROR] Signature for TitleMetaData was invalid.\n", mModuleLabel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
|||||||
proc.setVerboseMode(set.opt.verbose);
|
proc.setVerboseMode(set.opt.verbose);
|
||||||
proc.setVerifyMode(set.opt.verify);
|
proc.setVerifyMode(set.opt.verify);
|
||||||
proc.setRawMode(set.opt.raw);
|
proc.setRawMode(set.opt.raw);
|
||||||
proc.setPlainMode(set.opt.raw);
|
proc.setPlainMode(set.opt.plain);
|
||||||
proc.setShowSyscallName(set.exheader.show_syscalls_as_names);
|
proc.setShowSyscallName(set.exheader.show_syscalls_as_names);
|
||||||
proc.setRegionProcessOutputMode(proc.NcchRegion_Header, set.opt.info, false, tc::Optional<tc::io::Path>(), tc::Optional<tc::io::Path>());
|
proc.setRegionProcessOutputMode(proc.NcchRegion_Header, set.opt.info, false, tc::Optional<tc::io::Path>(), tc::Optional<tc::io::Path>());
|
||||||
proc.setRegionProcessOutputMode(proc.NcchRegion_ExHeader, set.opt.info, false, set.ncch.exheader_path, tc::Optional<tc::io::Path>());
|
proc.setRegionProcessOutputMode(proc.NcchRegion_ExHeader, set.opt.info, false, set.ncch.exheader_path, tc::Optional<tc::io::Path>());
|
||||||
@@ -126,7 +126,7 @@ int umain(const std::vector<std::string>& args, const std::vector<std::string>&
|
|||||||
ctrtool::CiaProcess proc;
|
ctrtool::CiaProcess proc;
|
||||||
proc.setInputStream(infile_stream);
|
proc.setInputStream(infile_stream);
|
||||||
proc.setKeyBag(set.opt.keybag);
|
proc.setKeyBag(set.opt.keybag);
|
||||||
proc.setCliOutputMode(true, false);
|
proc.setCliOutputMode(set.opt.info);
|
||||||
proc.setVerboseMode(set.opt.verbose);
|
proc.setVerboseMode(set.opt.verbose);
|
||||||
proc.setVerifyMode(set.opt.verify);
|
proc.setVerifyMode(set.opt.verify);
|
||||||
if (set.rom.content_extract_path.isSet())
|
if (set.rom.content_extract_path.isSet())
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
#define APP_NAME "CTRTool"
|
#define APP_NAME "CTRTool"
|
||||||
#define BIN_NAME "ctrtool"
|
#define BIN_NAME "ctrtool"
|
||||||
#define VER_MAJOR 1
|
#define VER_MAJOR 1
|
||||||
#define VER_MINOR 0
|
#define VER_MINOR 1
|
||||||
#define VER_PATCH 4
|
#define VER_PATCH 0
|
||||||
#define AUTHORS "jakcron"
|
#define AUTHORS "jakcron"
|
||||||
Reference in New Issue
Block a user