mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-08 11:51:48 +00:00
keys: make latest mkey non-mandatory for older FWs
This commit is contained in:
parent
a5e72a45e0
commit
5e333bb92e
2 changed files with 74 additions and 46 deletions
|
@ -1044,7 +1044,7 @@ int main(int argc, char *argv[])
|
||||||
updateTitleList(&g_systemTitlesMenu, &g_ncaMenu, true);
|
updateTitleList(&g_systemTitlesMenu, &g_ncaMenu, true);
|
||||||
|
|
||||||
Menu *cur_menu = &g_rootMenu;
|
Menu *cur_menu = &g_rootMenu;
|
||||||
u32 element_count = menuGetElementCount(cur_menu), page_size = 30;
|
u32 element_count = menuGetElementCount(cur_menu), page_size = 20;
|
||||||
|
|
||||||
TitleApplicationMetadata *app_metadata = NULL;
|
TitleApplicationMetadata *app_metadata = NULL;
|
||||||
|
|
||||||
|
@ -6232,7 +6232,7 @@ static void nspThreadFunc(void *arg)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
|
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(cur_nca_ctx->content_type), cur_nca_ctx->id_offset);
|
||||||
|
|
||||||
// don't go any further with this nca if we can't access its fs data because it's pointless
|
// don't go any further with this nca if we can't access its fs data because it's pointless
|
||||||
if (cur_nca_ctx->rights_id_available && !cur_nca_ctx->titlekey_retrieved && !no_titlekey_confirmation)
|
if (cur_nca_ctx->rights_id_available && !cur_nca_ctx->titlekey_retrieved && !no_titlekey_confirmation)
|
||||||
|
@ -6286,7 +6286,7 @@ static void nspThreadFunc(void *arg)
|
||||||
{
|
{
|
||||||
case NcmContentType_Program:
|
case NcmContentType_Program:
|
||||||
{
|
{
|
||||||
// don't proceed if we didn't allocate programinfo ctx or if we're dealing with a sparse layer
|
// don't proceed if we didn't allocate programinfo ctx
|
||||||
if (!program_count || !program_info_ctx) break;
|
if (!program_count || !program_info_ctx) break;
|
||||||
|
|
||||||
ProgramInfoContext *cur_program_info_ctx = &(program_info_ctx[program_idx]);
|
ProgramInfoContext *cur_program_info_ctx = &(program_info_ctx[program_idx]);
|
||||||
|
@ -6383,7 +6383,7 @@ static void nspThreadFunc(void *arg)
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool retrieve_tik_cert = (!remove_titlekey_crypto && tik.size > 0);
|
bool retrieve_tik_cert = (!remove_titlekey_crypto && tikIsValidTicket(&tik));
|
||||||
if (retrieve_tik_cert)
|
if (retrieve_tik_cert)
|
||||||
{
|
{
|
||||||
if (!(tik_common_block = tikGetCommonBlockFromTicket(&tik)))
|
if (!(tik_common_block = tikGetCommonBlockFromTicket(&tik)))
|
||||||
|
|
|
@ -110,7 +110,8 @@ static bool keysGenerateAesKeyFromAesKek(const u8 *kek_src, u8 key_generation, S
|
||||||
static bool g_keysetLoaded = false;
|
static bool g_keysetLoaded = false;
|
||||||
static Mutex g_keysetMutex = 0;
|
static Mutex g_keysetMutex = 0;
|
||||||
|
|
||||||
static u8 g_atmosphereKeyGeneration = 0;
|
static u8 g_atmosphereKeyGeneration = 0, g_currentMasterKeyIndex = 0;
|
||||||
|
static bool g_outdatedMasterKeyVectors = false, g_lowMasterKeyRequirement = false;
|
||||||
|
|
||||||
static SetCalRsa2048DeviceKey g_eTicketRsaDeviceKey = {0};
|
static SetCalRsa2048DeviceKey g_eTicketRsaDeviceKey = {0};
|
||||||
static KeysNxKeyset g_nxKeyset = {0};
|
static KeysNxKeyset g_nxKeyset = {0};
|
||||||
|
@ -132,6 +133,15 @@ bool keysLoadKeyset(void)
|
||||||
/* This actually represents an index, so we must be careful whenever we use it. */
|
/* This actually represents an index, so we must be careful whenever we use it. */
|
||||||
g_atmosphereKeyGeneration = utilsGetAtmosphereKeyGeneration();
|
g_atmosphereKeyGeneration = utilsGetAtmosphereKeyGeneration();
|
||||||
|
|
||||||
|
/* Get current master key index. */
|
||||||
|
g_currentMasterKeyIndex = (NcaKeyGeneration_Current - 1);
|
||||||
|
|
||||||
|
/* Determine if our master key vectors are outdated. */
|
||||||
|
g_outdatedMasterKeyVectors = (g_atmosphereKeyGeneration > g_currentMasterKeyIndex);
|
||||||
|
|
||||||
|
/* Determine if we're dealing with a lower master key requirement. */
|
||||||
|
g_lowMasterKeyRequirement = (g_atmosphereKeyGeneration < g_currentMasterKeyIndex);
|
||||||
|
|
||||||
/* Get eTicket RSA device key. */
|
/* Get eTicket RSA device key. */
|
||||||
Result rc = setcalGetEticketDeviceKey(&g_eTicketRsaDeviceKey);
|
Result rc = setcalGetEticketDeviceKey(&g_eTicketRsaDeviceKey);
|
||||||
if (R_FAILED(rc))
|
if (R_FAILED(rc))
|
||||||
|
@ -601,8 +611,8 @@ static bool keysReadKeysFromFile(void)
|
||||||
PARSE_HEX_KEY(eticket_rsa_kek_name, g_nxKeyset.eticket_rsa_kek, eticket_rsa_kek_available = true; continue);
|
PARSE_HEX_KEY(eticket_rsa_kek_name, g_nxKeyset.eticket_rsa_kek, eticket_rsa_kek_available = true; continue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse master keys, starting with the last known one. */
|
/* Parse master keys, starting with the minimum required one (if dealing with a lower master key requirement) or the last known one. */
|
||||||
for(u8 i = (NcaKeyGeneration_Current - 1); i < NcaKeyGeneration_Max; i++)
|
for(u8 i = (g_lowMasterKeyRequirement ? g_atmosphereKeyGeneration : g_currentMasterKeyIndex); i < NcaKeyGeneration_Max; i++)
|
||||||
{
|
{
|
||||||
PARSE_HEX_KEY_WITH_INDEX("master_key", i, g_nxKeyset.master_keys[i], break);
|
PARSE_HEX_KEY_WITH_INDEX("master_key", i, g_nxKeyset.master_keys[i], break);
|
||||||
}
|
}
|
||||||
|
@ -637,86 +647,104 @@ static bool keysReadKeysFromFile(void)
|
||||||
|
|
||||||
static bool keysDeriveMasterKeys(void)
|
static bool keysDeriveMasterKeys(void)
|
||||||
{
|
{
|
||||||
u8 tmp[AES_128_KEY_SIZE] = {0};
|
u8 tmp[AES_128_KEY_SIZE] = {0}, current_mkey_index = g_currentMasterKeyIndex;
|
||||||
const u8 current_mkey_index = (NcaKeyGeneration_Current - 1); // Convert into an index.
|
const bool is_dev = utilsIsDevelopmentUnit(), is_mariko = utilsIsMarikoUnit();
|
||||||
const bool is_dev = utilsIsDevelopmentUnit(), is_mariko = utilsIsMarikoUnit(), outdated_mkey_vectors = (g_atmosphereKeyGeneration > current_mkey_index);
|
bool current_mkey_available = false;
|
||||||
bool latest_mkey_available = false;
|
|
||||||
|
|
||||||
/* Check if the latest master key was retrieved. */
|
if (current_mkey_index != g_atmosphereKeyGeneration) LOG_MSG_WARNING("Current key generation mismatch detected (%02X != %02X).", current_mkey_index, g_atmosphereKeyGeneration);
|
||||||
if (outdated_mkey_vectors)
|
|
||||||
|
if (g_outdatedMasterKeyVectors)
|
||||||
{
|
{
|
||||||
/* Our master key vectors are outdated. */
|
/* Our master key vectors are outdated. */
|
||||||
/* This means the user is running both a HOS version with a newer key generation and an Atmosphère release with support for said HOS version. */
|
/* This means the user is running both a HOS version with a newer master key generation and an Atmosphère release with support for said HOS version. */
|
||||||
/* Not everything is lost, though. We just need to check if we parsed all master keys between the last one we know and the one Atmosphère supports (inclusive range). */
|
/* Not everything is lost, though. We just need to check if we parsed all master keys between the last one we know and the one Atmosphère supports (inclusive range). */
|
||||||
/* However, since we have no master key vectors for the additional master key(s), we can't reliably test them. */
|
/* However, since we have no master key vectors for the additional master key(s), we can't reliably test them. */
|
||||||
latest_mkey_available = true;
|
current_mkey_available = true;
|
||||||
|
|
||||||
for(u8 i = current_mkey_index; i <= g_atmosphereKeyGeneration; i++)
|
for(u8 i = current_mkey_index; i <= g_atmosphereKeyGeneration; i++)
|
||||||
{
|
{
|
||||||
if (keysIsKeyEmpty(g_nxKeyset.master_keys[i]))
|
if (keysIsKeyEmpty(g_nxKeyset.master_keys[i]))
|
||||||
{
|
{
|
||||||
latest_mkey_available = false;
|
current_mkey_available = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
/* Our master key vectors are up-to-date. */
|
|
||||||
/* Just checking if we have the latest known master key should suffice. */
|
|
||||||
latest_mkey_available = !keysIsKeyEmpty(g_nxKeyset.master_keys[current_mkey_index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only derive the latest master key if it hasn't been populated already. */
|
/* Bail out immediately if the newer master keys are unavailable. */
|
||||||
if (!latest_mkey_available)
|
if (!current_mkey_available)
|
||||||
{
|
|
||||||
/* Bail out immediately if our master key vectors are outdated. */
|
|
||||||
if (outdated_mkey_vectors)
|
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("Unable to derive master keys.\r\n" \
|
LOG_MSG_ERROR("PKG1 key generation (%02X) is higher than the last known\r\n" \
|
||||||
"PKG1 key generation (%02X) is higher than the last known key generation (%02X).\r\n" \
|
"key generation (%02X). Furthermore, one or more of the newer master keys are not\r\n" \
|
||||||
"Furthermore, one or more of the newer master keys are not available in the keys\r\n" \
|
"available in the keys file. Please redump your console keys and get an updated\r\n" \
|
||||||
"file. If you haven't already, please get an updated build at:\r\n%s\r\n%s", \
|
APP_TITLE " build before trying again. You can get newer builds at:\r\n%s\r\n%s", \
|
||||||
g_atmosphereKeyGeneration, current_mkey_index, PRERELEASE_URL, DISCORD_SERVER_URL);
|
g_atmosphereKeyGeneration, current_mkey_index, PRERELEASE_URL, DISCORD_SERVER_URL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* Our master key vectors are up-to-date. */
|
||||||
|
/* However, we may also be running under a console with an older HOS version and a lower master key generation. */
|
||||||
|
/* If this is the case, we'll need to adjust the current master key index to match Atmosphére's key generation value. */
|
||||||
|
/* There really is no point in demanding the most up-to-date master key under lower firmware versions. */
|
||||||
|
if (g_lowMasterKeyRequirement) current_mkey_index = g_atmosphereKeyGeneration;
|
||||||
|
|
||||||
LOG_MSG_WARNING("Latest known master key (%02X) unavailable. Latest master key derivation will be carried out.", current_mkey_index);
|
/* Now then, just checking if we have the current master key should suffice. */
|
||||||
|
current_mkey_available = !keysIsKeyEmpty(g_nxKeyset.master_keys[current_mkey_index]);
|
||||||
|
|
||||||
/* Derive the latest master KEK. */
|
/* Bail out if we're dealing with a lower master key generation and we don't have its master key. */
|
||||||
|
/* This is because current master key derivation depends on generation-specific master KEKs, and we only hardcode the last known one. */
|
||||||
|
if (!current_mkey_available && g_lowMasterKeyRequirement)
|
||||||
|
{
|
||||||
|
LOG_MSG_ERROR("\"master_key_%02x\" unavailable! Unable to derive lower\r\n" \
|
||||||
|
"master keys. Please redump your console keys and try again.", current_mkey_index);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Derive current master key if it's not populated. */
|
||||||
|
/* We should only enter this conditional block if Atmosphère's key generation matches our last known master key generation. */
|
||||||
|
if (!current_mkey_available)
|
||||||
|
{
|
||||||
|
LOG_MSG_WARNING("Current master key (%02X) unavailable. It will be derived.", current_mkey_index);
|
||||||
|
|
||||||
|
/* Derive the current master KEK. */
|
||||||
if (is_mariko)
|
if (is_mariko)
|
||||||
{
|
{
|
||||||
if (!g_marikoKekAvailable)
|
if (!g_marikoKekAvailable)
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("\"mariko_kek\" unavailable! Unable to derive master keys.");
|
LOG_MSG_ERROR("\"mariko_kek\" unavailable! Unable to derive current\r\n" \
|
||||||
|
"master key. You may need to manually derive it using PartialAesKeyCrack,\r\n" \
|
||||||
|
"and/or redump your console keys. Please try again afterwards.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derive the latest master KEK using the hardcoded Mariko master KEK source and the Mariko KEK. */
|
/* Derive the current master KEK using the hardcoded Mariko master KEK source and the Mariko KEK. */
|
||||||
aes128EcbCrypt(tmp, is_dev ? g_marikoMasterKekSourceDev : g_marikoMasterKekSourceProd, g_nxKeyset.mariko_kek, false);
|
aes128EcbCrypt(tmp, is_dev ? g_marikoMasterKekSourceDev : g_marikoMasterKekSourceProd, g_nxKeyset.mariko_kek, false);
|
||||||
} else {
|
} else {
|
||||||
if (!g_tsecRootKeyAvailable)
|
if (!g_tsecRootKeyAvailable)
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("\"tsec_root_key_%02X\" unavailable! Unable to derive master keys.", TSEC_ROOT_KEY_VERSION);
|
LOG_MSG_ERROR("\"tsec_root_key_%02x\" unavailable! Unable to derive\r\n" \
|
||||||
|
"current master key. Please redump your console keys and try again.", TSEC_ROOT_KEY_VERSION);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derive the latest master KEK using the hardcoded Erista master KEK source and the TSEC root key. */
|
/* Derive the current master KEK using the hardcoded Erista master KEK source and the TSEC root key. */
|
||||||
aes128EcbCrypt(tmp, g_eristaMasterKekSource, g_nxKeyset.tsec_root_key, false);
|
aes128EcbCrypt(tmp, g_eristaMasterKekSource, g_nxKeyset.tsec_root_key, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derive the latest master key using the hardcoded master key source and the latest master KEK. */
|
/* Derive the current master key using the hardcoded master key source and the current master KEK. */
|
||||||
aes128EcbCrypt(g_nxKeyset.master_keys[current_mkey_index], g_masterKeySource, tmp, false);
|
aes128EcbCrypt(g_nxKeyset.master_keys[current_mkey_index], g_masterKeySource, tmp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Derive all lower master keys using the latest master key and the master key vectors. */
|
/* Derive all lower master keys using the current master key and the master key vectors. */
|
||||||
for(u8 i = current_mkey_index; i > NcaKeyGeneration_Since100NUP; i--) aes128EcbCrypt(g_nxKeyset.master_keys[i - 1], is_dev ? g_masterKeyVectorsDev[i] : g_masterKeyVectorsProd[i], \
|
for(u8 i = current_mkey_index; i > 0; i--) aes128EcbCrypt(g_nxKeyset.master_keys[i - 1], is_dev ? g_masterKeyVectorsDev[i] : g_masterKeyVectorsProd[i], \
|
||||||
g_nxKeyset.master_keys[i], false);
|
g_nxKeyset.master_keys[i], false);
|
||||||
|
|
||||||
/* Check if we derived the right keys. */
|
/* Check if we derived the right keys. */
|
||||||
aes128EcbCrypt(tmp, is_dev ? g_masterKeyVectorsDev[NcaKeyGeneration_Since100NUP] : g_masterKeyVectorsProd[NcaKeyGeneration_Since100NUP], \
|
aes128EcbCrypt(tmp, is_dev ? g_masterKeyVectorsDev[0] : g_masterKeyVectorsProd[0], g_nxKeyset.master_keys[0], false);
|
||||||
g_nxKeyset.master_keys[NcaKeyGeneration_Since100NUP], false);
|
|
||||||
|
|
||||||
bool ret = keysIsKeyEmpty(tmp);
|
bool ret = keysIsKeyEmpty(tmp);
|
||||||
if (!ret) LOG_MSG_ERROR("Master key derivation failed! Wrong keys?");
|
if (!ret) LOG_MSG_ERROR("Derivation of %u lower master key(s) failed! Wrong keys?\r\n" \
|
||||||
|
"Please redump your console keys and try again.", current_mkey_index);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -764,9 +792,9 @@ static bool keysDerivePerGenerationKeys(void)
|
||||||
const u8 mkey_index = (i - 1);
|
const u8 mkey_index = (i - 1);
|
||||||
|
|
||||||
/* Make sure we're not dealing with an unpopulated master key entry. */
|
/* Make sure we're not dealing with an unpopulated master key entry. */
|
||||||
if (i > NcaKeyGeneration_Current && keysIsKeyEmpty(g_nxKeyset.master_keys[mkey_index]))
|
if (keysIsKeyEmpty(g_nxKeyset.master_keys[mkey_index]))
|
||||||
{
|
{
|
||||||
//LOG_MSG_DEBUG("\"master_key_%02X\" unavailable.", mkey_index);
|
//LOG_MSG_DEBUG("\"master_key_%02x\" unavailable.", mkey_index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,7 +921,7 @@ static bool keysGenerateAesKek(const u8 *kek_src, u8 key_generation, SmcGenerate
|
||||||
/* Make sure this master key is available. */
|
/* Make sure this master key is available. */
|
||||||
if (keysIsKeyEmpty(mkey))
|
if (keysIsKeyEmpty(mkey))
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("\"master_key_%02X\" unavailable!", mkey_index);
|
LOG_MSG_ERROR("\"master_key_%02x\" unavailable!", mkey_index);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue