Added NCCH/NCSD/CIA/BOSS encrypt options

This commit is contained in:
d0k3 2017-01-31 16:16:26 +01:00
parent 1a9cad856d
commit d2e16c9de5
9 changed files with 103 additions and 37 deletions

View File

@ -38,7 +38,7 @@
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
// GodMode9 version
#define VERSION "0.9.6"
#define VERSION "0.9.7"
// input / output paths
#define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9"

View File

@ -59,7 +59,7 @@ u32 IdentifyFileType(const char* path) {
fname && (strncasecmp(fname, NCCHINFO_NAME, 32) == 0)) {
return BIN_NCCHNFO; // ncchinfo.bin file
#if PAYLOAD_MAX_SIZE <= TEMP_BUFFER_SIZE
} else if ((fsize <= PAYLOAD_MAX_SIZE) && (strncasecmp(ext, "bin", 4) == 0)) {
} else if ((fsize <= PAYLOAD_MAX_SIZE) && ext && (strncasecmp(ext, "bin", 4) == 0)) {
return BIN_LAUNCH; // assume it's an ARM9 payload
#endif
}

View File

@ -14,15 +14,17 @@
#define SYS_FIRM (1<<9)
#define BIN_NCCHNFO (1<<10)
#define BIN_LAUNCH (1<<11)
#define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types
#define FLAG_CXI (1<<31)
#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|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|SYS_FIRM))
#define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS))
#define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD)))
#define FTYPE_HSINJECTABLE(tp) ((tp&(GAME_NCCH|FLAG_CXI)) == (GAME_NCCH|FLAG_CXI))
#define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))
#define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND))
#define FTYPE_XORPAD(tp) (tp&(BIN_NCCHNFO))
#define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH))

View File

@ -8,6 +8,10 @@
#include "sha.h"
#include "vff.h"
// use NCCH crypto defines for everything
#define CRYPTO_DECRYPT NCCH_NOCRYPTO
#define CRYPTO_ENCRYPT NCCH_STDCRYPTO
u32 GetOutputPath(char* dest, const char* path, const char* ext) {
// special handling for input from title directories (somewhat hacky)
if ((strspn(path, "AB147") > 0) && (strncmp(path + 1, ":/title/", 8) == 0)) {
@ -670,7 +674,7 @@ u32 CheckEncryptedGameFile(const char* path) {
else return 1;
}
u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 crypto, // crypto only relevant for NCCH
u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 crypto,
u32 offset, u32 size, TmdContentChunk* chunk, const u8* titlekey) { // this line only for CIA contents
// this will do a simple copy for unencrypted files
bool inplace = (strncmp(orig, dest, 256) == 0);
@ -680,6 +684,14 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16
FIL* dfp = (inplace) ? &ofile : &dfile;
FSIZE_t fsize;
// FIRM encryption is not possible (yet)
if ((mode & SYS_FIRM) && (crypto != CRYPTO_DECRYPT))
return 1;
// check for BOSS crypto
bool crypt_boss = ((mode & GAME_BOSS) && (CheckEncryptedBossFile(orig) == 0));
crypt_boss = ((mode & GAME_BOSS) && (crypt_boss == (crypto == CRYPTO_DECRYPT)));
// open file(s)
if (inplace) {
if (fvx_open(ofp, orig, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK)
@ -701,14 +713,14 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16
u32 ret = 0;
if (!ShowProgress(offset, fsize, dest)) ret = 1;
if (mode & (GAME_NCCH|GAME_NCSD|GAME_BOSS|SYS_FIRM)) { // for NCCH / NCSD / FIRM files
if (mode & (GAME_NCCH|GAME_NCSD|GAME_BOSS|SYS_FIRM)) { // for NCCH / NCSD / BOSS / FIRM files
for (u32 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) {
u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i));
UINT bytes_read, bytes_written;
if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1;
if (((mode & GAME_NCCH) && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) ||
((mode & GAME_NCSD) && (DecryptNcsdSequential(MAIN_BUFFER, i, read_bytes) != 0)) ||
((mode & GAME_BOSS) && (CryptBossSequential(MAIN_BUFFER, i, read_bytes) != 0)) ||
((mode & GAME_NCSD) && (CryptNcsdSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) ||
((mode & GAME_BOSS) && crypt_boss && (CryptBossSequential(MAIN_BUFFER, i, read_bytes) != 0)) ||
((mode & SYS_FIRM) && (DecryptFirmSequential(MAIN_BUFFER, i, read_bytes) != 0)))
ret = 1;
if (inplace) fvx_lseek(ofp, fvx_tell(ofp) - read_bytes);
@ -726,8 +738,8 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16
GetTmdCtr(ctr, chunk); // NCCH crypto?
if (fvx_read(ofp, MAIN_BUFFER, sizeof(NcchHeader), &bytes_read) != FR_OK) ret = 1;
if (cia_crypto) DecryptCiaContentSequential(MAIN_BUFFER, sizeof(NcchHeader), ctr, titlekey);
ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && NCCH_ENCRYPTED(ncch));
if (ncch_crypto && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) != 0))
ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && (NCCH_ENCRYPTED(ncch) || !(crypto & NCCH_NOCRYPTO)));
if (ncch_crypto && (SetupNcchCrypto(ncch, crypto) != 0))
ret = 1;
GetTmdCtr(ctr, chunk);
@ -737,7 +749,7 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16
u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i));
if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1;
if (cia_crypto && (DecryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey) != 0)) ret = 1;
if (ncch_crypto && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1;
if (ncch_crypto && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) ret = 1;
if (inplace) fvx_lseek(ofp, fvx_tell(ofp) - read_bytes);
if (fvx_write(dfp, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1;
sha_update(MAIN_BUFFER, read_bytes);
@ -754,7 +766,7 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16
return ret;
}
u32 DecryptCiaFile(const char* orig, const char* dest) {
u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) {
bool inplace = (strncmp(orig, dest, 256) == 0);
CiaStub* cia = (CiaStub*) TEMP_BUFFER;
CiaInfo info;
@ -779,7 +791,7 @@ u32 DecryptCiaFile(const char* orig, const char* dest) {
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) {
TmdContentChunk* chunk = &(cia->content_list[i]);
u64 size = getbe64(chunk->size);
if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_CIA, NCCH_NOCRYPTO, next_offset, size, chunk, titlekey) != 0)
if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_CIA, crypto, next_offset, size, chunk, titlekey) != 0)
return 1;
next_offset += size;
}
@ -798,7 +810,7 @@ u32 DecryptFirmFile(const char* orig, const char* dest) {
UINT btr;
// actual decryption
if (CryptNcchNcsdBossFirmFile(orig, dest, SYS_FIRM, 0, 0, 0, NULL, NULL) != 0)
if (CryptNcchNcsdBossFirmFile(orig, dest, SYS_FIRM, CRYPTO_DECRYPT, 0, 0, NULL, NULL) != 0)
return 1;
// open destination file, get FIRM header
@ -850,8 +862,9 @@ u32 DecryptFirmFile(const char* orig, const char* dest) {
return 0;
}
u32 DecryptGameFile(const char* path, bool inplace) {
u32 CryptGameFile(const char* path, bool inplace, bool encrypt) {
u32 filetype = IdentifyFileType(path);
u16 crypto = encrypt ? CRYPTO_ENCRYPT : CRYPTO_DECRYPT;
char dest[256];
char* destptr = (char*) path;
u32 ret = 0;
@ -871,11 +884,11 @@ u32 DecryptGameFile(const char* path, bool inplace) {
}
if (filetype & GAME_CIA)
ret = DecryptCiaFile(path, destptr);
ret = CryptCiaFile(path, destptr, crypto);
else if (filetype & SYS_FIRM)
ret = DecryptFirmFile(path, destptr);
else if (filetype & (GAME_NCCH|GAME_NCSD|GAME_BOSS))
ret = CryptNcchNcsdBossFirmFile(path, destptr, filetype, NCCH_NOCRYPTO, 0, 0, NULL, NULL);
ret = CryptNcchNcsdBossFirmFile(path, destptr, filetype, crypto, 0, 0, NULL, NULL);
else ret = 1;
if (!inplace && (ret != 0))
@ -1302,7 +1315,7 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) {
// copy / decrypt the source CXI
u32 ret = 0;
if (CryptNcchNcsdBossFirmFile(path, path_cxi, GAME_NCCH, NCCH_NOCRYPTO, 0, 0, NULL, NULL) != 0)
if (CryptNcchNcsdBossFirmFile(path, path_cxi, GAME_NCCH, CRYPTO_DECRYPT, 0, 0, NULL, NULL) != 0)
ret = 1;
// fix up the injected H&S NCCH header (copy H&S signature, title ID)

View File

@ -4,7 +4,7 @@
u32 VerifyGameFile(const char* path);
u32 CheckEncryptedGameFile(const char* path);
u32 DecryptGameFile(const char* path, bool inplace);
u32 CryptGameFile(const char* path, bool inplace, bool encrypt);
u32 BuildCiaFromGameFile(const char* path, bool force_legit);
u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
u32 InjectHealthAndSafety(const char* path, const char* destdrv);

View File

@ -11,7 +11,8 @@
#define NCCH_ENCRYPTED(ncch) (!((ncch)->flags[7] & 0x04))
#define NCCH_IS_CXI(ncch) ((ncch)->flags[5] & 0x02)
#define NCCH_NOCRYPTO 0x0004
#define NCCH_NOCRYPTO 0x0004
#define NCCH_STDCRYPTO 0x0000
#define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20))))
// wrapper defines

View File

@ -35,7 +35,7 @@ u64 GetNcsdTrimmedSize(NcsdHeader* header) {
}
// on the fly decryptor for NCSD
u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data) {
u32 CryptNcsdSequential(u8* data, u32 offset_data, u32 size_data, u16 crypto) {
// warning: this will only work for sequential processing
static NcsdHeader ncsd;
@ -64,7 +64,7 @@ u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data) {
if (size_i > size_data - (data_i - data))
size_i = size_data - (data_i - data);
// decrypt ncch segment
if (DecryptNcchSequential(data_i, offset_i, size_i) != 0)
if (CryptNcchSequential(data_i, offset_i, size_i, crypto) != 0)
return 1;
}

View File

@ -10,6 +10,10 @@
#define NCSD_DINFO_SIZE 0x300
#define NCSD_CNT0_OFFSET 0x4000
// wrapper defines
#define DecryptNcsdSequential(data, offset, size) CryptNcsdSequential(data, offset, size, NCCH_NOCRYPTO)
#define EncryptNcsdSequential(data, offset, size, crypto) CryptNcsdSequential(data, offset, size, crypto)
typedef struct {
u32 offset;
u32 size;
@ -34,4 +38,4 @@ typedef struct {
u32 ValidateNcsdHeader(NcsdHeader* header);
u64 GetNcsdTrimmedSize(NcsdHeader* header);
u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data);
u32 CryptNcsdSequential(u8* data, u32 offset_data, u32 size_data, u16 crypto);

View File

@ -571,7 +571,7 @@ u32 Sha256Calculator(const char* path) {
u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* current_dir, DirStruct* clipboard) {
DirEntry* curr_entry = &(current_dir->entry[*cursor]);
const char* optionstr[8];
const char* optionstr[16];
// check for file lock
if (!FileUnlock(curr_entry->path)) return 1;
@ -585,14 +585,15 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & DRV_IMAGE));
bool verificable = (FYTPE_VERIFICABLE(filetype));
bool decryptable = (FYTPE_DECRYPTABLE(filetype));
bool decryptable_inplace = (decryptable && (drvtype & (DRV_SDCARD|DRV_RAMDRIVE)));
bool encryptable = (FYTPE_ENCRYPTABLE(filetype));
bool cryptable_inplace = ((encryptable||decryptable) && (drvtype & DRV_FAT));
bool buildable = (FTYPE_BUILDABLE(filetype));
bool buildable_legit = (FTYPE_BUILDABLE_L(filetype));
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND));
bool xorpadable = (FTYPE_XORPAD(filetype));
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
bool special_opt = mountable || verificable || decryptable || decryptable_inplace ||
bool special_opt = mountable || verificable || decryptable || encryptable ||
buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable;
char pathstr[32 + 1];
@ -697,7 +698,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
int mount = (mountable) ? ++n_opt : -1;
int restore = (restorable) ? ++n_opt : -1;
int decrypt = (decryptable) ? ++n_opt : -1;
int decrypt_inplace = (decryptable_inplace) ? ++n_opt : -1;
int encrypt = (encryptable) ? ++n_opt : -1;
int build = (buildable) ? ++n_opt : -1;
int build_legit = (buildable_legit) ? ++n_opt : -1;
int verify = (verificable) ? ++n_opt : -1;
@ -707,8 +708,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
int launch = (launchable) ? ++n_opt : -1;
if (mount > 0) optionstr[mount-1] = "Mount image to drive";
if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)";
if (decrypt > 0) optionstr[decrypt-1] = "Decrypt file (SD output)";
if (decrypt_inplace > 0) optionstr[decrypt_inplace-1] = "Decrypt file (inplace)";
if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")";
if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")";
if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
if (build_legit > 0) optionstr[build_legit-1] = "Build CIA (legit)";
if (verify > 0) optionstr[verify-1] = "Verify file";
@ -741,17 +742,24 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
}
}
return 0;
} else if ((user_select == decrypt) || (user_select == decrypt_inplace)) { // -> decrypt game file
bool inplace = (user_select == decrypt_inplace);
if ((n_marked > 1) && ShowPrompt(true, "Try to decrypt all %lu selected files?", n_marked)) {
} else if (user_select == decrypt) { // -> decrypt game file
if (cryptable_inplace) {
optionstr[0] = "Decrypt to " OUTPUT_PATH;
optionstr[1] = "Decrypt inplace";
user_select = (int) ShowSelectPrompt(2, optionstr, pathstr);
} else user_select = 1;
bool inplace = (user_select == 2);
if (!user_select) { // do nothing when no choice is made
} else if ((n_marked > 1) && ShowPrompt(true, "Try to decrypt all %lu selected files?", n_marked)) {
u32 n_success = 0;
u32 n_unencrypted = 0;
u32 n_other = 0;
ShowString("Trying to decrypt %lu files...", n_marked);
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 (IdentifyFileType(path) != filetype) {
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++;
continue;
}
@ -760,7 +768,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
continue;
}
current_dir->entry[i].marked = false;
if (DecryptGameFile(path, inplace) == 0) n_success++;
if (CryptGameFile(path, inplace, false) == 0) n_success++;
else { // on failure: set cursor on failed title, break;
*cursor = i;
break;
@ -768,19 +776,57 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
}
if (n_other || n_unencrypted) {
ShowPrompt(false, "%lu/%lu files decrypted ok\n%lu/%lu not encrypted\n%lu/%lu not of same type",
n_success, n_marked, n_unencrypted, n_marked, n_other, n_marked);
n_success, n_marked, n_unencrypted, n_marked, n_other, 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);
} else {
if (CheckEncryptedGameFile(curr_entry->path) != 0) {
ShowPrompt(false, "%s\nFile is not encrypted", pathstr);
} else {
u32 ret = DecryptGameFile(curr_entry->path, inplace);
u32 ret = CryptGameFile(curr_entry->path, inplace, false);
if (inplace || (ret != 0)) ShowPrompt(false, "%s\nDecryption %s", pathstr, (ret == 0) ? "success" : "failed");
else ShowPrompt(false, "%s\nDecrypted to %s", pathstr, OUTPUT_PATH);
}
}
return 0;
} else if (user_select == encrypt) { // -> encrypt game file
if (cryptable_inplace) {
optionstr[0] = "Encrypt to " OUTPUT_PATH;
optionstr[1] = "Encrypt inplace";
user_select = (int) ShowSelectPrompt(2, optionstr, pathstr);
} else user_select = 1;
bool inplace = (user_select == 2);
if (!user_select) { // do nothing when no choice is made
} else if ((n_marked > 1) && ShowPrompt(true, "Try to encrypt all %lu selected files?", n_marked)) {
u32 n_success = 0;
u32 n_other = 0;
ShowString("Trying to encrypt %lu files...", n_marked);
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 (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++;
continue;
}
current_dir->entry[i].marked = false;
if (CryptGameFile(path, inplace, true) == 0) n_success++;
else { // on failure: set cursor on failed title, break;
*cursor = i;
break;
}
}
if (n_other) {
ShowPrompt(false, "%lu/%lu files encrypted ok\n%lu/%lu not of same type",
n_success, n_marked, n_other, 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);
} else {
u32 ret = CryptGameFile(curr_entry->path, inplace, true);
if (inplace || (ret != 0)) ShowPrompt(false, "%s\nEncryption %s", pathstr, (ret == 0) ? "success" : "failed");
else ShowPrompt(false, "%s\nEncrypted to %s", pathstr, OUTPUT_PATH);
}
return 0;
} else if ((user_select == build) || (user_select == build_legit)) { // -> build CIA
bool force_legit = (user_select == build_legit);
if ((n_marked > 1) && ShowPrompt(true, "Try to process all %lu selected files?", n_marked)) {
@ -790,7 +836,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked)
continue;
if (IdentifyFileType(path) != filetype) {
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++;
continue;
}
@ -822,7 +868,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
const char* path = current_dir->entry[i].path;
if (!current_dir->entry[i].marked)
continue;
if (IdentifyFileType(path) != filetype) {
if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) {
n_other++;
continue;
}