Emit errors/warns/logs in a consistent manner.

This commit is contained in:
jakcron
2022-04-12 20:05:02 +08:00
parent 60334cd7dd
commit c0ce043b30
10 changed files with 231 additions and 97 deletions
+8 -14
View File
@@ -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(stderr, "[{} LOG] Unsupported CardInfo::CryptoType ({})\n", mModuleLabel, (uint32_t)mHeader.card_info.flag.crypto_type);
}
if (initial_data_key_available) if (initial_data_key_available)
{ {
@@ -258,7 +252,7 @@ void ctrtool::CciProcess::verifyHeader()
} }
else else
{ {
fmt::print(stderr, "[{} LOG] Could not read static CFA/CCI RSA2048 public key.\n", mModuleLabel); fmt::print(stderr, "[{} LOG] Could not load CCI RSA2048 public key.\n", mModuleLabel);
mValidSignature = ValidState::Fail; mValidSignature = ValidState::Fail;
} }
+11 -13
View File
@@ -285,14 +285,6 @@ void ctrtool::CiaProcess::importHeader()
} }
} }
}
else
{
if (mVerify)
{
fmt::print(stderr, "[{} LOG] CIA has no Certificates, verifying Ticket or TitleMetaData will use the bundled public keys.\n", mModuleLabel);
}
} }
if (mTikSizeInfo.size > 0) if (mTikSizeInfo.size > 0)
@@ -302,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(stderr, "[{} LOG] Using fallback titlekey.\n", mModuleLabel); 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(stderr, "[{} LOG] Decrypting titlekey from ticket.\n", mModuleLabel); 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];
@@ -420,7 +418,7 @@ void ctrtool::CiaProcess::verifyMetadata()
// only show this warning for non-root signed certificates // only show this warning for non-root signed certificates
if (mCertChain[i].signature.issuer != "Root") if (mCertChain[i].signature.issuer != "Root")
{ {
fmt::print(stderr, "[{} LOG] Public key \"{}\" (for certificate \"{}\") was not present in the CIA certificate chain. The bundled public key was used instead.\n", mModuleLabel, mCertChain[i].signature.issuer, mCertChain[i].subject); fmt::print(stderr, "[{} LOG] 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; mCertSigValid[i] = keybag_issuer_itr->second->verifyHash(mCertChain[i].calculated_hash.data(), mCertChain[i].signature.sig.data()) ? ValidState::Good : ValidState::Fail;
} }
@@ -452,7 +450,7 @@ void ctrtool::CiaProcess::verifyMetadata()
// fallback try with the keybag imported issuer // fallback try with the keybag imported issuer
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTicket.signature.sig_type) else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTicket.signature.sig_type)
{ {
fmt::print(stderr, "[{} LOG] Public key \"{}\" (for ticket) was not present in the CIA certificate chain. The bundled public key was used instead.\n", mModuleLabel, mTicket.signature.issuer); fmt::print(stderr, "[{} LOG] 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; mTicketSigValid = keybag_issuer_itr->second->verifyHash(mTicket.calculated_hash.data(), mTicket.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
} }
else else
@@ -482,7 +480,7 @@ void ctrtool::CiaProcess::verifyMetadata()
// fallback try with the keybag imported issuer // fallback try with the keybag imported issuer
else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type) else if (keybag_issuer_itr != mIssuerSigner.end() && keybag_issuer_itr->second->getSigType() == mTitleMetaData.signature.sig_type)
{ {
fmt::print(stderr, "[{} LOG] Public key \"{}\" (for tmd) was not present in the CIA certificate chain. The bundled public key was used instead.\n", mModuleLabel, mTitleMetaData.signature.issuer); fmt::print(stderr, "[{} LOG] 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; mTitleMetaDataSigValid = keybag_issuer_itr->second->verifyHash(mTitleMetaData.calculated_hash.data(), mTitleMetaData.signature.sig.data()) ? ValidState::Good : ValidState::Fail;
} }
else else
+66 -6
View File
@@ -96,13 +96,18 @@ void ctrtool::ExHeaderProcess::verifyExHeader()
} }
else else
{ {
fmt::print(stderr, "Could not read AccessDescriptor public key.\n"); fmt::print(stderr, "[{} LOG] Could not load AccessDescriptor RSA2048 public key.\n", mModuleLabel);
mValidSignature = ValidState::Fail; mValidSignature = ValidState::Fail;
} }
if (mValidSignature != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] Service \"{}\" was not permitted\n", mModuleLabel, exhdr_service_access_control[i].decode());
} }
} }
} }
if (mValidLocalCaps.system_save_id[0] != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemSaveId1");
}
if (mValidLocalCaps.system_save_id[1] != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemSaveId2");
}
if (mValidLocalCaps.fs_access != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "FsAccess");
}
/*
if (mValidLocalCaps.core_version != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "CoreVersion");
}
*/
if (mValidLocalCaps.program_id != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ProgramId");
}
if (mValidLocalCaps.priority != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "ThreadPriority");
}
if (mValidLocalCaps.affinity_mask != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "AffinityMask");
}
if (mValidLocalCaps.ideal_processor != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "IdealProcessor");
}
if (mValidLocalCaps.old3ds_system_mode != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemMode (Old3DS)");
}
if (mValidLocalCaps.new3ds_system_mode != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "SystemMode (New3DS)");
}
if (mValidLocalCaps.enable_l2_cache != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "EnableL2Cache");
}
if (mValidLocalCaps.new3ds_cpu_speed != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} was not permmited by AccessDescriptor.\n", mModuleLabel, "CpuSpeed");
}
if (mValidLocalCaps.service_control != ValidState::Good)
{
fmt::print(stderr, "[{} LOG] {} 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))
+2 -2
View File
@@ -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;
+9 -2
View File
@@ -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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] Signature for FIRM was invalid (SigHax).\n", mModuleLabel);
mSignatureState = SignatureState_SigHax; mSignatureState = SignatureState_SigHax;
} }
else else
{ {
fmt::print(stderr, "[{} LOG] Signature for FIRM was invalid.\n", mModuleLabel);
mSignatureState = SignatureState_Fail; mSignatureState = SignatureState_Fail;
} }
} }
+4 -3
View File
@@ -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, "[{} LOG] 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, "[{} LOG] IVFC Layer {:d} failed validation.\n", mModuleLabel, i);
} }
} }
} }
+41 -16
View File
@@ -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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] Could not read 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] RomFs SuperBlock SHA2-256 hash was invalid.\n", mModuleLabel);
}
}
} }
void ctrtool::NcchProcess::printHeader() void ctrtool::NcchProcess::printHeader()
+1 -1
View File
@@ -200,7 +200,7 @@ 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()); 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);
+46 -20
View File
@@ -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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] Signature for Ticket was invalid.\n", mModuleLabel);
}
} }
} }
+42 -19
View File
@@ -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, "[{} 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 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, "[{} LOG] 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, "[{} LOG] 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, "[{} LOG] Signature for TitleMetaData was invalid.\n", mModuleLabel);
}
} }
} }