3 Commits
v1.2 ... v1.2.2

Author SHA1 Message Date
867d2d6520 Do not overwrite newer keyfile from Lockpick_RCM 2019-03-05 13:09:37 -05:00
986468a32b Bump version to v1.2.1 2019-02-25 11:53:47 -05:00
9deeefee9d Update for Hekate 4.8 + bis keys without master key 2019-02-25 11:52:49 -05:00
7 changed files with 76 additions and 25 deletions

View File

@ -32,7 +32,7 @@ include $(DEVKITPRO)/libnx/switch_rules
#---------------------------------------------------------------------------------
APP_TITLE := Lockpick
APP_AUTHOR := shchmue
APP_VERSION := 1.2
APP_VERSION := 1.2.2
TARGET := $(subst $e ,_,$(notdir $(APP_TITLE)))
BUILD := build

View File

@ -2,13 +2,15 @@ Lockpick
=
Lockpick is a ground-up C++17 rewrite of homebrew key derivation software, namely [kezplez-nx](https://github.com/tesnos/kezplez-nx). It also dumps titlekeys. This will dump all keys through `*_key_05` on firmwares below `6.2.0` and through `*_key_06` on `6.2.0`.
Due to key generation changes introduced in `7.0.0`, Lockpick is not able to dump keys ending in 07 at all. Furthermore, unfortunately the public method to dump `tsec_root_key` is only available on firmware `6.2.0` so `7.x` consoles can only dump through keys ending in 05.
What this software does differently
=
* Dumps `titlekeys` and SD seed
* Dumps all keys through `6.2.0`
* Uses the superfast `xxHash` instead of `sha256` when searching exefs for keys for a ~5x speed improvement
* Gets all possible keys from running process memory - this means no need to decrypt `Package2` at all, let alone decompress `KIP`s
* Gets `header_key` without `tsec`, `sbk`, `master_key_00` or `aes` sources - which may or may not be the same way `ChoiDujourNX` does it :eyes: (and I'm gonna issue a challenge to homebrew title installers to implement similar code so you don't need your users to use separate software like this :stuck_out_tongue_winking_eye: it's up to you to figure out if the same can be done for `key_area_keys` if needed)
* Gets bis keys and `header_key` without `tsec`, `sbk`, `master_key_00` or `aes` sources. Shoutout to exelix11 for using this method in [SwitchThemeInjector](https://github.com/exelix11/SwitchThemeInjector)! Homebrew devs should be doing this instead of requiring users to provide key files!
Usage
=

View File

@ -1,4 +1,16 @@
# Changelog
## Version 1.2.2
* Do not overwrite existing keyfile that contains master_key_07
* Read eticket_rsa_kek from existing keyfile in case user is only running this for titlekeys
* Create /switch folder if needed
## Version 1.2.1
* Generate bis keys without master keys
* Update file size check to support Hekate v4.8 TSEC dump
* Fixed prod.keys alphabetization error
* Fixed build warning for ff.c
* Added in-app disclaimer about which keys can be dumped
## Version 1.2
* Update for libnx v2.0.0 compatibility and still runs when built with v1.6.0
* The binary got even smaller!

View File

@ -141,7 +141,9 @@ namespace Common {
framebuf = (u32 *)gfxGetFramebuffer(&framebuf_width, NULL);
memset(framebuf, 0, gfxGetFramebufferSize());
#endif
draw_text(0x10, 0x020, YELLOW, "Lockpick! by shchmue");
draw_text(0x010, 0x020, YELLOW, "Lockpick! by shchmue");
draw_text(0x190, 0x020, YELLOW, "Note: This can only dump keys 00-05 (or 00-06 on 6.2.0)");
draw_text(0x190, 0x040, YELLOW, "Use Lockpick_RCM for newer keys on firmware 7.0.0+!");
draw_set_rect(814, 452 + 42 * 0, 450, 42, FLAG_RED);
draw_set_rect(814, 452 + 42 * 1, 450, 42, FLAG_ORANGE);
@ -185,7 +187,7 @@ namespace Common {
sbk = Key("secure_boot_key", 0x10, temp_key);
fclose(fuse_file);
}
else if (!tsec.found() && (p.file_size() == 0x30) &&
else if (!tsec.found() && (p.file_size() == 0x20 || p.file_size() == 0x30) &&
(std::string("tsec").compare(std::string(p.path().filename()).substr(0, 4)) == 0))
{
FILE *tsec_file = fopen(p.path().c_str(), "rb");

View File

@ -21,6 +21,7 @@
#include <algorithm>
#include <chrono>
#include <filesystem>
#include <functional>
#include <string>
#include <unordered_map>
@ -228,8 +229,8 @@ void KeyCollection::get_keys() {
} else {
Common::draw_text(0x010, 0x60, RED, "Get Tegra keys...");
Common::draw_text(0x190, 0x60, 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.");
Common::draw_text(0x2a0, 0x60, RED, "Warning: Saving limited keyset.");
Common::draw_text(0x2a0, 0x80, RED, "Dump TSEC and Fuses with Hekate.");
}
profiler_time = profile(&KeyCollection::get_memory_keys, *this);
@ -241,8 +242,31 @@ void KeyCollection::get_keys() {
profiler_time = profile(&KeyCollection::derive_keys, *this);
Common::draw_text_with_time(0x10, 0x0c0, GREEN, "Derive remaining keys...", profiler_time);
profiler_time = profile(&KeyCollection::save_keys, *this);
Common::draw_text_with_time(0x10, 0x0e0, GREEN, "Saving keys to keyfile...", profiler_time);
// avoid crash on CFWs that don't use /switch folder
if (!std::filesystem::exists("/switch"))
std::filesystem::create_directory("/switch");
// since Lockpick_RCM can dump newer keys, check for existing keyfile
bool Lockpick_RCM_file_found = false;
if (std::filesystem::exists("/switch/prod.keys")) {
FILE *key_file = fopen("/switch/prod.keys", "r");
char line[0x200];
while (fgets(line, sizeof(line), key_file)) {
if (strncmp("master_key_07", line, 13) == 0) {
Lockpick_RCM_file_found = true;
} else if (!eticket_rsa_kek.found() && (strncmp("eticket_rsa_kek", line, 15)) == 0) {
// grab eticket_rsa_kek from existing file to make sure we can dump titlekeys
eticket_rsa_kek = Key("eticket_rsa_kek", 0x10, Common::key_string_to_byte_vector(line));
}
}
fclose(key_file);
}
if (!Lockpick_RCM_file_found) {
profiler_time = profile(&KeyCollection::save_keys, *this);
Common::draw_text_with_time(0x10, 0x0e0, GREEN, "Saving keys to keyfile...", profiler_time);
} else {
Common::draw_text(0x10, 0x0e0, YELLOW, "Saving keys to keyfile...");
Common::draw_text(0x190, 0x0e0, YELLOW, "Newer keyfile found. Skipped overwriting keys");
}
total_time.stop();
Common::draw_line(0x8, 0xf0, 0x280, GREEN);
@ -369,10 +393,32 @@ void KeyCollection::derive_keys() {
header_key = {"header_key", 0x20, {}};
if (header_kek_source.found() && header_key_source.found()) {
u8 tempheaderkek[0x10], tempheaderkey[0x20];
splCryptoInitialize();
splCryptoGenerateAesKek(header_kek_source.key.data(), 0, 0, tempheaderkek);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data(), tempheaderkey);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data() + 0x00, tempheaderkey + 0x00);
splCryptoGenerateAesKey(tempheaderkek, header_key_source.key.data() + 0x10, tempheaderkey + 0x10);
header_key = {"header_key", 0x20, byte_vector(&tempheaderkey[0], &tempheaderkey[0x20])};
header_key = {"header_key", 0x20, byte_vector(tempheaderkey, tempheaderkey + 0x20)};
splCryptoExit();
}
if (bis_key_source_00.found() && bis_key_source_01.found() && bis_key_source_02.found()) {
u8 tempbiskek[0x10], tempbiskey[0x20];
splFsInitialize();
splFsGenerateSpecificAesKey(bis_key_source_00.key.data() + 0x00, 0, 0, tempbiskey + 0x00);
splFsGenerateSpecificAesKey(bis_key_source_00.key.data() + 0x10, 0, 0, tempbiskey + 0x10);
bis_key.push_back(Key {"bis_key_00", 0x20, byte_vector(tempbiskey, tempbiskey + 0x20)});
splFsExit();
splCryptoInitialize();
splCryptoGenerateAesKek(bis_kek_source.key.data(), 0, 1, tempbiskek);
splCryptoGenerateAesKey(tempbiskek, bis_key_source_01.key.data() + 0x00, tempbiskey + 0x00);
splCryptoGenerateAesKey(tempbiskek, bis_key_source_01.key.data() + 0x10, tempbiskey + 0x10);
bis_key.push_back(Key {"bis_key_01", 0x20, byte_vector(tempbiskey, tempbiskey + 0x20)});
splCryptoGenerateAesKey(tempbiskek, bis_key_source_02.key.data() + 0x00, tempbiskey + 0x00);
splCryptoGenerateAesKey(tempbiskek, bis_key_source_02.key.data() + 0x10, tempbiskey + 0x10);
bis_key.push_back(Key {"bis_key_02", 0x20, byte_vector(tempbiskey, tempbiskey + 0x20)});
bis_key.push_back(Key {"bis_key_03", 0x20, bis_key[2].key});
splCryptoExit();
}
for (u8 i = 0; i < aes_kek_generation_source.key.size(); i++) {
@ -390,15 +436,6 @@ void KeyCollection::derive_keys() {
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())
@ -467,11 +504,11 @@ void KeyCollection::save_keys() {
aes_kek_generation_source.save_key(key_file);
aes_key_generation_source.save_key(key_file);
bis_kek_source.save_key(key_file);
for (auto k : bis_key)
k.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)
@ -516,11 +553,11 @@ void KeyCollection::save_keys() {
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);
sd_seed.save_key(key_file);
sbk.save_key(key_file);
ssl_rsa_kek.save_key(key_file);
for (auto k : ssl_keys)
k->save_key(key_file);

View File

@ -15,7 +15,7 @@
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 2
#define FF_FS_MINIMIZE 1
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.

View File

@ -23,7 +23,6 @@ extern "C" void userAppInit()
{
plInitialize();
pmdmntInitialize();
splCryptoInitialize();
splInitialize();
}
@ -31,7 +30,6 @@ extern "C" void userAppExit()
{
plExit();
pmdmntExit();
splCryptoExit();
splExit();
}