Added functionality for NCCH encryption

This commit is contained in:
d0k3 2017-01-28 13:47:37 +01:00
parent cab496b2df
commit 7fb0f2df0f
3 changed files with 53 additions and 39 deletions

View File

@ -148,14 +148,16 @@ u32 GetNcchSeed(u8* seed, NcchHeader* ncch) {
return 1; return 1;
} }
u32 SetNcchKey(NcchHeader* ncch, u32 keyid) { u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid) {
u32 keyslot = (!keyid || !ncch->flags[3]) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto u8 flags3 = (crypt_to & 0x04) ? ncch->flags[3] : (crypt_to >> 8) & 0xFF;
(ncch->flags[3] == 0x0A) ? 0x18 : (ncch->flags[3] == 0x0B) ? 0x1B : 0x25; u8 flags7 = (crypt_to & 0x04) ? ncch->flags[7] : crypt_to & 0xFF;
u32 keyslot = (!keyid || !flags3) ? 0x2C : // standard / secure3 / secure4 / 7.x crypto
(flags3 == 0x0A) ? 0x18 : (flags3 == 0x0B) ? 0x1B : 0x25;
if (!NCCH_ENCRYPTED(ncch)) if (flags7 & 0x04)
return 1; return 1;
if (ncch->flags[7] & 0x01) { // fixed key crypto if (flags7 & 0x01) { // fixed key crypto
// from https://github.com/profi200/Project_CTR/blob/master/makerom/pki/dev.h // from https://github.com/profi200/Project_CTR/blob/master/makerom/pki/dev.h
u8 zeroKey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, u8 zeroKey[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // zero key 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // zero key
@ -171,7 +173,7 @@ u32 SetNcchKey(NcchHeader* ncch, u32 keyid) {
return 1; return 1;
// key Y for seed and non seed // key Y for seed and non seed
if (keyid && (ncch->flags[7] & 0x20)) { // seed crypto if (keyid && (flags7 & 0x20)) { // seed crypto
static u8 seedkeyY[16+16] __attribute__((aligned(32))) = { 0 }; static u8 seedkeyY[16+16] __attribute__((aligned(32))) = { 0 };
static u8 lsignature[16] = { 0 }; static u8 lsignature[16] = { 0 };
static u64 ltitleId = 0; static u64 ltitleId = 0;
@ -195,11 +197,11 @@ u32 SetNcchKey(NcchHeader* ncch, u32 keyid) {
u32 SetupNcchCrypto(NcchHeader* ncch) { u32 SetupNcchCrypto(NcchHeader* ncch) {
return (!NCCH_ENCRYPTED(ncch) || return (!NCCH_ENCRYPTED(ncch) ||
((SetNcchKey(ncch, 0) == 0) && (SetNcchKey(ncch, 1) == 0))) ? 0 : 1; ((SetNcchKey(ncch, NCCH_NOCRYPTO, 0) == 0) && (SetNcchKey(ncch, NCCH_NOCRYPTO, 1) == 0))) ? 0 : 1;
} }
u32 DecryptNcchSection(u8* data, u32 offset_data, u32 size_data, u32 CryptNcchSection(u8* data, u32 offset_data, u32 size_data, u32 offset_section, u32 size_section,
u32 offset_section, u32 size_section, u32 offset_ctr, NcchHeader* ncch, u32 snum, u32 keyid) { u32 offset_ctr, NcchHeader* ncch, u32 snum, u16 crypto, u32 keyid) {
const u32 mode = AES_CNT_CTRNAND_MODE; const u32 mode = AES_CNT_CTRNAND_MODE;
// check if section in data // check if section in data
@ -223,67 +225,70 @@ u32 DecryptNcchSection(u8* data, u32 offset_data, u32 size_data,
// actual decryption stuff // actual decryption stuff
u8 ctr[16]; u8 ctr[16];
GetNcchCtr(ctr, ncch, snum); GetNcchCtr(ctr, ncch, snum);
if (SetNcchKey(ncch, keyid) != 0) return 1; if (SetNcchKey(ncch, crypto, keyid) != 0) return 1;
ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr); ctr_decrypt_byte(data_i, data_i, size_i, offset_i + offset_ctr, mode, ctr);
return 0; return 0;
} }
// on the fly decryptor for NCCH // on the fly de-/encryptor for NCCH
u32 DecryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs) { u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto) {
const u32 offset_flag3 = 0x188 + 3; const u32 offset_flag3 = 0x188 + 3;
const u32 offset_flag7 = 0x188 + 7; const u32 offset_flag7 = 0x188 + 7;
// check for encryption // check for encryption
if (!NCCH_ENCRYPTED(ncch)) if (((!NCCH_ENCRYPTED(ncch)) && (crypto & 0x04)) ||
return 0; (crypto == NCCH_GET_CRYPTO(ncch)))
return 0; // desired end result already met
else if ((NCCH_ENCRYPTED(ncch)) && !(crypto & 0x04))
return 1; // encrypted differently
// ncch flags handling // ncch flags handling
if ((offset <= offset_flag3) && (offset + size > offset_flag3)) if ((offset <= offset_flag3) && (offset + size > offset_flag3))
data[offset_flag3 - offset] = 0; data[offset_flag3 - offset] = (crypto >> 8);
if ((offset <= offset_flag7) && (offset + size > offset_flag7)) { if ((offset <= offset_flag7) && (offset + size > offset_flag7)) {
data[offset_flag7 - offset] &= ~(0x01|0x20); data[offset_flag7 - offset] &= ~(0x01|0x20|0x04);
data[offset_flag7 - offset] |= 0x04; data[offset_flag7 - offset] |= (crypto & (0x01|0x20|0x04));
} }
// exthdr handling // exthdr handling
if (ncch->size_exthdr) { if (ncch->size_exthdr) {
if (DecryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
NCCH_EXTHDR_OFFSET, NCCH_EXTHDR_OFFSET,
NCCH_EXTHDR_SIZE, NCCH_EXTHDR_SIZE,
0, ncch, 1, 0) != 0) return 1; 0, ncch, 1, crypto, 0) != 0) return 1;
} }
// exefs handling // exefs handling
if (ncch->size_exefs) { if (ncch->size_exefs) {
// exefs header handling // exefs header handling
if (DecryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
ncch->offset_exefs * NCCH_MEDIA_UNIT, ncch->offset_exefs * NCCH_MEDIA_UNIT,
0x200, 0, ncch, 2, 0) != 0) return 1; 0x200, 0, ncch, 2, crypto, 0) != 0) return 1;
// exefs file handling // exefs file handling
if (exefs) for (u32 i = 0; i < 10; i++) { if (exefs) for (u32 i = 0; i < 10; i++) {
ExeFsFileHeader* file = exefs->files + i; ExeFsFileHeader* file = exefs->files + i;
if (DecryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
(ncch->offset_exefs * NCCH_MEDIA_UNIT) + 0x200 + file->offset, (ncch->offset_exefs * NCCH_MEDIA_UNIT) + 0x200 + file->offset,
file->size, 0x200 + file->offset, file->size, 0x200 + file->offset,
ncch, 2, EXEFS_KEYID(file->name)) != 0) return 1; ncch, 2, crypto, EXEFS_KEYID(file->name)) != 0) return 1;
} }
} }
// romfs handling // romfs handling
if (ncch->size_romfs) { if (ncch->size_romfs) {
if (DecryptNcchSection(data, offset, size, if (CryptNcchSection(data, offset, size,
ncch->offset_romfs * NCCH_MEDIA_UNIT, ncch->offset_romfs * NCCH_MEDIA_UNIT,
ncch->size_romfs * NCCH_MEDIA_UNIT, ncch->size_romfs * NCCH_MEDIA_UNIT,
0, ncch, 3, 1) != 0) return 1; 0, ncch, 3, crypto, 1) != 0) return 1;
} }
return 0; return 0;
} }
// on the fly decryptor for NCCH - sequential // on the fly de- / encryptor for NCCH - sequential
u32 DecryptNcchSequential(u8* data, u32 offset, u32 size) { u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto) {
// warning: this will only work for sequential processing // warning: this will only work for sequential processing
// unexpected results otherwise // unexpected results otherwise
static NcchHeader ncch = { 0 }; static NcchHeader ncch = { 0 };
@ -302,21 +307,20 @@ u32 DecryptNcchSequential(u8* data, u32 offset, u32 size) {
if (!ncchptr) return 1; if (!ncchptr) return 1;
// fetch exefs header from data // fetch exefs header from data
if (!exefsptr) { if (ncchptr->offset_exefs && !exefsptr) {
u32 offset_exefs = ncchptr->offset_exefs * NCCH_MEDIA_UNIT; u32 offset_exefs = ncchptr->offset_exefs * NCCH_MEDIA_UNIT;
if ((offset <= offset_exefs) && if ((offset <= offset_exefs) &&
((offset + size) >= offset_exefs + sizeof(ExeFsHeader))) { ((offset + size) >= offset_exefs + sizeof(ExeFsHeader))) {
if (DecryptNcch(data, offset, offset_exefs + sizeof(ExeFsHeader) - offset, ncchptr, NULL) != 0)
return 1;
memcpy(&exefs, data + offset_exefs - offset, sizeof(ExeFsHeader)); memcpy(&exefs, data + offset_exefs - offset, sizeof(ExeFsHeader));
size -= offset_exefs + sizeof(ExeFsHeader) - offset; if ((NCCH_ENCRYPTED(ncchptr)) &&
data += offset_exefs + sizeof(ExeFsHeader) - offset; (DecryptNcch((u8*) &exefs, offset_exefs, sizeof(ExeFsHeader), ncchptr, NULL) != 0))
offset = offset_exefs + sizeof(ExeFsHeader); return 1;
if (ValidateExeFsHeader(&exefs, 0) != 0) return 1;
exefsptr = &exefs; exefsptr = &exefs;
} }
} }
return DecryptNcch(data, offset, size, ncchptr, exefsptr); return CryptNcch(data, offset, size, ncchptr, exefsptr, crypto);
} }
u32 SetNcchSdFlag(u8* data) { // data must be at least 0x600 byte and start with NCCH header u32 SetNcchSdFlag(u8* data) { // data must be at least 0x600 byte and start with NCCH header

View File

@ -9,6 +9,16 @@
#define NCCH_EXTHDR_OFFSET 0x200 #define NCCH_EXTHDR_OFFSET 0x200
#define NCCH_ENCRYPTED(ncch) (!((ncch)->flags[7] & 0x04)) #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
// wrapper defines
#define DecryptNcch(data, offset, size, ncch, exefs) CryptNcch(data, offset, size, ncch, exefs, NCCH_NOCRYPTO)
#define EncryptNcch(data, offset, size, ncch, exefs, crypto) CryptNcch(data, offset, size, ncch, exefs, crypto)
#define DecryptNcchSequential(data, offset, size) CryptNcchSequential(data, offset, size, NCCH_NOCRYPTO)
#define EncryptNcchSequential(data, offset, size, crypto) CryptNcchSequential(data, offset, size, crypto)
// see: https://www.3dbrew.org/wiki/NCCH/Extended_Header // see: https://www.3dbrew.org/wiki/NCCH/Extended_Header
// very limited, contains only required stuff // very limited, contains only required stuff
@ -60,8 +70,8 @@ typedef struct {
} __attribute__((packed, aligned(16))) NcchHeader; } __attribute__((packed, aligned(16))) NcchHeader;
u32 ValidateNcchHeader(NcchHeader* header); u32 ValidateNcchHeader(NcchHeader* header);
u32 SetNcchKey(NcchHeader* ncch, u32 keyid); u32 SetNcchKey(NcchHeader* ncch, u16 crypt_to, u32 keyid);
u32 SetupNcchCrypto(NcchHeader* ncch); u32 SetupNcchCrypto(NcchHeader* ncch);
u32 DecryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs); u32 CryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs, u16 crypto);
u32 DecryptNcchSequential(u8* data, u32 offset, u32 size); u32 CryptNcchSequential(u8* data, u32 offset, u32 size, u16 crypto);
u32 SetNcchSdFlag(u8* data); u32 SetNcchSdFlag(u8* data);

View File

@ -48,7 +48,7 @@ u32 BuildNcchInfoXorpad(u8* buffer, NcchInfoEntry* entry, u32 size, u32 offset)
ncch.flags[3] = (u8) entry->ncchFlag3; ncch.flags[3] = (u8) entry->ncchFlag3;
ncch.flags[7] = (u8) (entry->ncchFlag7 & ~0x04); ncch.flags[7] = (u8) (entry->ncchFlag7 & ~0x04);
ncch.programId = ncch.partitionId = entry->titleId; ncch.programId = ncch.partitionId = entry->titleId;
if (SetNcchKey(&ncch, 1) != 0) if (SetNcchKey(&ncch, NCCH_NOCRYPTO, 1) != 0)
return 1; return 1;
// write xorpad // write xorpad