forked from Mirror/GodMode9
Improved NCCH crypto routines
... now allow on-the-fly reencryption. H&S inject is now also possible for encrypted CXIs
This commit is contained in:
parent
3f31807c75
commit
1a9cad856d
@ -37,7 +37,7 @@ u32 IdentifyFileType(const char* path) {
|
||||
return GAME_NCSD; // NCSD (".3DS") file
|
||||
} else if (ValidateNcchHeader((NcchHeader*) (void*) header) == 0) {
|
||||
NcchHeader* ncch = (NcchHeader*) (void*) header;
|
||||
u32 type = GAME_NCCH | (NCCH_IS_CXI(ncch) ? FLAG_CXI : 0) | (NCCH_ENCRYPTED(ncch) ? FLAG_ENCRYPTED : 0);
|
||||
u32 type = GAME_NCCH | (NCCH_IS_CXI(ncch) ? FLAG_CXI : 0);
|
||||
if (fsize >= (ncch->size * NCCH_MEDIA_UNIT))
|
||||
return type; // NCCH (".APP") file
|
||||
} else if (ValidateExeFsHeader((ExeFsHeader*) (void*) header, fsize) == 0) {
|
||||
|
@ -15,15 +15,14 @@
|
||||
#define BIN_NCCHNFO (1<<10)
|
||||
#define BIN_LAUNCH (1<<11)
|
||||
|
||||
#define FLAG_CXI (1<<30)
|
||||
#define FLAG_ENCRYPTED (1<<31)
|
||||
#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 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|FLAG_ENCRYPTED)) == (GAME_NCCH|FLAG_CXI))
|
||||
#define FTYPE_HSINJECTABLE(tp) ((tp&(GAME_NCCH|FLAG_CXI)) == (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))
|
||||
|
@ -309,7 +309,7 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
|
||||
}
|
||||
|
||||
// check / setup crypto
|
||||
if (SetupNcchCrypto(&ncch) != 0) {
|
||||
if (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0) {
|
||||
if (!offset) ShowPrompt(false, "%s\nError: Crypto not set up", pathstr);
|
||||
fvx_close(&file);
|
||||
return 1;
|
||||
@ -722,12 +722,12 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16
|
||||
UINT bytes_read, bytes_written;
|
||||
u8 ctr[16];
|
||||
|
||||
NcchHeader* ncch = (NcchHeader*) (void*) MAIN_BUFFER;
|
||||
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((NcchHeader*) (void*) MAIN_BUFFER) == 0) &&
|
||||
NCCH_ENCRYPTED((NcchHeader*) (void*) MAIN_BUFFER));
|
||||
if (ncch_crypto && (SetupNcchCrypto((NcchHeader*) (void*) MAIN_BUFFER) != 0))
|
||||
ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && NCCH_ENCRYPTED(ncch));
|
||||
if (ncch_crypto && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) != 0))
|
||||
ret = 1;
|
||||
|
||||
GetTmdCtr(ctr, chunk);
|
||||
@ -912,7 +912,7 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset,
|
||||
NcchHeader ncch;
|
||||
if ((fvx_read(&ofile, &ncch, sizeof(NcchHeader), &bytes_read) != FR_OK) ||
|
||||
(ValidateNcchHeader(&ncch) != 0) ||
|
||||
(SetupNcchCrypto(&ncch) != 0))
|
||||
(SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0))
|
||||
ncch_crypto = false;
|
||||
fvx_lseek(&ofile, offset);
|
||||
}
|
||||
@ -1243,9 +1243,9 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) {
|
||||
const u32 tidlow_hs_n3ds[] = { 0x20020300, 0x20021300, 0x20022300, 0, 0, 0x00027300, 0 };
|
||||
NcchHeader ncch;
|
||||
|
||||
// check input file
|
||||
// check input file / crypto
|
||||
if ((LoadNcchHeaders(&ncch, NULL, NULL, path, 0) != 0) ||
|
||||
(NCCH_ENCRYPTED(&ncch)) || !(NCCH_IS_CXI(&ncch)))
|
||||
!(NCCH_IS_CXI(&ncch)) || (SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0))
|
||||
return 1;
|
||||
|
||||
// write permissions
|
||||
@ -1288,8 +1288,9 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) {
|
||||
u64 tid_hs = ((u64) 0x00040010 << 32) | tidlow_hs;
|
||||
u16 crypto = NCCH_NOCRYPTO;
|
||||
u8 sig[0x100];
|
||||
if ((LoadNcchHeaders(&ncch, NULL, NULL, path_cxi, 0) != 0) || (SetupNcchCrypto(&ncch) != 0) ||
|
||||
!(NCCH_IS_CXI(&ncch)) || (ncch.programId != tid_hs) || (ncch.partitionId != tid_hs))
|
||||
if ((LoadNcchHeaders(&ncch, NULL, NULL, path_cxi, 0) != 0) ||
|
||||
(SetupNcchCrypto(&ncch, NCCH_NOCRYPTO) != 0) || !(NCCH_IS_CXI(&ncch)) ||
|
||||
(ncch.programId != tid_hs) || (ncch.partitionId != tid_hs))
|
||||
return 1;
|
||||
crypto = NCCH_GET_CRYPTO(&ncch);
|
||||
memcpy(sig, ncch.signature, 0x100);
|
||||
|
@ -148,9 +148,9 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid) {
|
||||
u8 flags3 = (crypt_to & 0x04) ? ncch->flags[3] : (crypt_to >> 8) & 0xFF;
|
||||
u8 flags7 = (crypt_to & 0x04) ? ncch->flags[7] : crypt_to & 0xFF;
|
||||
u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid) {
|
||||
u8 flags3 = (crypto >> 8) & 0xFF;
|
||||
u8 flags7 = crypto & 0xFF;
|
||||
u32 keyslot = (!keyid || !flags3) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto
|
||||
(flags3 == 0x0A) ? 0x18 : (flags3 == 0x0B) ? 0x1B : 0x25;
|
||||
|
||||
@ -195,13 +195,20 @@ u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 SetupNcchCrypto(NcchHeader* ncch) {
|
||||
return (!NCCH_ENCRYPTED(ncch) ||
|
||||
((SetNcchKey(ncch, NCCH_NOCRYPTO, 0) == 0) && (SetNcchKey(ncch, NCCH_NOCRYPTO, 1) == 0))) ? 0 : 1;
|
||||
// this is used to force and check crypto setup
|
||||
// (also prevents SHA register usage later on)
|
||||
u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to) {
|
||||
u16 crypt_from = NCCH_GET_CRYPTO(ncch);
|
||||
u32 res_from = ((crypt_from & NCCH_NOCRYPTO) ||
|
||||
((SetNcchKey(ncch, crypt_from, 0) == 0) && (SetNcchKey(ncch, crypt_from, 1) == 0))) ? 0 : 1;
|
||||
u32 res_to = ((crypt_to & NCCH_NOCRYPTO) ||
|
||||
((SetNcchKey(ncch, crypt_to, 0) == 0) && (SetNcchKey(ncch, crypt_to, 1) == 0))) ? 0 : 1;
|
||||
return res_from | res_to;
|
||||
}
|
||||
|
||||
u32 CryptNcchSection(u8* data, u32 offset_data, u32 size_data, u32 offset_section, u32 size_section,
|
||||
u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypto, u32 keyid) {
|
||||
u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypt_to, u32 keyid) {
|
||||
u16 crypt_from = NCCH_GET_CRYPTO(ncch);
|
||||
const u32 mode = AES_CNT_CTRNAND_MODE;
|
||||
|
||||
// check if section in data
|
||||
@ -225,30 +232,34 @@ u32 CryptNcchSection(u8* data, u32 offset_data, u32 size_data, u32 offset_sectio
|
||||
// actual decryption stuff
|
||||
u8 ctr[16];
|
||||
GetNcchCtr(ctr, ncch, snum);
|
||||
if (SetNcchKey(ncch, crypto, keyid) != 0) return 1;
|
||||
ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr);
|
||||
if (!(crypt_from & NCCH_NOCRYPTO)) {
|
||||
if (SetNcchKey(ncch, crypt_from, keyid) != 0) return 1;
|
||||
ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr);
|
||||
}
|
||||
if (!(crypt_to & NCCH_NOCRYPTO)) {
|
||||
if (SetNcchKey(ncch, crypt_to, keyid) != 0) return 1;
|
||||
ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// on the fly de-/encryptor for NCCH
|
||||
u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto) {
|
||||
u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypt_to) {
|
||||
const u32 offset_flag3 = 0x188 + 3;
|
||||
const u32 offset_flag7 = 0x188 + 7;
|
||||
u16 crypt_from = NCCH_GET_CRYPTO(ncch);
|
||||
|
||||
// check for encryption
|
||||
if (((!NCCH_ENCRYPTED(ncch)) && (crypto & 0x04)) ||
|
||||
(crypto == NCCH_GET_CRYPTO(ncch)))
|
||||
if ((crypt_to & crypt_from & NCCH_NOCRYPTO) || (crypt_to == crypt_from))
|
||||
return 0; // desired end result already met
|
||||
else if ((NCCH_ENCRYPTED(ncch)) && !(crypto & 0x04))
|
||||
return 1; // encrypted differently
|
||||
|
||||
// ncch flags handling
|
||||
if ((offset <= offset_flag3) && (offset + size > offset_flag3))
|
||||
data[offset_flag3 - offset] = (crypto >> 8);
|
||||
data[offset_flag3 - offset] = (crypt_to >> 8);
|
||||
if ((offset <= offset_flag7) && (offset + size > offset_flag7)) {
|
||||
data[offset_flag7 - offset] &= ~(0x01|0x20|0x04);
|
||||
data[offset_flag7 - offset] |= (crypto & (0x01|0x20|0x04));
|
||||
data[offset_flag7 - offset] |= (crypt_to & (0x01|0x20|0x04));
|
||||
}
|
||||
|
||||
// exthdr handling
|
||||
@ -256,7 +267,7 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe
|
||||
if (CryptNcchSection(data, offset, size,
|
||||
NCCH_EXTHDR_OFFSET,
|
||||
NCCH_EXTHDR_SIZE,
|
||||
0, ncch, 1, crypto, 0) != 0) return 1;
|
||||
0, ncch, 1, crypt_to, 0) != 0) return 1;
|
||||
}
|
||||
|
||||
// exefs handling
|
||||
@ -264,7 +275,7 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe
|
||||
// exefs header handling
|
||||
if (CryptNcchSection(data, offset, size,
|
||||
ncch->offset_exefs * NCCH_MEDIA_UNIT,
|
||||
0x200, 0, ncch, 2, crypto, 0) != 0) return 1;
|
||||
0x200, 0, ncch, 2, crypt_to, 0) != 0) return 1;
|
||||
|
||||
// exefs file handling
|
||||
if (exefs) for (u32 i = 0; i < 10; i++) {
|
||||
@ -272,7 +283,7 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe
|
||||
if (CryptNcchSection(data, offset, size,
|
||||
(ncch->offset_exefs * NCCH_MEDIA_UNIT) + 0x200 + file->offset,
|
||||
file->size, 0x200 + file->offset,
|
||||
ncch, 2, crypto, EXEFS_KEYID(file->name)) != 0) return 1;
|
||||
ncch, 2, crypt_to, EXEFS_KEYID(file->name)) != 0) return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,14 +292,14 @@ u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exe
|
||||
if (CryptNcchSection(data, offset, size,
|
||||
ncch->offset_romfs * NCCH_MEDIA_UNIT,
|
||||
ncch->size_romfs * NCCH_MEDIA_UNIT,
|
||||
0, ncch, 3, crypto, 1) != 0) return 1;
|
||||
0, ncch, 3, crypt_to, 1) != 0) return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// on the fly de- / encryptor for NCCH - sequential
|
||||
u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto) {
|
||||
u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypt_to) {
|
||||
// warning: this will only work for sequential processing
|
||||
// unexpected results otherwise
|
||||
static NcchHeader ncch = { 0 };
|
||||
@ -320,7 +331,7 @@ u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto) {
|
||||
}
|
||||
}
|
||||
|
||||
return CryptNcch(data, offset, size, ncchptr, exefsptr, crypto);
|
||||
return CryptNcch(data, offset, size, ncchptr, exefsptr, crypt_to);
|
||||
}
|
||||
|
||||
u32 SetNcchSdFlag(u8* data) { // data must be at least 0x600 byte and start with NCCH header
|
||||
|
@ -11,8 +11,8 @@
|
||||
#define NCCH_ENCRYPTED(ncch) (!((ncch)->flags[7] & 0x04))
|
||||
#define NCCH_IS_CXI(ncch) ((ncch)->flags[5] & 0x02)
|
||||
|
||||
#define NCCH_GET_CRYPTO(ncch) (((ncch)->flags[3] << 8) | (ncch)->flags[7])
|
||||
#define NCCH_NOCRYPTO 0x0004
|
||||
#define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20))))
|
||||
|
||||
// wrapper defines
|
||||
#define DecryptNcch(data, offset, size, ncch, exefs) CryptNcch(data, offset, size, ncch, exefs, NCCH_NOCRYPTO)
|
||||
@ -70,8 +70,8 @@ typedef struct {
|
||||
} __attribute__((packed, aligned(16))) NcchHeader;
|
||||
|
||||
u32 ValidateNcchHeader(NcchHeader* header);
|
||||
u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid);
|
||||
u32 SetupNcchCrypto(NcchHeader* ncch);
|
||||
u32 SetNcchKey(NcchHeader* ncch, u16 crypto, u32 keyid);
|
||||
u32 SetupNcchCrypto(NcchHeader* ncch, u16 crypt_to);
|
||||
u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto);
|
||||
u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto);
|
||||
u32 SetNcchSdFlag(u8* data);
|
||||
|
@ -48,7 +48,7 @@ u32 BuildNcchInfoXorpad(u8* buffer, NcchInfoEntry* entry, u32 size, u32 offset)
|
||||
ncch.flags[3] = (u8) entry->ncchFlag3;
|
||||
ncch.flags[7] = (u8) (entry->ncchFlag7 & ~0x04);
|
||||
ncch.programId = ncch.partitionId = entry->titleId;
|
||||
if (SetNcchKey(&ncch, NCCH_NOCRYPTO, 1) != 0)
|
||||
if (SetNcchKey(&ncch, NCCH_GET_CRYPTO(&ncch), 1) != 0)
|
||||
return 1;
|
||||
|
||||
// write xorpad
|
||||
|
@ -121,7 +121,7 @@ bool BuildVGameNcchDir(void) {
|
||||
u32 n = 0;
|
||||
|
||||
// NCCH crypto
|
||||
bool ncch_crypto = (NCCH_ENCRYPTED(ncch)) && (SetupNcchCrypto(ncch) == 0);
|
||||
bool ncch_crypto = (NCCH_ENCRYPTED(ncch)) && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) == 0);
|
||||
|
||||
// header
|
||||
strncpy(templates[n].name, NAME_NCCH_HEADER, 32);
|
||||
|
Loading…
x
Reference in New Issue
Block a user