Lockpick/source/KeyCollection.cpp

650 lines
31 KiB
C++

/*
* Copyright (c) 2018 shchmue
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "KeyCollection.hpp"
#include "Common.hpp"
#include "creport_debug_types.hpp"
#include "Stopwatch.hpp"
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iomanip>
extern "C" {
#include "set_ext.h"
}
const u8 KeyCollection::null_hash[0x20] = { // hash of empty string
0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14, 0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24,
0x27, 0xAE, 0x41, 0xE4, 0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55};
// function timer
template<typename Duration = std::chrono::microseconds, typename FT, typename ... Args>
typename Duration::rep profile(FT&& fun, Args&&... args) {
const auto beg = std::chrono::high_resolution_clock::now();
std::invoke(fun, std::forward<Args>(args)...);//std::forward<FT>(fun)(std::forward<Args>(args)...);
const auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<Duration>(end - beg).count();
}
KeyCollection::KeyCollection() {
// init all key hashes
u8 index = 0;
char keynum[] = "00";
for (auto key : std::vector<byte_vector>
{
{0xDF, 0x20, 0x6F, 0x59, 0x44, 0x54, 0xEF, 0xDC, 0x70, 0x74, 0x48, 0x3B, 0x0D, 0xED, 0x9F, 0xD3},
{0x0C, 0x25, 0x61, 0x5D, 0x68, 0x4C, 0xEB, 0x42, 0x1C, 0x23, 0x79, 0xEA, 0x82, 0x25, 0x12, 0xAC},
{0x33, 0x76, 0x85, 0xEE, 0x88, 0x4A, 0xAE, 0x0A, 0xC2, 0x8A, 0xFD, 0x7D, 0x63, 0xC0, 0x43, 0x3B},
{0x2D, 0x1F, 0x48, 0x80, 0xED, 0xEC, 0xED, 0x3E, 0x3C, 0xF2, 0x48, 0xB5, 0x65, 0x7D, 0xF7, 0xBE},
{0xBB, 0x5A, 0x01, 0xF9, 0x88, 0xAF, 0xF5, 0xFC, 0x6C, 0xFF, 0x07, 0x9E, 0x13, 0x3C, 0x39, 0x80},
{0xD8, 0xCC, 0xE1, 0x26, 0x6A, 0x35, 0x3F, 0xCC, 0x20, 0xF3, 0x2D, 0x3B, 0x51, 0x7D, 0xE9, 0xC0}
})
{
sprintf(keynum, "%02x", index++);
keyblob_key_source.push_back(Key {"keyblob_key_source_" + std::string(keynum), 0x10, key});
}
for (auto key : std::vector<byte_vector>
{
{0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D}, /* Zeroes encrypted with Master Key 00. */
{0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD}, /* Master key 00 encrypted with Master key 01. */
{0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72}, /* Master key 01 encrypted with Master key 02. */
{0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07}, /* Master key 02 encrypted with Master key 03. */
{0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9}, /* Master key 03 encrypted with Master key 04. */
{0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE}, /* Master key 04 encrypted with Master key 05. */
{0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57}, /* Master key 05 encrypted with Master key 06. */
})
{
mkey_vector.push_back(Key {key, 0x10});
}
master_kek_source.resize(KNOWN_KEYBLOBS);
master_kek_source.push_back(Key {"master_kek_source_06", 0x10, {
0x37, 0x4B, 0x77, 0x29, 0x59, 0xB4, 0x04, 0x30, 0x81, 0xF6, 0xE5, 0x8C, 0x6D, 0x36, 0x17, 0x9A}});
//======================================Keys======================================//
// from Package1 -> TrustZone (Secure Monitor)
aes_kek_generation_source = {"aes_kek_generation_source", 0x10, {
0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9}};
aes_kek_seed_01 = {"aes_kek_seed_01", 0x10, {
0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74}};
aes_kek_seed_03 = {"aes_kek_seed_03", 0x10, {
0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB}};
package2_key_source = {"package2_key_source", 0x10, {
0xFB, 0x8B, 0x6A, 0x9C, 0x79, 0x00, 0xC8, 0x49, 0xEF, 0xD2, 0x4D, 0x85, 0x4D, 0x30, 0xA0, 0xC7}};
titlekek_source = {"titlekek_source", 0x10, {
0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B}};
retail_specific_aes_key_source = {"retail_specific_aes_key_source", 0x10, {
0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95}};
// from Package1ldr
keyblob_mac_key_source = {"keyblob_mac_key_source", 0x10, {
0x59, 0xC7, 0xFB, 0x6F, 0xBE, 0x9B, 0xBE, 0x87, 0x65, 0x6B, 0x15, 0xC0, 0x53, 0x73, 0x36, 0xA5}};
master_key_source = {"master_key_source", 0x10, {
0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C}};
per_console_key_source = {"per_console_key_source", 0x10, {
0x4F, 0x02, 0x5F, 0x0E, 0xB6, 0x6D, 0x11, 0x0E, 0xDC, 0x32, 0x7D, 0x41, 0x86, 0xC2, 0xF4, 0x78}};
// from SPL
aes_key_generation_source = {"aes_key_generation_source", 0x10, {
0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8}};
// from FS
bis_kek_source = {"bis_kek_source", 0x10, {
0x34, 0xC1, 0xA0, 0xC4, 0x82, 0x58, 0xF8, 0xB4, 0xFA, 0x9E, 0x5E, 0x6A, 0xDA, 0xFC, 0x7E, 0x4F}};
bis_key_source_00 = {"bis_key_source_00", 0x20, {
0xF8, 0x3F, 0x38, 0x6E, 0x2C, 0xD2, 0xCA, 0x32, 0xA8, 0x9A, 0xB9, 0xAA, 0x29, 0xBF, 0xC7, 0x48,
0x7D, 0x92, 0xB0, 0x3A, 0xA8, 0xBF, 0xDE, 0xE1, 0xA7, 0x4C, 0x3B, 0x6E, 0x35, 0xCB, 0x71, 0x06}};
bis_key_source_01 = {"bis_key_source_01", 0x20, {
0x41, 0x00, 0x30, 0x49, 0xDD, 0xCC, 0xC0, 0x65, 0x64, 0x7A, 0x7E, 0xB4, 0x1E, 0xED, 0x9C, 0x5F,
0x44, 0x42, 0x4E, 0xDA, 0xB4, 0x9D, 0xFC, 0xD9, 0x87, 0x77, 0x24, 0x9A, 0xDC, 0x9F, 0x7C, 0xA4}};
bis_key_source_02 = {"bis_key_source_02", 0x20, {
0x52, 0xC2, 0xE9, 0xEB, 0x09, 0xE3, 0xEE, 0x29, 0x32, 0xA1, 0x0C, 0x1F, 0xB6, 0xA0, 0x92, 0x6C,
0x4D, 0x12, 0xE1, 0x4B, 0x2A, 0x47, 0x4C, 0x1C, 0x09, 0xCB, 0x03, 0x59, 0xF0, 0x15, 0xF4, 0xE4}};
//=====================================Hashes=====================================//
// from FS
header_kek_source = {"header_kek_source", 0x9fd1b07be05b8f4d, {
0x18, 0x88, 0xca, 0xed, 0x55, 0x51, 0xb3, 0xed, 0xe0, 0x14, 0x99, 0xe8, 0x7c, 0xe0, 0xd8, 0x68,
0x27, 0xf8, 0x08, 0x20, 0xef, 0xb2, 0x75, 0x92, 0x10, 0x55, 0xaa, 0x4e, 0x2a, 0xbd, 0xff, 0xc2}, 0x10};
header_key_source = {"header_key_source", 0x3e7228ec5873427b, {
0x8f, 0x78, 0x3e, 0x46, 0x85, 0x2d, 0xf6, 0xbe, 0x0b, 0xa4, 0xe1, 0x92, 0x73, 0xc4, 0xad, 0xba,
0xee, 0x16, 0x38, 0x00, 0x43, 0xe1, 0xb8, 0xc4, 0x18, 0xc4, 0x08, 0x9a, 0x8b, 0xd6, 0x4a, 0xa6}, 0x20};
key_area_key_application_source = {"key_area_key_application_source", 0x0b14ccce20dbb59b, {
0x04, 0xad, 0x66, 0x14, 0x3c, 0x72, 0x6b, 0x2a, 0x13, 0x9f, 0xb6, 0xb2, 0x11, 0x28, 0xb4, 0x6f,
0x56, 0xc5, 0x53, 0xb2, 0xb3, 0x88, 0x71, 0x10, 0x30, 0x42, 0x98, 0xd8, 0xd0, 0x09, 0x2d, 0x9e}, 0x10};
key_area_key_ocean_source = {"key_area_key_ocean_source", 0x055b26945075ff88, {
0xfd, 0x43, 0x40, 0x00, 0xc8, 0xff, 0x2b, 0x26, 0xf8, 0xe9, 0xa9, 0xd2, 0xd2, 0xc1, 0x2f, 0x6b,
0xe5, 0x77, 0x3c, 0xbb, 0x9d, 0xc8, 0x63, 0x00, 0xe1, 0xbd, 0x99, 0xf8, 0xea, 0x33, 0xa4, 0x17}, 0x10};
key_area_key_system_source = {"key_area_key_system_source", 0xb2c28e84e1796251, {
0x1f, 0x17, 0xb1, 0xfd, 0x51, 0xad, 0x1c, 0x23, 0x79, 0xb5, 0x8f, 0x15, 0x2c, 0xa4, 0x91, 0x2e,
0xc2, 0x10, 0x64, 0x41, 0xe5, 0x17, 0x22, 0xf3, 0x87, 0x00, 0xd5, 0x93, 0x7a, 0x11, 0x62, 0xf7}, 0x10};
save_mac_kek_source = {"save_mac_kek_source", 0x1e15ac1f6f21a26a, {
0x3D, 0xCB, 0xA1, 0x00, 0xAD, 0x4D, 0xF1, 0x54, 0x7F, 0xE3, 0xC4, 0x79, 0x5C, 0x4B, 0x22, 0x8A,
0xA9, 0x80, 0x38, 0xF0, 0x7A, 0x36, 0xF1, 0xBC, 0x14, 0x8E, 0xEA, 0xF3, 0xDC, 0xD7, 0x50, 0xF4}, 0x10};
save_mac_key_source = {"save_mac_key_source", 0x68b9ed0d367e6dc4, {
0xB4, 0x7B, 0x60, 0x0B, 0x1A, 0xD3, 0x14, 0xF9, 0x41, 0x14, 0x7D, 0x8B, 0x39, 0x1D, 0x4B, 0x19,
0x87, 0xCC, 0x8C, 0x88, 0x4A, 0xC8, 0x9F, 0xFC, 0x91, 0xCA, 0xE2, 0x21, 0xC5, 0x24, 0x51, 0xF7}, 0x10};
sd_card_kek_source = {"sd_card_kek_source", 0xc408d710a3b821eb, {
0x6B, 0x2E, 0xD8, 0x77, 0xC2, 0xC5, 0x23, 0x34, 0xAC, 0x51, 0xE5, 0x9A, 0xBF, 0xA7, 0xEC, 0x45,
0x7F, 0x4A, 0x7D, 0x01, 0xE4, 0x62, 0x91, 0xE9, 0xF2, 0xEA, 0xA4, 0x5F, 0x01, 0x1D, 0x24, 0xB7}, 0x10};
sd_card_nca_key_source = {"sd_card_nca_key_source", 0xbea347c9f8472947, {
0x2E, 0x75, 0x1C, 0xEC, 0xF7, 0xD9, 0x3A, 0x2B, 0x95, 0x7B, 0xD5, 0xFF, 0xCB, 0x08, 0x2F, 0xD0,
0x38, 0xCC, 0x28, 0x53, 0x21, 0x9D, 0xD3, 0x09, 0x2C, 0x6D, 0xAB, 0x98, 0x38, 0xF5, 0xA7, 0xCC}, 0x20};
sd_card_save_key_source = {"sd_card_save_key_source", 0xf87fe8c3688c3022, {
0xD4, 0x82, 0x74, 0x35, 0x63, 0xD3, 0xEA, 0x5D, 0xCD, 0xC3, 0xB7, 0x4E, 0x97, 0xC9, 0xAC, 0x8A,
0x34, 0x21, 0x64, 0xFA, 0x04, 0x1A, 0x1D, 0xC8, 0x0F, 0x17, 0xF6, 0xD3, 0x1E, 0x4B, 0xC0, 0x1C}, 0x20};
// from ES
eticket_rsa_kek_source = {"eticket_rsa_kek_source", 0x76d15de09d439bdc, {
0xB7, 0x1D, 0xB2, 0x71, 0xDC, 0x33, 0x8D, 0xF3, 0x80, 0xAA, 0x2C, 0x43, 0x35, 0xEF, 0x88, 0x73,
0xB1, 0xAF, 0xD4, 0x08, 0xE8, 0x0B, 0x35, 0x82, 0xD8, 0x71, 0x9F, 0xC8, 0x1C, 0x5E, 0x51, 0x1C}, 0x10};
eticket_rsa_kekek_source = {"eticket_rsa_kekek_source", 0x97436d4ff39703da, {
0xE8, 0x96, 0x5A, 0x18, 0x7D, 0x30, 0xE5, 0x78, 0x69, 0xF5, 0x62, 0xD0, 0x43, 0x83, 0xC9, 0x96,
0xDE, 0x48, 0x7B, 0xBA, 0x57, 0x61, 0x36, 0x3D, 0x2D, 0x4D, 0x32, 0x39, 0x18, 0x66, 0xA8, 0x5C}, 0x10};
// from SSL
ssl_rsa_kek_source_x = {"ssl_rsa_kek_source_x", 0xa7084dadd5d9da93, {
0x69, 0xA0, 0x8E, 0x62, 0xE0, 0xAE, 0x50, 0x7B, 0xB5, 0xDA, 0x0E, 0x65, 0x17, 0x9A, 0xE3, 0xBE,
0x05, 0x1F, 0xED, 0x3C, 0x49, 0x94, 0x1D, 0xF4, 0xEF, 0x29, 0x56, 0xD3, 0x6D, 0x30, 0x11, 0x0C}, 0x10};
ssl_rsa_kek_source_y = {"ssl_rsa_kek_source_y", 0xbafd95c9f258dc4a, {
0x1C, 0x86, 0xF3, 0x63, 0x26, 0x54, 0x17, 0xD4, 0x99, 0x22, 0x9E, 0xB1, 0xC4, 0xAD, 0xC7, 0x47,
0x9B, 0x2A, 0x15, 0xF9, 0x31, 0x26, 0x1F, 0x31, 0xEE, 0x67, 0x76, 0xAE, 0xB4, 0xC7, 0x65, 0x42}, 0x10};
// from TSEC
tsec_root_key = {"tsec_root_key", 0x57b73665b0bbd424, {
0x03, 0x2a, 0xdf, 0x0a, 0x6b, 0xe7, 0xdd, 0x7c, 0x11, 0xa4, 0xfa, 0x5c, 0xd6, 0x4a, 0x15, 0x75,
0xe4, 0x69, 0xb9, 0xda, 0x5d, 0x8b, 0xd5, 0x6a, 0x12, 0xd0, 0xfb, 0xc0, 0xeb, 0x84, 0xe8, 0xe7}, 0x10};
rsa_oaep_kek_generation_source = {"rsa_oaep_kek_generation_source", 0x10};
rsa_private_kek_generation_source = {"rsa_private_kek_generation_source", 0x10};
es_keys = {
&eticket_rsa_kek_source,
&eticket_rsa_kekek_source
};
fs_rodata_keys = {
&bis_kek_source,
&bis_key_source_00,
&bis_key_source_01,
&bis_key_source_02,
&header_kek_source,
&key_area_key_application_source,
&key_area_key_ocean_source,
&key_area_key_system_source,
&save_mac_kek_source,
&save_mac_key_source
};
// only look for sd keys if at least firm 2.0.0
if (kernelAbove200()) {
fs_rodata_keys.insert(fs_rodata_keys.end(), {
&sd_card_kek_source,
&sd_card_nca_key_source,
&sd_card_save_key_source
});
}
package1ldr_keys = {
&keyblob_mac_key_source,
&master_key_source,
&per_console_key_source
};
ssl_keys = {
&ssl_rsa_kek_source_x,
&ssl_rsa_kek_source_y
};
tz_keys = {
&aes_kek_generation_source,
&package2_key_source,
&titlekek_source,
&retail_specific_aes_key_source,
&aes_kek_seed_01,
&aes_kek_seed_03
};
};
int KeyCollection::get_keys() {
Stopwatch total_time;
total_time.start();
int64_t profiler_time = profile(Common::get_tegra_keys, sbk, tsec, tsec_root_key);
if ((sbk.found() && tsec.found()) || tsec_root_key.found()) {
Common::draw_text_with_time(0x10, 0x80, GREEN, "Get Tegra keys...", profiler_time);
} else {
Common::draw_text(0x010, 0x80, RED, "Get Tegra keys...");
Common::draw_text(0x190, 0x80, RED, "Failed");
Common::draw_text(0x190, 0x20, RED, "Warning: Saving limited keyset.");
Common::draw_text(0x190, 0x40, RED, "Dump Tegra keys with payload and run again to get all keys.");
}
profiler_time = profile(&KeyCollection::get_memory_keys, *this);
Common::draw_text_with_time(0x10, 0x0a0, GREEN, "Get keys from memory...", profiler_time);
profiler_time = profile(&KeyCollection::get_master_keys, *this);
Common::draw_text_with_time(0x10, 0x0c0, GREEN, "Get master keys...", profiler_time);
profiler_time = profile(&KeyCollection::derive_keys, *this);
Common::draw_text_with_time(0x10, 0x0e0, GREEN, "Derive remaining keys...", profiler_time);
profiler_time = profile(&KeyCollection::save_keys, *this);
Common::draw_text_with_time(0x10, 0x100, GREEN, "Saving keys to keyfile...", profiler_time);
total_time.stop();
Common::draw_line(0x8, 0x110, 0x280, GREEN);
Common::draw_text_with_time(0x10, 0x130, GREEN, "Total time elapsed:", total_time.get_elapsed());
char keys_str[32];
sprintf(keys_str, "Total keys found: %lu", Key::get_saved_key_count());
Common::draw_text(0x2a0, 0x130, CYAN, keys_str);
Common::draw_text(0x30, 0x160, YELLOW, "WARNING: dumping titlekeys may crash homebrew or games UNLESS you reboot afterwards");
Common::draw_text(0x160, 0x180, CYAN, ">> Press A to dump titlekeys or + to exit <<");
for(;;) {
hidScanInput();
u64 kDown = hidKeysDown(CONTROLLER_P1_AUTO);
if (kDown & KEY_PLUS)
return Status_success_no_titlekeys;
else if (kDown & KEY_A)
break;
consoleUpdate(NULL);
}
Common::draw_text(0x10, 0x1b0, CYAN, "Dumping titlekeys...");
consoleUpdate(NULL);
profiler_time = profile(&KeyCollection::get_titlekeys, *this);
if (titlekeys_dumped > 0) {
Common::draw_text_with_time(0x10, 0x1b0, GREEN, "Dumping titlekeys...", profiler_time);
sprintf(keys_str, "Titlekeys found: %lu", titlekeys_dumped);
Common::draw_text(0x2a0, 0x1b0, CYAN, keys_str);
return Status_success_titlekeys;
} else {
Common::draw_text(0x010, 0x1b0, RED, "Dumping titlekeys...");
Common::draw_text(0x190, 0x1b0, RED, "Failed. Reboot and try again!");
return Status_success_titlekeys_failed;
}
}
void KeyCollection::get_master_keys() {
char keynum[] = "00";
if (sbk.found() && tsec.found()) {
for (u8 i = 0; i < keyblob_key_source.size(); i++) {
sprintf(keynum, "%02x", i);
keyblob_key.push_back(Key {"keyblob_key_" + std::string(keynum), 0x10,
sbk.aes_decrypt_ecb(tsec.aes_decrypt_ecb(keyblob_key_source[i].key))});
keyblob_mac_key.push_back(Key {"keyblob_mac_key_" + std::string(keynum), 0x10,
keyblob_key.back().aes_decrypt_ecb(keyblob_mac_key_source.key)});
}
}
if (!keyblob_mac_key.empty()) {
KeyLocation Keyblobs;
Keyblobs.get_keyblobs();
u8 index = 0;
for (byte_vector::const_iterator It = Keyblobs.data.begin(); It != Keyblobs.data.end(); It += 0x200) {
sprintf(keynum, "%02x", index);
encrypted_keyblob.push_back(Key {"encrypted_keyblob_" + std::string(keynum), 0xb0, byte_vector(It, It + 0xb0)});
byte_vector keyblob_mac(keyblob_mac_key[index].cmac(byte_vector(encrypted_keyblob.back().key.begin() + 0x10, encrypted_keyblob.back().key.end())));
if (!std::equal(encrypted_keyblob.back().key.begin(), encrypted_keyblob.back().key.begin() + 0x10, keyblob_mac.begin())) {
// if keyblob cmac fails, invalidate all console-unique keys to prevent faulty derivation or saving bad values
sbk = Key();
tsec = Key();
keyblob_key.clear();
keyblob_mac_key.clear();
break;
}
index++;
}
}
for (u8 i = 0; i < keyblob_key.size(); i++) {
sprintf(keynum, "%02x", i);
keyblob.push_back(Key {"keyblob_" + std::string(keynum), 0x90,
keyblob_key[i].aes_decrypt_ctr(
byte_vector(encrypted_keyblob[i].key.begin() + 0x20, encrypted_keyblob[i].key.end()),
byte_vector(encrypted_keyblob[i].key.begin() + 0x10, encrypted_keyblob[i].key.begin() + 0x20))});
package1_key.push_back(Key {"package1_key_" + std::string(keynum), 0x10,
byte_vector(keyblob.back().key.begin() + 0x80, keyblob.back().key.end())});
master_kek.push_back(Key {"master_kek_" + std::string(keynum), 0x10,
byte_vector(keyblob.back().key.begin(), keyblob.back().key.begin() + 0x10)});
master_key.push_back(Key {"master_key_" + std::string(keynum), 0x10, master_kek.back().aes_decrypt_ecb(master_key_source.key)});
}
if (tsec_root_key.found()) {
if (master_kek.empty()) {
master_kek.resize(KNOWN_KEYBLOBS);
master_key.resize(KNOWN_KEYBLOBS);
}
master_kek.push_back(Key {"master_kek_06", 0x10, tsec_root_key.aes_decrypt_ecb(master_kek_source[KNOWN_KEYBLOBS].key)});
master_key.push_back(Key {"master_key_06", 0x10, master_kek[KNOWN_KEYBLOBS].aes_decrypt_ecb(master_key_source.key)});
if (!master_key[KNOWN_KEYBLOBS - 1].found()) {
for (int i = KNOWN_KEYBLOBS - 1; i >= 0; i--) {
sprintf(keynum, "%02x", i);
master_key[i] = Key {"master_key_" + std::string(keynum), 0x10, master_key[i+1].aes_decrypt_ecb(mkey_vector[i+1].key)};
}
byte_vector zeroes(0x10, 0);
if (!std::equal(zeroes.begin(), zeroes.end(), master_key[0].aes_decrypt_ecb(mkey_vector[0].key).begin())) {
// if last mkey doesn't decrypt vector to zeroes, invalidate all master_keys and keks
master_kek.clear();
master_key.clear();
}
}
}
}
void KeyCollection::get_memory_keys() {
KeyLocation
ESRodata,
FSRodata,
FSData,
SSLRodata;
FSRodata.get_from_memory(FS_TID, SEG_RODATA);
FSData.get_from_memory(FS_TID, SEG_DATA);
for (auto k : fs_rodata_keys)
k->find_key(FSRodata.data);
header_key_source.find_key(FSData.data);
ESRodata.get_from_memory(ES_TID, SEG_RODATA);
for (auto k : es_keys)
k->find_key(ESRodata.data);
SSLRodata.get_from_memory(SSL_TID, SEG_RODATA);
for (auto k : ssl_keys)
k->find_key(SSLRodata.data);
}
void KeyCollection::derive_keys() {
header_key = {"header_key", 0x20, {}};
if (header_kek_source.found() && header_key_source.found()) {
u8 tempheaderkek[0x10], tempheaderkey[0x20];
splCryptoGenerateAesKek(header_kek_source.key.data(), 0, 0, tempheaderkek);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data(), tempheaderkey);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data() + 0x10, tempheaderkey + 0x10);
header_key = {"header_key", 0x20, byte_vector(&tempheaderkey[0], &tempheaderkey[0x20])};
}
for (u8 i = 0; i < aes_kek_generation_source.key.size(); i++) {
rsa_oaep_kek_generation_source.key.push_back(aes_kek_generation_source.key[i] ^ aes_kek_seed_03.key[i]);
rsa_private_kek_generation_source.key.push_back(aes_kek_generation_source.key[i] ^ aes_kek_seed_01.key[i]);
}
rsa_oaep_kek_generation_source.set_found();
rsa_private_kek_generation_source.set_found();
if (!keyblob_key.empty())
device_key = Key {"device_key", 0x10, keyblob_key[0].aes_decrypt_ecb(per_console_key_source.key)};
if (device_key.found() && save_mac_kek_source.found() && save_mac_key_source.found()) {
Key kek = {save_mac_kek_source.generate_kek(device_key, aes_kek_generation_source, Key {}), 0x10};
save_mac_key = Key {"save_mac_key", 0x10, kek.aes_decrypt_ecb(save_mac_key_source.key)};
}
if (device_key.found()) {
Key kek = {device_key.aes_decrypt_ecb(retail_specific_aes_key_source.key), 0x10};
bis_key.push_back(Key {"bis_key_00", 0x20, kek.aes_decrypt_ecb(bis_key_source_00.key)});
kek = Key {bis_kek_source.generate_kek(device_key, aes_kek_generation_source, aes_key_generation_source), 0x10};
bis_key.push_back(Key {"bis_key_01", 0x20, kek.aes_decrypt_ecb(bis_key_source_01.key)});
bis_key.push_back(Key {"bis_key_02", 0x20, kek.aes_decrypt_ecb(bis_key_source_02.key)});
bis_key.push_back(Key {"bis_key_03", 0x20, bis_key[2].key});\
}
char keynum[] = "00";
for (u8 i = 0; i < master_key.size(); i++) {
if (!master_key[i].found())
continue;
sprintf(keynum, "%02x", i);
key_area_key_application.push_back(Key {"key_area_key_application_" + std::string(keynum), 0x10,
key_area_key_application_source.generate_kek(master_key[i], aes_kek_generation_source, aes_key_generation_source)});
key_area_key_ocean.push_back(Key {"key_area_key_ocean_" + std::string(keynum), 0x10,
key_area_key_ocean_source.generate_kek(master_key[i], aes_kek_generation_source, aes_key_generation_source)});
key_area_key_system.push_back(Key {"key_area_key_system_" + std::string(keynum), 0x10,
key_area_key_system_source.generate_kek(master_key[i], aes_kek_generation_source, aes_key_generation_source)});
package2_key.push_back(Key {"package2_key_" + std::string(keynum), 0x10, master_key[i].aes_decrypt_ecb(package2_key_source.key)});
titlekek.push_back(Key {"titlekek_" + std::string(keynum), 0x10, master_key[i].aes_decrypt_ecb(titlekek_source.key)});
}
if (eticket_rsa_kek_source.found() && eticket_rsa_kekek_source.found() && !master_key.empty())
eticket_rsa_kek = Key {"eticket_rsa_kek", 0x10,
eticket_rsa_kekek_source.generate_kek(master_key[0], rsa_oaep_kek_generation_source, eticket_rsa_kek_source)};
if (ssl_rsa_kek_source_x.found() && ssl_rsa_kek_source_y.found() && !master_key.empty())
ssl_rsa_kek = Key {"ssl_rsa_kek", 0x10,
ssl_rsa_kek_source_x.generate_kek(master_key[0], rsa_private_kek_generation_source, ssl_rsa_kek_source_y)};
}
void KeyCollection::save_keys() {
std::ofstream key_file("/switch/prod.keys");
aes_kek_generation_source.save_key(key_file);
aes_key_generation_source.save_key(key_file);
bis_kek_source.save_key(key_file);
bis_key_source_00.save_key(key_file);
bis_key_source_01.save_key(key_file);
bis_key_source_02.save_key(key_file);
for (auto k : bis_key)
k.save_key(key_file);
device_key.save_key(key_file);
eticket_rsa_kek.save_key(key_file);
for (auto k : es_keys)
k->save_key(key_file);
header_kek_source.save_key(key_file);
header_key.save_key(key_file);
header_key_source.save_key(key_file);
for (auto k : key_area_key_application)
k.save_key(key_file);
key_area_key_application_source.save_key(key_file);
for (auto k : key_area_key_ocean)
k.save_key(key_file);
key_area_key_ocean_source.save_key(key_file);
for (auto k : key_area_key_system)
k.save_key(key_file);
key_area_key_system_source.save_key(key_file);
for (auto k : keyblob)
k.save_key(key_file);
for (auto k : keyblob_key)
k.save_key(key_file);
for (auto k : keyblob_key_source)
k.save_key(key_file);
for (auto k : keyblob_mac_key)
k.save_key(key_file);
keyblob_mac_key_source.save_key(key_file);
for (auto k : master_kek)
k.save_key(key_file);
for (auto k : master_kek_source)
k.save_key(key_file);
for (auto k : master_key)
k.save_key(key_file);
master_key_source.save_key(key_file);
for (auto k : package1_key)
k.save_key(key_file);
for (auto k : package2_key)
k.save_key(key_file);
package2_key_source.save_key(key_file);
per_console_key_source.save_key(key_file);
retail_specific_aes_key_source.save_key(key_file);
rsa_oaep_kek_generation_source.save_key(key_file);
rsa_private_kek_generation_source.save_key(key_file);
save_mac_kek_source.save_key(key_file);
save_mac_key.save_key(key_file);
save_mac_key_source.save_key(key_file);
sbk.save_key(key_file);
sd_card_kek_source.save_key(key_file);
sd_card_nca_key_source.save_key(key_file);
sd_card_save_key_source.save_key(key_file);
ssl_rsa_kek.save_key(key_file);
for (auto k : ssl_keys)
k->save_key(key_file);
for (auto k : titlekek)
k.save_key(key_file);
titlekek_source.save_key(key_file);
tsec.save_key(key_file);
tsec_root_key.save_key(key_file);
}
void KeyCollection::get_titlekeys() {
if (!kernelAbove200() || !eticket_rsa_kek.found())
return;
// get extended eticket RSA key from PRODINFO
u8 eticket_data[0x244] = {};
setcalInitialize();
setcalGetEticketDeviceKey(eticket_data);
setcalExit();
byte_vector dec_keypair = eticket_rsa_kek.aes_decrypt_ctr(
byte_vector(eticket_data + 0x14, eticket_data + 0x244),
byte_vector(eticket_data + 4, eticket_data + 0x14)
);
// public exponent must be 65537 == 0x10001
if (!(dec_keypair[0x201] == 1) || !(dec_keypair[0x203] == 1))
return;
u8 *D = &dec_keypair[0], *N = &dec_keypair[0x100], *E = &dec_keypair[0x200];
if (!test_key_pair(E, D, N))
return;
FsFileSystem save_fs;
Result rc;
for(size_t attempts = 0; attempts < 100; attempts++) {
pmshellTerminateProcessByTitleId(ES_TID);
if (R_SUCCEEDED(rc = fsMount_SystemSaveData(&save_fs, ES_COMMON_SAVE_ID)))
break;
}
if (R_FAILED(rc))
return;
if (fsdevMountDevice("save", save_fs) == -1)
return;
char ca_issuer[4], titlekey_block[0x100], rights_id[0x10], write_string[0x20];
std::ofstream titlekey_file("/switch/title.keys");
std::ifstream common_ticket_bin("save:/ticket.bin", std::ios::binary);
for (size_t i = 0; ; i += 0x400) {
common_ticket_bin.seekg(i + 0x140);
common_ticket_bin.read(ca_issuer, 4);
if (!std::equal(ca_issuer, ca_issuer + 4, "Root"))
break;
common_ticket_bin.seekg(i + 0x180);
common_ticket_bin.read(titlekey_block, 0x10);
common_ticket_bin.seekg(i + 0x2a0);
common_ticket_bin.read(rights_id, 0x10);
for (size_t j = 0; j < 0x10; j++)
sprintf(&write_string[j*2], "%02x", rights_id[j]);
titlekey_file.write(write_string, 0x20);
titlekey_file.write(" = ", 3);
for (size_t j = 0; j < 0x10; j++)
sprintf(&write_string[j*2], "%02x", titlekey_block[j]);
titlekey_file.write(write_string, 0x20);
titlekey_file.write("\n", 1);
titlekeys_dumped++;
}
fsdevUnmountDevice("save");
if (R_FAILED(fsMount_SystemSaveData(&save_fs, ES_PERSONALIZED_SAVE_ID)) ||
(fsdevMountDevice("save", save_fs) == -1))
return;
std::ifstream personalized_ticket_bin("save:/ticket.bin", std::ios::binary);
u8 M[0x100];
for (size_t i = 0; ; i += 0x400) {
personalized_ticket_bin.seekg(i + 0x140);
personalized_ticket_bin.read(ca_issuer, 4);
if (!std::equal(ca_issuer, ca_issuer + 4, "Root"))
break;
personalized_ticket_bin.seekg(i + 0x180);
personalized_ticket_bin.read(titlekey_block, 0x100);
splUserExpMod(titlekey_block, N, D, 0x100, M);
// decrypts the titlekey from personalized ticket
u8 salt[0x20], db[0xdf];
mgf1(M + 0x21, 0xdf, salt, 0x20);
for (size_t j = 0; j < 0x20; j++)
salt[j] ^= M[j + 1];
mgf1(salt, 0x20, db, 0xdf);
for (size_t j = 0; j < 0xdf; j++)
db[j] ^= M[j + 0x21];
// verify it starts with hash of null string
if (!std::equal(db, db + 0x20, null_hash))
continue;
personalized_ticket_bin.seekg(i + 0x2a0);
personalized_ticket_bin.read(rights_id, 0x10);
for (size_t j = 0; j < 0x10; j++)
sprintf(&write_string[j*2], "%02x", rights_id[j]);
titlekey_file.write(write_string, 0x20);
titlekey_file.write(" = ", 3);
for (size_t j = 0; j < 0x10; j++)
sprintf(&write_string[j*2], "%02x", db[j + 0xcf]);
titlekey_file.write(write_string, 0x20);
titlekey_file.write("\n", 1);
titlekeys_dumped++;
}
fsdevUnmountDevice("save");
}
void KeyCollection::mgf1(const u8 *data, size_t data_length, u8 *mask, size_t mask_length) {
u8 data_counter[data_length + 4] = {};
std::copy(data, data + data_length, data_counter);
Common::sha256(data_counter, mask, data_length + 4);
for (u32 i = 1; i < (mask_length / 0x20) + 1; i++) {
for (size_t j = 0; j < 4; j++)
data_counter[data_length + 3 - j] = (i >> (8 * j)) & 0xff;
if (i * 0x20 <= mask_length)
Common::sha256(data_counter, mask + (i * 0x20), data_length + 4);
else {
u8 temp_mask[0x20];
Common::sha256(data_counter, temp_mask, data_length + 4);
std::copy(temp_mask, temp_mask + mask_length - (i * 0x20), mask + (i * 0x20));
}
}
}
bool KeyCollection::test_key_pair(const void *E, const void *D, const void *N) {
u8 X[0x100] = {0}, Y[0x100] = {0}, Z[0x100] = {0};
// 0xCAFEBABE
X[0xfc] = 0xca; X[0xfd] = 0xfe; X[0xfe] = 0xba; X[0xff] = 0xbe;
splUserExpMod(X, N, D, 0x100, Y);
splUserExpMod(Y, N, E, 4, Z);
for (size_t i = 0; i < 0x100; i++)
if (X[i] != Z[i])
return false;
return true;
}