Add options for building / decrypting / encrypting aeskeydb.bin

This commit is contained in:
d0k3 2017-04-17 23:45:49 +02:00
parent 679d90dea7
commit b278be0b49
6 changed files with 176 additions and 11 deletions

View File

@ -50,7 +50,7 @@
// GodMode9 version // GodMode9 version
#define VERSION "1.0.9" #define VERSION "1.1.0"
// Maximum payload size (arbitrary value!) // Maximum payload size (arbitrary value!)
#define SELF_MAX_SIZE (320 * 1024) // 320kB #define SELF_MAX_SIZE (320 * 1024) // 320kB

View File

@ -19,4 +19,5 @@ typedef struct {
} __attribute__((packed)) AesKeyInfo; } __attribute__((packed)) AesKeyInfo;
u32 GetUnitKeysType(void); u32 GetUnitKeysType(void);
void CryptAesKeyInfo(AesKeyInfo* info);
u32 LoadKeyFromFile(u8* key, u32 keyslot, char type, char* id); u32 LoadKeyFromFile(u8* key, u32 keyslot, char type, char* id);

View File

@ -32,11 +32,12 @@
#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM|SYS_TICKDB)) #define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM|SYS_TICKDB))
#define FYTPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_BOSS|SYS_FIRM)) #define FYTPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_BOSS|SYS_FIRM))
#define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM)) #define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM|BIN_KEYDB))
#define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS)) #define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|BIN_KEYDB))
#define FTYPE_CIABUILD(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD)) #define FTYPE_CIABUILD(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
#define FTYPE_CIABUILD_L(tp) (FTYPE_CIABUILD(tp) && (tp&(GAME_TMD))) #define FTYPE_CIABUILD_L(tp) (FTYPE_CIABUILD(tp) && (tp&(GAME_TMD)))
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB)) #define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
#define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY))
#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS)) #define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS))
#define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR)) #define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR))
#define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI)) #define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))

119
source/game/keydbutil.c Normal file
View File

@ -0,0 +1,119 @@
#include "keydbutil.h"
#include "fsperm.h"
#include "filetype.h"
#include "unittype.h"
#include "vff.h"
#include "ui.h"
#define MAX_KEYDB_SIZE (TEMP_BUFFER_SIZE)
u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt) {
AesKeyInfo* keydb = (AesKeyInfo*) MAIN_BUFFER;
const char* path_out = (inplace) ? path : OUTPUT_PATH "/" KEYDB_NAME;
u32 n_keys;
UINT bt, btw;
// write permissions
if (!CheckWritePermissions(path_out))
return 1;
// load key database
if ((fvx_qread(path, keydb, 0, MAIN_BUFFER_SIZE, &bt) != FR_OK) ||
(bt % sizeof(AesKeyInfo)) || (bt >= MAIN_BUFFER_SIZE))
return 1;
// en-/decrypt keys
n_keys = bt / sizeof(AesKeyInfo);
for (u32 i = 0; i < n_keys; i++) {
if ((bool) keydb[i].isEncrypted == !encrypt)
CryptAesKeyInfo(&(keydb[i]));
}
// dump key database
if (!inplace) f_unlink(path_out);
if ((fvx_qwrite(path_out, keydb, 0, bt, &btw) != FR_OK) || (bt != btw))
return 1;
return 0;
}
u32 AddKeyToDb(AesKeyInfo* key_info, AesKeyInfo* key_entry) {
AesKeyInfo* key = key_info;
if (key_entry) { // key entry provided
for (; key->slot < 0x40; key++) {
if ((u8*) key - (u8*) key_info >= MAX_KEYDB_SIZE) return 1;
if ((key_entry->slot == key->slot) && (key_entry->type == key->type) &&
(strncasecmp(key_entry->id, key->id, 10) == 0) &&
((bool) key_entry->isDevkitKey == (bool) key->isDevkitKey))
return 0; // key already in db
}
memcpy(key++, key_entry, sizeof(AesKeyInfo));
}
if ((u8*) key - (u8*) key_info >= MAX_KEYDB_SIZE) return 1;
memset(key, 0xFF, sizeof(AesKeyInfo)); // this used to signal keydb end
return 0;
}
u32 BuildKeyDb(const char* path, bool dump) {
AesKeyInfo* key_info = (AesKeyInfo*) MAIN_BUFFER;
const char* path_out = OUTPUT_PATH "/" KEYDB_NAME;
const char* path_in = path;
UINT br;
// write permissions
if (!CheckWritePermissions(path_out))
return 1;
if (!path_in && !dump) { // no input path given - initialize
AddKeyToDb(key_info, NULL);
if ((fvx_stat(path_out, NULL) == FR_OK) &&
(ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out)))
path_in = path_out;
else return 0;
}
u32 filetype = path_in ? IdentifyFileType(path_in) : 0;
if (filetype & BIN_KEYDB) { // AES key database
AesKeyInfo* key_info_merge = (AesKeyInfo*) TEMP_BUFFER;
if ((fvx_qread(path_in, key_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) ||
(br % sizeof(AesKeyInfo)) || (br >= MAIN_BUFFER_SIZE)) return 1;
u32 n_keys = br / sizeof(AesKeyInfo);
for (u32 i = 0; i < n_keys; i++) {
if (key_info_merge[i].isEncrypted) // build an unencrypted db
CryptAesKeyInfo(&(key_info_merge[i]));
if (AddKeyToDb(key_info, key_info_merge + i) != 0) return 1;
}
} else if (filetype & BIN_LEGKEY) { // legacy key file
AesKeyInfo key;
unsigned int keyslot = 0xFF;
char typestr[16] = { 0 };
char* name_in = strrchr(path_in, '/');
memset(&key, 0, sizeof(AesKeyInfo));
key.type = 'N';
if (!name_in || (strnlen(++name_in, 32) > 24)) return 1; // safety
if ((sscanf(name_in, "slot0x%02XKey%s", &keyslot, typestr) != 2) &&
(sscanf(name_in, "slot0x%02Xkey%s", &keyslot, typestr) != 2)) return 1;
char* dot = strrchr(typestr, '.');
if (!dot) return 1;
*dot = '\0';
if ((typestr[1] == '\0') && ((*typestr == 'X') || (*typestr == 'Y'))) key.type = *typestr;
else strncpy(key.id, typestr, 10);
key.slot = keyslot;
key.isDevkitKey = (IS_DEVKIT) ? 1 : 0; // just assume it's a devkit key on devkit
if ((fvx_qread(path_in, key.key, 0, 16, &br) != FR_OK) || (br != 16)) return 1;
if (AddKeyToDb(key_info, &key) != 0) return 1;
}
if (dump) {
u32 dump_size = 0;
for (AesKeyInfo* key = key_info; key->slot <= 0x40; key++) {
dump_size += sizeof(AesKeyInfo);
if (dump_size >= MAX_KEYDB_SIZE) return 1;
}
f_unlink(path_out);
if (!dump_size || (fvx_qwrite(path_out, key_info, 0, dump_size, &br) != FR_OK) || (br != dump_size))
return 1;
}
return 0;
}

8
source/game/keydbutil.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "common.h"
#include "keydb.h"
u32 CryptAesKeyDb(const char* path, bool inplace, bool encrypt);
u32 AddKeyToDb(AesKeyInfo* key_info, AesKeyInfo* key_entry);
u32 BuildKeyDb(const char* path, bool dump);

View File

@ -6,6 +6,7 @@
#include "fsutil.h" #include "fsutil.h"
#include "fsperm.h" #include "fsperm.h"
#include "gameutil.h" #include "gameutil.h"
#include "keydbutil.h"
#include "nandutil.h" #include "nandutil.h"
#include "filetype.h" #include "filetype.h"
#include "unittype.h" #include "unittype.h"
@ -655,6 +656,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool cia_buildable = (FTYPE_CIABUILD(filetype)); bool cia_buildable = (FTYPE_CIABUILD(filetype));
bool cia_buildable_legit = (FTYPE_CIABUILD_L(filetype)); bool cia_buildable_legit = (FTYPE_CIABUILD_L(filetype));
bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path; bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path;
bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path;
bool titleinfo = (FTYPE_TITLEINFO(filetype)); bool titleinfo = (FTYPE_TITLEINFO(filetype));
bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT)); bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT));
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype)); bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
@ -663,7 +665,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool xorpadable = (FTYPE_XORPAD(filetype)); bool xorpadable = (FTYPE_XORPAD(filetype));
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT)); bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit ||
tik_buildable || titleinfo || hsinjectable || restorable || xorpadable || launchable || ebackupable; tik_buildable || key_buildable || titleinfo || hsinjectable || restorable || xorpadable || launchable || ebackupable;
char pathstr[32+1]; char pathstr[32+1];
TruncateString(pathstr, curr_entry->path, 32, 8); TruncateString(pathstr, curr_entry->path, 32, 8);
@ -704,6 +706,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
(filetype & SYS_FIRM ) ? "FIRM image options..." : (filetype & SYS_FIRM ) ? "FIRM image options..." :
(filetype & SYS_TICKDB) ? (tik_buildable) ? "Ticket.db options..." : "Mount as ticket.db" : (filetype & SYS_TICKDB) ? (tik_buildable) ? "Ticket.db options..." : "Mount as ticket.db" :
(filetype & BIN_TIKDB) ? "Titlekey options..." : (filetype & BIN_TIKDB) ? "Titlekey options..." :
(filetype & BIN_KEYDB) ? "AESkeydb options..." :
(filetype & BIN_LEGKEY) ? "Build " KEYDB_NAME :
(filetype & BIN_NCCHNFO)? "NCCHinfo options..." : (filetype & BIN_NCCHNFO)? "NCCHinfo options..." :
(filetype & BIN_LAUNCH) ? "Launch as arm9 payload" : "???"; (filetype & BIN_LAUNCH) ? "Launch as arm9 payload" : "???";
optionstr[hexviewer-1] = "Show in Hexeditor"; optionstr[hexviewer-1] = "Show in Hexeditor";
@ -803,6 +807,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
int cia_build_legit = (cia_buildable_legit) ? ++n_opt : -1; int cia_build_legit = (cia_buildable_legit) ? ++n_opt : -1;
int tik_build_enc = (tik_buildable) ? ++n_opt : -1; int tik_build_enc = (tik_buildable) ? ++n_opt : -1;
int tik_build_dec = (tik_buildable) ? ++n_opt : -1; int tik_build_dec = (tik_buildable) ? ++n_opt : -1;
int key_build = (key_buildable) ? ++n_opt : -1;
int verify = (verificable) ? ++n_opt : -1; int verify = (verificable) ? ++n_opt : -1;
int ctrtransfer = (transferable) ? ++n_opt : -1; int ctrtransfer = (transferable) ? ++n_opt : -1;
int hsinject = (hsinjectable) ? ++n_opt : -1; int hsinject = (hsinjectable) ? ++n_opt : -1;
@ -819,6 +824,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
if (cia_build_legit > 0) optionstr[cia_build_legit-1] = "Build CIA (legit)"; if (cia_build_legit > 0) optionstr[cia_build_legit-1] = "Build CIA (legit)";
if (tik_build_enc > 0) optionstr[tik_build_enc-1] = "Build " TIKDB_NAME_ENC; if (tik_build_enc > 0) optionstr[tik_build_enc-1] = "Build " TIKDB_NAME_ENC;
if (tik_build_dec > 0) optionstr[tik_build_dec-1] = "Build " TIKDB_NAME_DEC; if (tik_build_dec > 0) optionstr[tik_build_dec-1] = "Build " TIKDB_NAME_DEC;
if (key_build > 0) optionstr[key_build-1] = "Build " KEYDB_NAME;
if (verify > 0) optionstr[verify-1] = "Verify file"; if (verify > 0) optionstr[verify-1] = "Verify file";
if (ctrtransfer > 0) optionstr[ctrtransfer-1] = "Transfer image to CTRNAND"; if (ctrtransfer > 0) optionstr[ctrtransfer-1] = "Transfer image to CTRNAND";
if (hsinject > 0) optionstr[hsinject-1] = "Inject to H&S"; if (hsinject > 0) optionstr[hsinject-1] = "Inject to H&S";
@ -873,12 +879,13 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
n_other++; n_other++;
continue; continue;
} }
if (CheckEncryptedGameFile(path) != 0) { if (!(filetype & BIN_KEYDB) && (CheckEncryptedGameFile(path) != 0)) {
n_unencrypted++; n_unencrypted++;
continue; continue;
} }
current_dir->entry[i].marked = false; current_dir->entry[i].marked = false;
if (CryptGameFile(path, inplace, false) == 0) n_success++; if (!(filetype & BIN_KEYDB) && (CryptGameFile(path, inplace, false) == 0)) n_success++;
else if ((filetype & BIN_KEYDB) && (CryptAesKeyDb(path, inplace, false) == 0)) n_success++;
else { // on failure: set cursor on failed title, break; else { // on failure: set cursor on failed title, break;
*cursor = i; *cursor = i;
break; break;
@ -890,10 +897,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
} else ShowPrompt(false, "%lu/%lu files decrypted ok", n_success, n_marked); } else ShowPrompt(false, "%lu/%lu files decrypted ok", n_success, n_marked);
if (!inplace && n_success) ShowPrompt(false, "%lu files written to %s", n_success, OUTPUT_PATH); if (!inplace && n_success) ShowPrompt(false, "%lu files written to %s", n_success, OUTPUT_PATH);
} else { } else {
if (CheckEncryptedGameFile(curr_entry->path) != 0) { if (!(filetype & BIN_KEYDB) && (CheckEncryptedGameFile(curr_entry->path) != 0)) {
ShowPrompt(false, "%s\nFile is not encrypted", pathstr); ShowPrompt(false, "%s\nFile is not encrypted", pathstr);
} else { } else {
u32 ret = CryptGameFile(curr_entry->path, inplace, false); u32 ret = (filetype & BIN_KEYDB) ? CryptAesKeyDb(curr_entry->path, inplace, false) :
CryptGameFile(curr_entry->path, inplace, false);
if (inplace || (ret != 0)) ShowPrompt(false, "%s\nDecryption %s", pathstr, (ret == 0) ? "success" : "failed"); if (inplace || (ret != 0)) ShowPrompt(false, "%s\nDecryption %s", pathstr, (ret == 0) ? "success" : "failed");
else ShowPrompt(false, "%s\nDecrypted to %s", pathstr, OUTPUT_PATH); else ShowPrompt(false, "%s\nDecrypted to %s", pathstr, OUTPUT_PATH);
} }
@ -921,7 +929,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
continue; continue;
} }
current_dir->entry[i].marked = false; current_dir->entry[i].marked = false;
if (CryptGameFile(path, inplace, true) == 0) n_success++; if (!(filetype & BIN_KEYDB) && (CryptGameFile(path, inplace, true) == 0)) n_success++;
else if ((filetype & BIN_KEYDB) && (CryptAesKeyDb(path, inplace, true) == 0)) n_success++;
else { // on failure: set cursor on failed title, break; else { // on failure: set cursor on failed title, break;
*cursor = i; *cursor = i;
break; break;
@ -933,7 +942,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
} else ShowPrompt(false, "%lu/%lu files encrypted ok", n_success, n_marked); } else ShowPrompt(false, "%lu/%lu files encrypted ok", n_success, n_marked);
if (!inplace && n_success) ShowPrompt(false, "%lu files written to %s", n_success, OUTPUT_PATH); if (!inplace && n_success) ShowPrompt(false, "%lu files written to %s", n_success, OUTPUT_PATH);
} else { } else {
u32 ret = CryptGameFile(curr_entry->path, inplace, true); u32 ret = (filetype & BIN_KEYDB) ? CryptAesKeyDb(curr_entry->path, inplace, true) :
CryptGameFile(curr_entry->path, inplace, true);
if (inplace || (ret != 0)) ShowPrompt(false, "%s\nEncryption %s", pathstr, (ret == 0) ? "success" : "failed"); if (inplace || (ret != 0)) ShowPrompt(false, "%s\nEncryption %s", pathstr, (ret == 0) ? "success" : "failed");
else ShowPrompt(false, "%s\nEncrypted to %s", pathstr, OUTPUT_PATH); else ShowPrompt(false, "%s\nEncrypted to %s", pathstr, OUTPUT_PATH);
} }
@ -1029,10 +1039,36 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored", if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
path_out, n_success, n_marked, n_other, n_marked); path_out, n_success, n_marked, n_other, n_marked);
else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked); else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked);
} else ShowPrompt(false, "%s\nBuild database failed."); } else ShowPrompt(false, "%s\nBuild database failed.", path_out);
} else ShowPrompt(false, "%s\nBuild database %s.", path_out, } else ShowPrompt(false, "%s\nBuild database %s.", path_out,
(BuildTitleKeyInfo(curr_entry->path, dec, true) == 0) ? "success" : "failed"); (BuildTitleKeyInfo(curr_entry->path, dec, true) == 0) ? "success" : "failed");
return 0; return 0;
} else if (user_select == key_build) { // -> (Re)Build AES key database
const char* path_out = OUTPUT_PATH "/" KEYDB_NAME;
if (BuildKeyDb(NULL, false) != 0) return 1; // init database
ShowString("Building %s...", KEYDB_NAME);
if (n_marked > 1) {
u32 n_success = 0;
u32 n_other = 0;
for (u32 i = 0; i < current_dir->n_entries; i++) {
const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked)
continue;
if (!FTYPE_KEYBUILD(IdentifyFileType(path))) {
n_other++;
continue;
}
current_dir->entry[i].marked = false;
if (BuildKeyDb(path, false) == 0) n_success++; // ignore failures for now
}
if (BuildKeyDb(NULL, true) == 0) {
if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
path_out, n_success, n_marked, n_other, n_marked);
else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked);
} else ShowPrompt(false, "%s\nBuild database failed.", path_out);
} else ShowPrompt(false, "%s\nBuild database %s.", path_out,
(BuildKeyDb(curr_entry->path, true) == 0) ? "success" : "failed");
return 0;
} else if (user_select == show_info) { // -> Show title info } else if (user_select == show_info) { // -> Show title info
if (ShowGameFileTitleInfo(curr_entry->path) != 0) if (ShowGameFileTitleInfo(curr_entry->path) != 0)
ShowPrompt(false, "Title info: not found"); ShowPrompt(false, "Title info: not found");